Safeparcelables in AppSearch - VisibilityDocument

Make VisibilityDocument and VisibilityPermissionDocument SafeParcelable,
and remove their inheritance to GenericDocument. GenericDocument will
contain a GenericDocumentParcel in the future, and it is unnecessary to
have this internal SafeParcel for VisibilityDocument. Plus, we have plan
NOT to store visibility documents in IcingLib in the future:
b/298118943.

For now, we still need to convert those two to GenericDocuments until
we completely treat them differently for b/298118943.

Bug: 275629842
Test: appsearch:appsearch:connectedCheck appsearch:appsearch-local-storage:connectedCheck
Change-Id: Ic237f2460f79d6997e8639e7a0e320cccbe30190
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
index c2eac42..d20b0f7 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
@@ -2269,7 +2269,6 @@
         VisibilityDocument visibilityDocument = new VisibilityDocument.Builder("schema")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .setCreationTimestampMillis(12345L)
                 .build();
 
         // Insert schema for package A and B.
@@ -2314,13 +2313,11 @@
                 new VisibilityDocument.Builder("packageA$database/schema")
                         .setNotDisplayedBySystem(true)
                         .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                        .setCreationTimestampMillis(12345L)
                         .build();
         VisibilityDocument expectedVisibilityDocumentB =
                 new VisibilityDocument.Builder("packageB$database/schema")
                         .setNotDisplayedBySystem(true)
                         .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                        .setCreationTimestampMillis(12345L)
                         .build();
         assertThat(mAppSearchImpl.mVisibilityStoreLocked
                 .getVisibility("packageA$database/schema"))
@@ -4243,7 +4240,6 @@
         VisibilityDocument visibilityDocument = new VisibilityDocument.Builder("Email")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .setCreationTimestampMillis(12345L)
                 .build();
         List<AppSearchSchema> schemas =
                 Collections.singletonList(new AppSearchSchema.Builder("Email").build());
@@ -4264,17 +4260,17 @@
         VisibilityDocument expectedDocument = new VisibilityDocument.Builder(prefix + "Email")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .setCreationTimestampMillis(12345L)
                 .build();
         assertThat(mAppSearchImpl.mVisibilityStoreLocked.getVisibility(prefix + "Email"))
                 .isEqualTo(expectedDocument);
         // Verify the VisibilityDocument is saved to AppSearchImpl.
-        VisibilityDocument actualDocument =  new VisibilityDocument(mAppSearchImpl.getDocument(
+        VisibilityDocument actualDocument =
+                new VisibilityDocument.Builder(mAppSearchImpl.getDocument(
                 VisibilityStore.VISIBILITY_PACKAGE_NAME,
                 VisibilityStore.VISIBILITY_DATABASE_NAME,
                 VisibilityDocument.NAMESPACE,
                 /*id=*/ prefix + "Email",
-                /*typePropertyPaths=*/ Collections.emptyMap()));
+                /*typePropertyPaths=*/ Collections.emptyMap())).build();
         assertThat(actualDocument).isEqualTo(expectedDocument);
     }
 
@@ -4284,7 +4280,6 @@
         VisibilityDocument visibilityDocument1 = new VisibilityDocument.Builder("Email1")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .setCreationTimestampMillis(12345L)
                 .build();
         List<AppSearchSchema> schemas1 =
                 Collections.singletonList(new AppSearchSchema.Builder("Email1").build());
@@ -4305,24 +4300,23 @@
         VisibilityDocument expectedDocument1 = new VisibilityDocument.Builder(prefix1 + "Email1")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .setCreationTimestampMillis(12345L)
                 .build();
         assertThat(mAppSearchImpl.mVisibilityStoreLocked.getVisibility(prefix1 + "Email1"))
                 .isEqualTo(expectedDocument1);
         // Verify the VisibilityDocument is saved to AppSearchImpl.
-        VisibilityDocument actualDocument1 =  new VisibilityDocument(mAppSearchImpl.getDocument(
+        VisibilityDocument actualDocument1 =
+                new VisibilityDocument.Builder(mAppSearchImpl.getDocument(
                 VisibilityStore.VISIBILITY_PACKAGE_NAME,
                 VisibilityStore.VISIBILITY_DATABASE_NAME,
                 VisibilityDocument.NAMESPACE,
                 /*id=*/ prefix1 + "Email1",
-                /*typePropertyPaths=*/ Collections.emptyMap()));
+                /*typePropertyPaths=*/ Collections.emptyMap())).build();
         assertThat(actualDocument1).isEqualTo(expectedDocument1);
 
         // Create Visibility Document for Email2
         VisibilityDocument visibilityDocument2 = new VisibilityDocument.Builder("Email2")
                 .setNotDisplayedBySystem(false)
                 .addVisibleToPackage(new PackageIdentifier("pkgFoo", new byte[32]))
-                .setCreationTimestampMillis(54321L)
                 .build();
         List<AppSearchSchema> schemas2 =
                 Collections.singletonList(new AppSearchSchema.Builder("Email2").build());
@@ -4343,29 +4337,29 @@
         VisibilityDocument expectedDocument2 = new VisibilityDocument.Builder(prefix2 + "Email2")
                 .setNotDisplayedBySystem(false)
                 .addVisibleToPackage(new PackageIdentifier("pkgFoo", new byte[32]))
-                .setCreationTimestampMillis(54321)
                 .build();
         assertThat(mAppSearchImpl.mVisibilityStoreLocked.getVisibility(prefix2 + "Email2"))
                 .isEqualTo(expectedDocument2);
         // Verify the VisibilityDocument is saved to AppSearchImpl.
-        VisibilityDocument actualDocument2 =  new VisibilityDocument(mAppSearchImpl.getDocument(
+        VisibilityDocument actualDocument2 =  new VisibilityDocument.Builder(
+                mAppSearchImpl.getDocument(
                 VisibilityStore.VISIBILITY_PACKAGE_NAME,
                 VisibilityStore.VISIBILITY_DATABASE_NAME,
                 VisibilityDocument.NAMESPACE,
                 /*id=*/ prefix2 + "Email2",
-                /*typePropertyPaths=*/ Collections.emptyMap()));
+                /*typePropertyPaths=*/ Collections.emptyMap())).build();
         assertThat(actualDocument2).isEqualTo(expectedDocument2);
 
         // Check the existing visibility document retains.
         assertThat(mAppSearchImpl.mVisibilityStoreLocked.getVisibility(prefix1 + "Email1"))
                 .isEqualTo(expectedDocument1);
         // Verify the VisibilityDocument is saved to AppSearchImpl.
-        actualDocument1 =  new VisibilityDocument(mAppSearchImpl.getDocument(
+        actualDocument1 =  new VisibilityDocument.Builder(mAppSearchImpl.getDocument(
                 VisibilityStore.VISIBILITY_PACKAGE_NAME,
                 VisibilityStore.VISIBILITY_DATABASE_NAME,
                 VisibilityDocument.NAMESPACE,
                 /*id=*/ prefix1 + "Email1",
-                /*typePropertyPaths=*/ Collections.emptyMap()));
+                /*typePropertyPaths=*/ Collections.emptyMap())).build();
         assertThat(actualDocument1).isEqualTo(expectedDocument1);
     }
 
@@ -4375,7 +4369,6 @@
         VisibilityDocument visibilityDocument = new VisibilityDocument.Builder("Email")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .setCreationTimestampMillis(12345L)
                 .build();
 
         List<AppSearchSchema> schemas =
@@ -4395,17 +4388,17 @@
         VisibilityDocument expectedDocument = new VisibilityDocument.Builder(prefix + "Email")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .setCreationTimestampMillis(12345L)
                 .build();
         assertThat(mAppSearchImpl.mVisibilityStoreLocked.getVisibility(prefix + "Email"))
                 .isEqualTo(expectedDocument);
         // Verify the VisibilityDocument is saved to AppSearchImpl.
-        VisibilityDocument actualDocument =  new VisibilityDocument(mAppSearchImpl.getDocument(
+        VisibilityDocument actualDocument =
+                new VisibilityDocument.Builder(mAppSearchImpl.getDocument(
                 VisibilityStore.VISIBILITY_PACKAGE_NAME,
                 VisibilityStore.VISIBILITY_DATABASE_NAME,
                 VisibilityDocument.NAMESPACE,
                 /*id=*/ prefix + "Email",
-                /*typePropertyPaths=*/ Collections.emptyMap()));
+                /*typePropertyPaths=*/ Collections.emptyMap())).build();
         assertThat(actualDocument).isEqualTo(expectedDocument);
 
         // Set schema Email and its all-default visibility document to AppSearch database1
@@ -4439,7 +4432,6 @@
         VisibilityDocument visibilityDocument = new VisibilityDocument.Builder("Email")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .setCreationTimestampMillis(12345L)
                 .build();
 
         List<AppSearchSchema> schemas =
@@ -4458,17 +4450,17 @@
         VisibilityDocument expectedDocument = new VisibilityDocument.Builder(prefix + "Email")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .setCreationTimestampMillis(12345L)
                 .build();
         assertThat(mAppSearchImpl.mVisibilityStoreLocked.getVisibility(prefix + "Email"))
                 .isEqualTo(expectedDocument);
         // Verify the VisibilityDocument is saved to AppSearchImpl.
-        VisibilityDocument actualDocument =  new VisibilityDocument(mAppSearchImpl.getDocument(
+        VisibilityDocument actualDocument =
+                new VisibilityDocument.Builder(mAppSearchImpl.getDocument(
                 VisibilityStore.VISIBILITY_PACKAGE_NAME,
                 VisibilityStore.VISIBILITY_DATABASE_NAME,
                 VisibilityDocument.NAMESPACE,
                 /*id=*/ prefix + "Email",
-                /*typePropertyPaths=*/ Collections.emptyMap()));
+                /*typePropertyPaths=*/ Collections.emptyMap())).build();
         assertThat(actualDocument).isEqualTo(expectedDocument);
 
         // remove the schema and visibility setting from AppSearch
@@ -4511,7 +4503,6 @@
         VisibilityDocument visibilityDocument = new VisibilityDocument.Builder("Email")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .setCreationTimestampMillis(12345L)
                 .build();
         List<AppSearchSchema> schemas =
                 Collections.singletonList(new AppSearchSchema.Builder("Email").build());
@@ -4541,18 +4532,18 @@
         VisibilityDocument expectedDocument = new VisibilityDocument.Builder(prefix + "Email")
                 .setNotDisplayedBySystem(true)
                 .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                .setCreationTimestampMillis(12345L)
                 .build();
 
         assertThat(mAppSearchImpl.mVisibilityStoreLocked.getVisibility(prefix + "Email"))
                 .isEqualTo(expectedDocument);
         // Verify the VisibilityDocument is saved to AppSearchImpl.
-        VisibilityDocument actualDocument =  new VisibilityDocument(mAppSearchImpl.getDocument(
+        VisibilityDocument actualDocument =
+                new VisibilityDocument.Builder(mAppSearchImpl.getDocument(
                 VisibilityStore.VISIBILITY_PACKAGE_NAME,
                 VisibilityStore.VISIBILITY_DATABASE_NAME,
                 VisibilityDocument.NAMESPACE,
                 /*id=*/ prefix + "Email",
-                /*typePropertyPaths=*/ Collections.emptyMap()));
+                /*typePropertyPaths=*/ Collections.emptyMap())).build();
         assertThat(actualDocument).isEqualTo(expectedDocument);
 
         // remove schema and visibility document
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java
index d516e81..77772c4 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java
@@ -134,31 +134,29 @@
                 ALWAYS_OPTIMIZE,
                 /*visibilityChecker=*/null);
 
-        VisibilityDocument actualDocument1 = new VisibilityDocument(
+        VisibilityDocument actualDocument1 = new VisibilityDocument.Builder(
                 appSearchImpl.getDocument(
                         VisibilityStore.VISIBILITY_PACKAGE_NAME,
                         VisibilityStore.VISIBILITY_DATABASE_NAME,
                         VisibilityDocument.NAMESPACE,
                         /*id=*/ prefix + "Schema1",
-                        /*typePropertyPaths=*/ Collections.emptyMap()));
-        VisibilityDocument actualDocument2 = new VisibilityDocument(
+                        /*typePropertyPaths=*/ Collections.emptyMap())).build();
+        VisibilityDocument actualDocument2 = new VisibilityDocument.Builder(
                 appSearchImpl.getDocument(
                         VisibilityStore.VISIBILITY_PACKAGE_NAME,
                         VisibilityStore.VISIBILITY_DATABASE_NAME,
                         VisibilityDocument.NAMESPACE,
                         /*id=*/ prefix + "Schema2",
-                        /*typePropertyPaths=*/ Collections.emptyMap()));
+                        /*typePropertyPaths=*/ Collections.emptyMap())).build();
 
         VisibilityDocument expectedDocument1 =
                 new VisibilityDocument.Builder(/*id=*/ prefix + "Schema1")
                         .setNotDisplayedBySystem(true)
-                        .setCreationTimestampMillis(actualDocument1.getCreationTimestampMillis())
                         .addVisibleToPackage(new PackageIdentifier(packageNameFoo, sha256CertFoo))
                         .build();
         VisibilityDocument expectedDocument2 =
                 new VisibilityDocument.Builder(/*id=*/ prefix + "Schema2")
                         .setNotDisplayedBySystem(true)
-                        .setCreationTimestampMillis(actualDocument2.getCreationTimestampMillis())
                         .addVisibleToPackage(new PackageIdentifier(packageNameBar, sha256CertBar))
                         .build();
         assertThat(actualDocument1).isEqualTo(expectedDocument1);
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java
index 56086d4..c5bd8ac 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java
@@ -128,13 +128,13 @@
                 ALWAYS_OPTIMIZE,
                 /*visibilityChecker=*/null);
 
-        VisibilityDocument actualDocument = new VisibilityDocument(
+        VisibilityDocument actualDocument = new VisibilityDocument.Builder(
                 appSearchImpl.getDocument(
                         VisibilityStore.VISIBILITY_PACKAGE_NAME,
                         VisibilityStore.VISIBILITY_DATABASE_NAME,
                         VisibilityDocument.NAMESPACE,
                         /*id=*/ prefix + "Schema",
-                        /*typePropertyPaths=*/ Collections.emptyMap()));
+                        /*typePropertyPaths=*/ Collections.emptyMap())).build();
 
         assertThat(actualDocument.isNotDisplayedBySystem()).isTrue();
         assertThat(actualDocument.getPackageNames()).asList().containsExactly(packageNameFoo,
diff --git a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreTest.java b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreTest.java
index 721b3ad..17e8a15 100644
--- a/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreTest.java
+++ b/appsearch/appsearch-local-storage/src/androidTest/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStoreTest.java
@@ -110,12 +110,13 @@
         assertThat(mVisibilityStore.getVisibility(prefix + "Email"))
                 .isEqualTo(visibilityDocument);
         // Verify the VisibilityDocument is saved to AppSearchImpl.
-        VisibilityDocument actualDocument =  new VisibilityDocument(mAppSearchImpl.getDocument(
+        VisibilityDocument actualDocument =
+                new VisibilityDocument.Builder(mAppSearchImpl.getDocument(
                 VisibilityStore.VISIBILITY_PACKAGE_NAME,
                 VisibilityStore.VISIBILITY_DATABASE_NAME,
                 VisibilityDocument.NAMESPACE,
                 /*id=*/ prefix + "Email",
-                /*typePropertyPaths=*/ Collections.emptyMap()));
+                /*typePropertyPaths=*/ Collections.emptyMap())).build();
         assertThat(actualDocument).isEqualTo(visibilityDocument);
     }
 
@@ -130,12 +131,13 @@
         assertThat(mVisibilityStore.getVisibility("Email"))
                 .isEqualTo(visibilityDocument);
         // Verify the VisibilityDocument is saved to AppSearchImpl.
-        VisibilityDocument actualDocument =  new VisibilityDocument(mAppSearchImpl.getDocument(
+        VisibilityDocument actualDocument = new VisibilityDocument.Builder(
+                mAppSearchImpl.getDocument(
                 VisibilityStore.VISIBILITY_PACKAGE_NAME,
                 VisibilityStore.VISIBILITY_DATABASE_NAME,
                 VisibilityDocument.NAMESPACE,
                 /*id=*/ "Email",
-                /*typePropertyPaths=*/ Collections.emptyMap()));
+                /*typePropertyPaths=*/ Collections.emptyMap())).build();
         assertThat(actualDocument).isEqualTo(visibilityDocument);
 
         mVisibilityStore.removeVisibility(ImmutableSet.of(visibilityDocument.getId()));
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
index 8b136a3..b64bab8 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
@@ -813,10 +813,9 @@
                 // fake the id, they can only mess their own app. That's totally allowed and
                 // they can do this via the public API too.
                 String prefixedSchemaType = prefix + unPrefixedDocument.getId();
-                prefixedVisibilityDocuments.add(new VisibilityDocument(
-                        unPrefixedDocument.toBuilder()
-                                .setId(prefixedSchemaType)
-                                .build()));
+                prefixedVisibilityDocuments.add(
+                        new VisibilityDocument.Builder(
+                                unPrefixedDocument).setId(prefixedSchemaType).build());
                 // This schema has visibility settings. We should keep it from the removal list.
                 deprecatedVisibilityDocuments.remove(prefixedSchemaType);
             }
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStore.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStore.java
index b44cc3e..e3d5916 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStore.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/visibilitystore/VisibilityStore.java
@@ -165,7 +165,7 @@
             mAppSearchImpl.putDocument(
                     VISIBILITY_PACKAGE_NAME,
                     VISIBILITY_DATABASE_NAME,
-                    prefixedVisibilityDocument,
+                    prefixedVisibilityDocument.toGenericDocument(),
                     /*sendChangeNotifications=*/ false,
                     /*logger=*/ null);
             mVisibilityDocumentMap.put(prefixedVisibilityDocument.getId(),
@@ -226,13 +226,13 @@
             VisibilityDocument visibilityDocument;
             try {
                 // Note: We use the other clients' prefixed schema type as ids
-                visibilityDocument = new VisibilityDocument(
+                visibilityDocument = new VisibilityDocument.Builder(
                         mAppSearchImpl.getDocument(
                                 VISIBILITY_PACKAGE_NAME,
                                 VISIBILITY_DATABASE_NAME,
                                 VisibilityDocument.NAMESPACE,
                                 /*id=*/ prefixedSchemaType,
-                                /*typePropertyPaths=*/ Collections.emptyMap()));
+                                /*typePropertyPaths=*/ Collections.emptyMap())).build();
             } catch (AppSearchException e) {
                 if (e.getResultCode() == RESULT_NOT_FOUND) {
                     // The schema has all default setting and we won't have a VisibilityDocument for
@@ -274,7 +274,7 @@
             mAppSearchImpl.putDocument(
                     VISIBILITY_PACKAGE_NAME,
                     VISIBILITY_DATABASE_NAME,
-                    migratedDocument,
+                    migratedDocument.toGenericDocument(),
                     /*sendChangeNotifications=*/ false,
                     /*logger=*/ null);
         }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityDocument.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityDocument.java
index f794752..2bea91f 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityDocument.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityDocument.java
@@ -15,19 +15,23 @@
  */
 package androidx.appsearch.app;
 
-import android.os.Bundle;
+import android.os.Parcel;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.VisibilityDocumentCreator;
 import androidx.collection.ArraySet;
-import androidx.core.util.Preconditions;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -35,7 +39,11 @@
  * @exportToFramework:hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class VisibilityDocument extends GenericDocument {
[email protected](creator = "VisibilityDocumentCreator")
+public final class VisibilityDocument extends AbstractSafeParcelable {
+    @NonNull
+    public static final VisibilityDocumentCreator CREATOR = new VisibilityDocumentCreator();
+
     /**
      * The Schema type for documents that hold AppSearch's metadata, such as visibility settings.
      */
@@ -92,53 +100,107 @@
                     .build())
             .build();
 
-    public VisibilityDocument(@NonNull GenericDocument genericDocument) {
-        super(genericDocument);
+    @NonNull
+    @Field(id = 1, getter = "getId")
+    private String mId;
+
+    @Field(id = 2, getter = "isNotDisplayedBySystem")
+    private final boolean mIsNotDisplayedBySystem;
+
+    @NonNull
+    @Field(id = 3, getter = "getPackageNames")
+    private final String[] mPackageNames;
+
+    @NonNull
+    @Field(id = 4, getter = "getSha256Certs")
+    private final byte[][] mSha256Certs;
+
+    @Nullable
+    @Field(id = 5, getter = "getPermissionDocuments")
+    private final VisibilityPermissionDocument[] mPermissionDocuments;
+
+    @Nullable
+    // We still need to convert this class to a GenericDocument until we completely treat it
+    // differently in AppSearchImpl.
+    // TODO(b/298118943) Remove this once internally we don't use GenericDocument to store
+    //  visibility information.
+    private GenericDocument mGenericDocument;
+
+    @Nullable
+    private Integer mHashCode;
+
+    @Constructor
+    VisibilityDocument(
+            @Param(id = 1) @NonNull String id,
+            @Param(id = 2) boolean isNotDisplayedBySystem,
+            @Param(id = 3) @NonNull String[] packageNames,
+            @Param(id = 4) @NonNull byte[][] sha256Certs,
+            @Param(id = 5) @Nullable VisibilityPermissionDocument[] permissionDocuments) {
+        mId = Objects.requireNonNull(id);
+        mIsNotDisplayedBySystem = isNotDisplayedBySystem;
+        mPackageNames = Objects.requireNonNull(packageNames);
+        mSha256Certs = Objects.requireNonNull(sha256Certs);
+        mPermissionDocuments = permissionDocuments;
     }
 
-    public VisibilityDocument(@NonNull Bundle bundle) {
-        super(bundle);
+    /**
+     * Gets the id for this VisibilityDocument.
+     *
+     * <p>This is being used as the document id when we convert a {@link VisibilityDocument}
+     * to a {@link GenericDocument}.
+     */
+    @NonNull
+    public String getId() {
+        return mId;
     }
 
     /** Returns whether this schema is visible to the system. */
     public boolean isNotDisplayedBySystem() {
-        return getPropertyBoolean(NOT_DISPLAYED_BY_SYSTEM_PROPERTY);
+        return mIsNotDisplayedBySystem;
     }
 
     /**
-     * Returns a package name array which could access this schema. Use {@link #getSha256Certs()}
-     * to get package's sha 256 certs. The same index of package names array and sha256Certs array
+     * Returns a package name array which could access this schema. Use {@link #getSha256Certs()} to
+     * get package's sha 256 certs. The same index of package names array and sha256Certs array
      * represents same package.
      */
     @NonNull
     public String[] getPackageNames() {
-        return Preconditions.checkNotNull(getPropertyStringArray(PACKAGE_NAME_PROPERTY));
+        return mPackageNames;
     }
 
     /**
-     * Returns a package sha256Certs array which could access this schema. Use
-     * {@link #getPackageNames()} to get package's name. The same index of package names array
-     * and sha256Certs array represents same package.
+     * Returns a package sha256Certs array which could access this schema. Use {@link
+     * #getPackageNames()} to get package's name. The same index of package names array and
+     * sha256Certs array represents same package.
      */
     @NonNull
     public byte[][] getSha256Certs() {
-        return Preconditions.checkNotNull(getPropertyBytesArray(SHA_256_CERT_PROPERTY));
+        return mSha256Certs;
+    }
+
+    /** Gets a list of {@link VisibilityDocument}.
+     *
+     * <p>A {@link VisibilityDocument} holds all required permissions for the caller need to have
+     * to access the schema this {@link VisibilityDocument} presents.
+     */
+    @Nullable
+    VisibilityPermissionDocument[] getPermissionDocuments() {
+        return mPermissionDocuments;
     }
 
     /**
-     * Returns an array of Android Permissions that caller mush hold to access the schema
-     * this {@link VisibilityDocument} represents.
+     * Returns an array of Android Permissions that caller mush hold to access the schema this
+     * {@link VisibilityDocument} represents.
      */
-    @Nullable
+    @NonNull
     public Set<Set<Integer>> getVisibleToPermissions() {
-        GenericDocument[] permissionDocuments = getPropertyDocumentArray(PERMISSION_PROPERTY);
-        if (permissionDocuments == null) {
+        if (mPermissionDocuments == null) {
             return Collections.emptySet();
         }
-        Set<Set<Integer>> visibleToPermissions = new ArraySet<>(permissionDocuments.length);
-        for (GenericDocument permissionDocument : permissionDocuments) {
-            Set<Integer> requiredPermissions = new VisibilityPermissionDocument(
-                    permissionDocument).getAllRequiredPermissions();
+        Set<Set<Integer>> visibleToPermissions = new ArraySet<>(mPermissionDocuments.length);
+        for (VisibilityPermissionDocument permissionDocument : mPermissionDocuments) {
+            Set<Integer> requiredPermissions = permissionDocument.getAllRequiredPermissions();
             if (requiredPermissions != null) {
                 visibleToPermissions.add(requiredPermissions);
             }
@@ -146,92 +208,7 @@
         return visibleToPermissions;
     }
 
-    /** Builder for {@link VisibilityDocument}. */
-    public static class Builder extends GenericDocument.Builder<Builder> {
-        private final Set<PackageIdentifier> mPackageIdentifiers = new ArraySet<>();
-
-        /**
-         * Creates a {@link Builder} for a {@link VisibilityDocument}.
-         *
-         * @param id The SchemaType of the {@link AppSearchSchema} that this
-         *           {@link VisibilityDocument} represents. The package and database prefix will be
-         *           added in server side. We are using prefixed schema type to be the final id of
-         *           this {@link VisibilityDocument}.
-         */
-        public Builder(@NonNull String id) {
-            super(NAMESPACE, id, SCHEMA_TYPE);
-        }
-
-        /** Sets whether this schema has opted out of platform surfacing. */
-        @CanIgnoreReturnValue
-        @NonNull
-        public Builder setNotDisplayedBySystem(boolean notDisplayedBySystem) {
-            return setPropertyBoolean(NOT_DISPLAYED_BY_SYSTEM_PROPERTY,
-                    notDisplayedBySystem);
-        }
-
-        /** Add {@link PackageIdentifier} of packages which has access to this schema. */
-        @CanIgnoreReturnValue
-        @NonNull
-        public Builder addVisibleToPackages(@NonNull Set<PackageIdentifier> packageIdentifiers) {
-            Preconditions.checkNotNull(packageIdentifiers);
-            mPackageIdentifiers.addAll(packageIdentifiers);
-            return this;
-        }
-
-        /** Add {@link PackageIdentifier} of packages which has access to this schema. */
-        @CanIgnoreReturnValue
-        @NonNull
-        public Builder addVisibleToPackage(@NonNull PackageIdentifier packageIdentifier) {
-            Preconditions.checkNotNull(packageIdentifier);
-            mPackageIdentifiers.add(packageIdentifier);
-            return this;
-        }
-
-        /**
-         * Sets required permission sets for a package needs to hold to the schema this
-         * {@link VisibilityDocument} represents.
-         *
-         * <p> The querier could have access if they holds ALL required permissions of ANY of the
-         * individual value sets.
-         */
-        @CanIgnoreReturnValue
-        @NonNull
-        public Builder setVisibleToPermissions(@NonNull Set<Set<Integer>> visibleToPermissions) {
-            Preconditions.checkNotNull(visibleToPermissions);
-            VisibilityPermissionDocument[] permissionDocuments =
-                    new VisibilityPermissionDocument[visibleToPermissions.size()];
-            int i = 0;
-            for (Set<Integer> allRequiredPermissions : visibleToPermissions) {
-                permissionDocuments[i++] = new VisibilityPermissionDocument
-                        .Builder(NAMESPACE, /*id=*/String.valueOf(i))
-                        .setVisibleToAllRequiredPermissions(allRequiredPermissions)
-                        .build();
-            }
-            setPropertyDocument(PERMISSION_PROPERTY, permissionDocuments);
-            return this;
-        }
-
-        /** Build a {@link VisibilityDocument} */
-        @Override
-        @NonNull
-        public VisibilityDocument build() {
-            String[] packageNames = new String[mPackageIdentifiers.size()];
-            byte[][] sha256Certs = new byte[mPackageIdentifiers.size()][32];
-            int i = 0;
-            for (PackageIdentifier packageIdentifier : mPackageIdentifiers) {
-                packageNames[i] = packageIdentifier.getPackageName();
-                sha256Certs[i] = packageIdentifier.getSha256Certificate();
-                ++i;
-            }
-            setPropertyString(PACKAGE_NAME_PROPERTY, packageNames);
-            setPropertyBytes(SHA_256_CERT_PROPERTY, sha256Certs);
-            return new VisibilityDocument(super.build());
-        }
-    }
-
-
-    /**  Build the List of {@link VisibilityDocument} from visibility settings. */
+    /** Build the List of {@link VisibilityDocument} from visibility settings. */
     @NonNull
     public static List<VisibilityDocument> toVisibilityDocuments(
             @NonNull SetSchemaRequest setSchemaRequest) {
@@ -241,13 +218,11 @@
                 setSchemaRequest.getSchemasVisibleToPackages();
         Map<String, Set<Set<Integer>>> schemasVisibleToPermissions =
                 setSchemaRequest.getRequiredPermissionsForSchemaTypeVisibility();
-
         List<VisibilityDocument> visibilityDocuments = new ArrayList<>(searchSchemas.size());
-
         for (AppSearchSchema searchSchema : searchSchemas) {
             String schemaType = searchSchema.getSchemaType();
             VisibilityDocument.Builder documentBuilder =
-                    new VisibilityDocument.Builder(/*id=*/searchSchema.getSchemaType());
+                    new VisibilityDocument.Builder(/*id=*/ searchSchema.getSchemaType());
             documentBuilder.setNotDisplayedBySystem(
                     schemasNotDisplayedBySystem.contains(schemaType));
 
@@ -263,4 +238,209 @@
         }
         return visibilityDocuments;
     }
+
+    /**
+     * Generates a {@link GenericDocument} from the current class.
+     *
+     * <p>This conversion is needed until we don't treat Visibility related documents as
+     * {@link GenericDocument}s internally.
+     */
+    @NonNull
+    public GenericDocument toGenericDocument() {
+        if (mGenericDocument == null) {
+            GenericDocument.Builder<?> builder = new GenericDocument.Builder<>(
+                    NAMESPACE, mId, SCHEMA_TYPE);
+            builder.setPropertyBoolean(NOT_DISPLAYED_BY_SYSTEM_PROPERTY, mIsNotDisplayedBySystem);
+            builder.setPropertyString(PACKAGE_NAME_PROPERTY, mPackageNames);
+            builder.setPropertyBytes(SHA_256_CERT_PROPERTY, mSha256Certs);
+
+            // Generate an array of GenericDocument for VisibilityPermissionDocument.
+            if (mPermissionDocuments != null) {
+                GenericDocument[] permissionGenericDocs =
+                        new GenericDocument[mPermissionDocuments.length];
+                for (int i = 0; i < mPermissionDocuments.length; ++i) {
+                    permissionGenericDocs[i] = mPermissionDocuments[i].toGenericDocument();
+                }
+                builder.setPropertyDocument(PERMISSION_PROPERTY, permissionGenericDocs);
+            }
+
+            // The creationTimestamp doesn't matter for Visibility documents.
+            // But to make tests pass, we set it 0 so two GenericDocuments generated from
+            // the same VisibilityDocument can be same.
+            builder.setCreationTimestampMillis(0L);
+
+            mGenericDocument = builder.build();
+        }
+        return mGenericDocument;
+    }
+
+    @Override
+    public int hashCode() {
+        if (mHashCode == null) {
+            mHashCode = Objects.hash(
+                    mId,
+                    mIsNotDisplayedBySystem,
+                    Arrays.hashCode(mPackageNames),
+                    Arrays.deepHashCode(mSha256Certs),
+                    Arrays.hashCode(mPermissionDocuments));
+        }
+        return mHashCode;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof VisibilityDocument)) {
+            return false;
+        }
+        VisibilityDocument otherVisibilityDocument = (VisibilityDocument) other;
+        return mId.equals(otherVisibilityDocument.mId)
+                && mIsNotDisplayedBySystem == otherVisibilityDocument.mIsNotDisplayedBySystem
+                && Arrays.equals(
+                mPackageNames, otherVisibilityDocument.mPackageNames)
+                && Arrays.deepEquals(
+                mSha256Certs, otherVisibilityDocument.mSha256Certs)
+                && Arrays.equals(
+                mPermissionDocuments, otherVisibilityDocument.mPermissionDocuments);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        VisibilityDocumentCreator.writeToParcel(this, dest, flags);
+    }
+
+    /** Builder for {@link VisibilityDocument}. */
+    public static final class Builder {
+        private final Set<PackageIdentifier> mPackageIdentifiers = new ArraySet<>();
+        private String mId;
+        private boolean mIsNotDisplayedBySystem;
+        private VisibilityPermissionDocument[] mPermissionDocuments;
+
+        /**
+         * Creates a {@link Builder} for a {@link VisibilityDocument}.
+         *
+         * @param id The SchemaType of the {@link AppSearchSchema} that this {@link
+         *     VisibilityDocument} represents. The package and database prefix will be added in
+         *     server side. We are using prefixed schema type to be the final id of this {@link
+         *     VisibilityDocument}.
+         */
+        public Builder(@NonNull String id) {
+            mId = Objects.requireNonNull(id);
+        }
+
+        /**
+         * Constructs a {@link VisibilityDocument} from a {@link GenericDocument}.
+         *
+         * <p>This constructor is still needed until we don't treat Visibility related documents as
+         * {@link GenericDocument}s internally.
+         */
+        public Builder(@NonNull GenericDocument genericDocument) {
+            Objects.requireNonNull(genericDocument);
+
+            mId = genericDocument.getId();
+            mIsNotDisplayedBySystem = genericDocument.getPropertyBoolean(
+                    NOT_DISPLAYED_BY_SYSTEM_PROPERTY);
+
+            String[] packageNames = genericDocument.getPropertyStringArray(PACKAGE_NAME_PROPERTY);
+            byte[][] sha256Certs = genericDocument.getPropertyBytesArray(SHA_256_CERT_PROPERTY);
+            for (int i = 0; i < packageNames.length; ++i) {
+                mPackageIdentifiers.add(new PackageIdentifier(packageNames[i], sha256Certs[i]));
+            }
+
+            GenericDocument[] permissionDocs =
+                    genericDocument.getPropertyDocumentArray(PERMISSION_PROPERTY);
+            if (permissionDocs != null) {
+                mPermissionDocuments = new VisibilityPermissionDocument[permissionDocs.length];
+                for (int i = 0; i < permissionDocs.length; ++i) {
+                    mPermissionDocuments[i] = new VisibilityPermissionDocument.Builder(
+                            permissionDocs[i]).build();
+                }
+            }
+        }
+
+        public Builder(@NonNull VisibilityDocument visibilityDocument) {
+            Objects.requireNonNull(visibilityDocument);
+
+            mIsNotDisplayedBySystem = visibilityDocument.mIsNotDisplayedBySystem;
+            mPermissionDocuments = visibilityDocument.mPermissionDocuments;
+            for (int i = 0; i < visibilityDocument.mPackageNames.length; ++i) {
+                mPackageIdentifiers.add(new PackageIdentifier(visibilityDocument.mPackageNames[i],
+                            visibilityDocument.mSha256Certs[i]));
+            }
+        }
+
+        /** Sets id. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setId(@NonNull String id) {
+            mId = Objects.requireNonNull(id);
+            return this;
+        }
+
+        /** Sets whether this schema has opted out of platform surfacing. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setNotDisplayedBySystem(boolean notDisplayedBySystem) {
+            mIsNotDisplayedBySystem = notDisplayedBySystem;
+            return this;
+        }
+
+        /** Add {@link PackageIdentifier} of packages which has access to this schema. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder addVisibleToPackages(@NonNull Set<PackageIdentifier> packageIdentifiers) {
+            mPackageIdentifiers.addAll(Objects.requireNonNull(packageIdentifiers));
+            return this;
+        }
+
+        /** Add {@link PackageIdentifier} of packages which has access to this schema. */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder addVisibleToPackage(@NonNull PackageIdentifier packageIdentifier) {
+            mPackageIdentifiers.add(Objects.requireNonNull(packageIdentifier));
+            return this;
+        }
+
+        /**
+         * Sets required permission sets for a package needs to hold to the schema this {@link
+         * VisibilityDocument} represents.
+         *
+         * <p>The querier could have access if they holds ALL required permissions of ANY of the
+         * individual value sets.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setVisibleToPermissions(@NonNull Set<Set<Integer>> visibleToPermissions) {
+            Objects.requireNonNull(visibleToPermissions);
+            mPermissionDocuments =
+                    new VisibilityPermissionDocument[visibleToPermissions.size()];
+            int i = 0;
+            for (Set<Integer> allRequiredPermissions : visibleToPermissions) {
+                mPermissionDocuments[i++] =
+                        new VisibilityPermissionDocument.Builder(
+                                NAMESPACE, /*id=*/ String.valueOf(i))
+                                .setVisibleToAllRequiredPermissions(allRequiredPermissions)
+                                .build();
+            }
+            return this;
+        }
+
+        /** Build a {@link VisibilityDocument} */
+        @NonNull
+        public VisibilityDocument build() {
+            String[] packageNames = new String[mPackageIdentifiers.size()];
+            byte[][] sha256Certs = new byte[mPackageIdentifiers.size()][32];
+            int i = 0;
+            for (PackageIdentifier packageIdentifier : mPackageIdentifiers) {
+                packageNames[i] = packageIdentifier.getPackageName();
+                sha256Certs[i] = packageIdentifier.getSha256Certificate();
+                ++i;
+            }
+            return new VisibilityDocument(mId, mIsNotDisplayedBySystem,
+                    packageNames, sha256Certs, mPermissionDocuments);
+        }
+    }
 }
+
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityPermissionDocument.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityPermissionDocument.java
index 80a6a2e..54269fd 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityPermissionDocument.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/VisibilityPermissionDocument.java
@@ -16,12 +16,19 @@
 
 package androidx.appsearch.app;
 
+import android.os.Parcel;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.annotation.CanIgnoreReturnValue;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.VisibilityPermissionDocumentCreator;
 import androidx.collection.ArraySet;
 
+import java.util.Arrays;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -30,7 +37,11 @@
  * @exportToFramework:hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class VisibilityPermissionDocument extends GenericDocument {
[email protected](creator = "VisibilityPermissionDocumentCreator")
+public final class VisibilityPermissionDocument extends AbstractSafeParcelable {
+    @NonNull
+    public static final VisibilityPermissionDocumentCreator CREATOR =
+            new VisibilityPermissionDocumentCreator();
 
     /**
      * The Schema type for documents that hold AppSearch's metadata, such as visibility settings.
@@ -53,49 +64,79 @@
                     .build())
             .build();
 
-    VisibilityPermissionDocument(@NonNull GenericDocument genericDocument) {
-        super(genericDocument);
+    @NonNull
+    @Field(id = 1, getter = "getId")
+    private final String mId;
+
+    @NonNull
+    @Field(id = 2, getter = "getNamespace")
+    private final String mNamespace;
+
+    @Nullable
+    @Field(id = 3, getter = "getAllRequiredPermissionsInts")
+    // SafeParcelable doesn't support Set<Integer>, so we have to convert it to int[].
+    private final int[] mAllRequiredPermissions;
+
+    @Nullable
+    // We still need to convert this class to a GenericDocument until we completely treat it
+    // differently in AppSearchImpl.
+    // TODO(b/298118943) Remove this once internally we don't use GenericDocument to store
+    //  visibility information.
+    private GenericDocument mGenericDocument;
+
+    @Nullable
+    private Integer mHashCode;
+
+    @Constructor
+    VisibilityPermissionDocument(
+            @Param(id = 1) @NonNull String id,
+            @Param(id = 2) @NonNull String namespace,
+            @Param(id = 3) @Nullable int[] allRequiredPermissions) {
+        mId = Objects.requireNonNull(id);
+        mNamespace = Objects.requireNonNull(namespace);
+        mAllRequiredPermissions = allRequiredPermissions;
     }
 
     /**
-     * Returns an array of Android Permissions that caller mush hold to access the schema
-     * that the outer {@link VisibilityDocument} represents.
+     * Gets the id for this {@link VisibilityPermissionDocument}.
+     *
+     * <p>This is being used as the document id when we convert a
+     * {@link VisibilityPermissionDocument} to a {@link GenericDocument}.
+     */
+    @NonNull
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Gets the namespace for this {@link VisibilityPermissionDocument}.
+     *
+     * <p>This is being used as the namespace when we convert a
+     * {@link VisibilityPermissionDocument} to a {@link GenericDocument}.
+     */
+    @NonNull
+    public String getNamespace() {
+        return mNamespace;
+    }
+
+    /** Gets the required Android Permissions in an int array. */
+    @Nullable
+    int[] getAllRequiredPermissionsInts() {
+        return mAllRequiredPermissions;
+    }
+
+    /**
+     * Returns an array of Android Permissions that caller mush hold to access the schema that the
+     * outer {@link VisibilityDocument} represents.
      */
     @Nullable
     public Set<Integer> getAllRequiredPermissions() {
-        return toInts(getPropertyLongArray(ALL_REQUIRED_PERMISSIONS_PROPERTY));
-    }
-
-    /** Builder for {@link VisibilityPermissionDocument}. */
-    public static class Builder extends GenericDocument.Builder<Builder> {
-
-        /**
-         * Creates a {@link VisibilityDocument.Builder} for a {@link VisibilityDocument}.
-         */
-        public Builder(@NonNull String namespace, @NonNull String id) {
-            super(namespace, id, SCHEMA_TYPE);
-        }
-
-        /** Sets whether this schema has opted out of platform surfacing. */
-        @CanIgnoreReturnValue
-        @NonNull
-        public Builder setVisibleToAllRequiredPermissions(
-                @NonNull Set<Integer> allRequiredPermissions) {
-            setPropertyLong(ALL_REQUIRED_PERMISSIONS_PROPERTY, toLongs(allRequiredPermissions));
-            return this;
-        }
-
-        /** Build a {@link VisibilityPermissionDocument} */
-        @Override
-        @NonNull
-        public VisibilityPermissionDocument build() {
-            return new VisibilityPermissionDocument(super.build());
-        }
+        return toIntegerSet(mAllRequiredPermissions);
     }
 
     @NonNull
-    static long[] toLongs(@NonNull Set<Integer> properties) {
-        long[] outputs = new long[properties.size()];
+    private static int[] toInts(@NonNull Set<Integer> properties) {
+        int[] outputs = new int[properties.size()];
         int i = 0;
         for (int property : properties) {
             outputs[i++] = property;
@@ -104,14 +145,125 @@
     }
 
     @Nullable
-    private static Set<Integer> toInts(@Nullable long[] properties) {
+    private static Set<Integer> toIntegerSet(@Nullable int[] properties) {
         if (properties == null) {
             return null;
         }
         Set<Integer> outputs = new ArraySet<>(properties.length);
-        for (long property : properties) {
-            outputs.add((int) property);
+        for (int property : properties) {
+            outputs.add(property);
         }
         return outputs;
     }
+
+    /**
+     * Generates a {@link GenericDocument} from the current class.
+     *
+     * <p>This conversion is needed until we don't treat Visibility related documents as
+     * {@link GenericDocument}s internally.
+     */
+    @NonNull
+    public GenericDocument toGenericDocument() {
+        if (mGenericDocument == null) {
+            GenericDocument.Builder<?> builder = new GenericDocument.Builder<>(
+                    mNamespace, mId, SCHEMA_TYPE);
+
+            if (mAllRequiredPermissions != null) {
+                // GenericDocument only supports long, so int[] needs to be converted to
+                // long[] here.
+                long[] longs = new long[mAllRequiredPermissions.length];
+                for (int i = 0; i < mAllRequiredPermissions.length; ++i) {
+                    longs[i] = mAllRequiredPermissions[i];
+                }
+                builder.setPropertyLong(ALL_REQUIRED_PERMISSIONS_PROPERTY, longs);
+            }
+
+            mGenericDocument = builder.build();
+        }
+        return mGenericDocument;
+    }
+
+    @Override
+    public int hashCode() {
+        if (mHashCode == null) {
+            mHashCode = Objects.hash(mId, mNamespace, Arrays.hashCode(mAllRequiredPermissions));
+        }
+        return mHashCode;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof VisibilityPermissionDocument)) {
+            return false;
+        }
+        VisibilityPermissionDocument otherVisibilityPermissionDocument =
+                (VisibilityPermissionDocument) other;
+        return mId.equals(otherVisibilityPermissionDocument.mId)
+                && mNamespace.equals(otherVisibilityPermissionDocument.mNamespace)
+                && Arrays.equals(
+                mAllRequiredPermissions,
+                otherVisibilityPermissionDocument.mAllRequiredPermissions);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        VisibilityPermissionDocumentCreator.writeToParcel(this, dest, flags);
+    }
+
+    /** Builder for {@link VisibilityPermissionDocument}. */
+    public static final class Builder {
+        private String mId;
+        private String mNamespace;
+        private int[] mAllRequiredPermissions;
+
+        /**
+         * Constructs a {@link VisibilityPermissionDocument} from a {@link GenericDocument}.
+         *
+         * <p>This constructor is still needed until we don't treat Visibility related documents as
+         * {@link GenericDocument}s internally.
+         */
+        public Builder(@NonNull GenericDocument genericDocument) {
+            Objects.requireNonNull(genericDocument);
+            mId = genericDocument.getId();
+            mNamespace = genericDocument.getNamespace();
+            // GenericDocument only supports long[], so we need to convert it back to int[].
+            long[] longs = genericDocument.getPropertyLongArray(
+                    ALL_REQUIRED_PERMISSIONS_PROPERTY);
+            if (longs != null) {
+                mAllRequiredPermissions = new int[longs.length];
+                for (int i = 0; i < longs.length; ++i) {
+                    mAllRequiredPermissions[i] = (int) longs[i];
+                }
+            }
+        }
+
+        /** Creates a {@link VisibilityDocument.Builder} for a {@link VisibilityDocument}. */
+        public Builder(@NonNull String namespace, @NonNull String id) {
+            mNamespace = Objects.requireNonNull(namespace);
+            mId = Objects.requireNonNull(id);
+        }
+
+        /**
+         * Sets a set of Android Permissions that caller mush hold to access the schema that the
+         * outer {@link VisibilityDocument} represents.
+         */
+        @CanIgnoreReturnValue
+        @NonNull
+        public Builder setVisibleToAllRequiredPermissions(
+                @NonNull Set<Integer> allRequiredPermissions) {
+            mAllRequiredPermissions = toInts(Objects.requireNonNull(allRequiredPermissions));
+            return this;
+        }
+
+        /** Builds a {@link VisibilityPermissionDocument} */
+        @NonNull
+        public VisibilityPermissionDocument build() {
+            return new VisibilityPermissionDocument(mId,
+                    mNamespace,
+                    mAllRequiredPermissions);
+        }
+    }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/StubCreators.java b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/StubCreators.java
index c4f9241..b60c9e4 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/StubCreators.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/StubCreators.java
@@ -74,4 +74,12 @@
     /** Stub creator for {@link GenericDocumentParcel}. */
     public static class GenericDocumentParcelCreator extends AbstractCreator {
     }
+
+    /** Stub creator for {@link androidx.appsearch.app.VisibilityPermissionDocument}. */
+    public static class VisibilityPermissionDocumentCreator extends AbstractCreator {
+    }
+
+    /** Stub creator for {@link androidx.appsearch.app.VisibilityDocument}. */
+    public static class VisibilityDocumentCreator extends AbstractCreator {
+    }
 }