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.

The related change in Jetpack: aosp/2733415

Bug: 275629842
Test: atest CtsAppSearchTestCases CtsAppSearchHostTestCases AppSearchServicesTests AppSearchCoreTests ContactsIndexerTests

Change-Id: I17fb1f1a95bef6d690c3ae388555a94675cdb130
diff --git a/framework/java/android/app/appsearch/AppSearchSession.java b/framework/java/android/app/appsearch/AppSearchSession.java
index cb5f1b4..37db835 100644
--- a/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/framework/java/android/app/appsearch/AppSearchSession.java
@@ -168,28 +168,22 @@
             schemaBundles.add(schema.getBundle());
         }
 
-        // Extract a List<VisibilityDocument> from the request and convert to a
-        // List<VisibilityDocument.Bundle> to send via binder.
+        // Extract a List<VisibilityDocument> from the request
         List<VisibilityDocument> visibilityDocuments = VisibilityDocument
                 .toVisibilityDocuments(request);
-        List<Bundle> visibilityBundles = new ArrayList<>(visibilityDocuments.size());
-        for (int i = 0; i < visibilityDocuments.size(); i++) {
-            visibilityBundles.add(visibilityDocuments.get(i).getBundle());
-        }
-
         // No need to trigger migration if user never set migrator
         if (request.getMigrators().isEmpty()) {
             setSchemaNoMigrations(
                     request,
                     schemaBundles,
-                    visibilityBundles,
+                    visibilityDocuments,
                     callbackExecutor,
                     callback);
         } else {
             setSchemaWithMigrations(
                     request,
                     schemaBundles,
-                    visibilityBundles,
+                    visibilityDocuments,
                     workExecutor,
                     callbackExecutor,
                     callback);
@@ -847,7 +841,7 @@
     private void setSchemaNoMigrations(
             @NonNull SetSchemaRequest request,
             @NonNull List<Bundle> schemaBundles,
-            @NonNull List<Bundle> visibilityBundles,
+            @NonNull List<VisibilityDocument> visibilityDocs,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) {
         try {
@@ -855,7 +849,7 @@
                     mCallerAttributionSource,
                     mDatabaseName,
                     schemaBundles,
-                    visibilityBundles,
+                    visibilityDocs,
                     request.isForceOverride(),
                     request.getVersion(),
                     mUserHandle,
@@ -908,7 +902,7 @@
     private void setSchemaWithMigrations(
             @NonNull SetSchemaRequest request,
             @NonNull List<Bundle> schemaBundles,
-            @NonNull List<Bundle> visibilityBundles,
+            @NonNull List<VisibilityDocument> visibilityDocs,
             @NonNull Executor workExecutor,
             @NonNull @CallbackExecutor Executor callbackExecutor,
             @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) {
@@ -952,7 +946,7 @@
 
                 // No need to trigger migration if no migrator is active.
                 if (activeMigrators.isEmpty()) {
-                    setSchemaNoMigrations(request, schemaBundles, visibilityBundles,
+                    setSchemaNoMigrations(request, schemaBundles, visibilityDocs,
                             callbackExecutor, callback);
                     return;
                 }
@@ -968,7 +962,7 @@
                         mCallerAttributionSource,
                         mDatabaseName,
                         schemaBundles,
-                        visibilityBundles,
+                        visibilityDocs,
                         /*forceOverride=*/ false,
                         request.getVersion(),
                         mUserHandle,
@@ -1030,7 +1024,7 @@
                                 mCallerAttributionSource,
                                 mDatabaseName,
                                 schemaBundles,
-                                visibilityBundles,
+                                visibilityDocs,
                                 /*forceOverride=*/ true,
                                 request.getVersion(),
                                 mUserHandle,
diff --git a/framework/java/android/app/appsearch/VisibilityDocument.aidl b/framework/java/android/app/appsearch/VisibilityDocument.aidl
new file mode 100644
index 0000000..8b9824e
--- /dev/null
+++ b/framework/java/android/app/appsearch/VisibilityDocument.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright 2023, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.app.appsearch;
+
+/** {@hide} */
+parcelable VisibilityDocument;
\ No newline at end of file
diff --git a/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl b/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl
index 6ce3434..e063d1a 100644
--- a/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl
+++ b/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl
@@ -24,6 +24,7 @@
 import android.app.appsearch.aidl.IAppSearchObserverProxy;
 import android.app.appsearch.aidl.IAppSearchResultCallback;
 import android.app.appsearch.aidl.DocumentsParcel;
+import android.app.appsearch.VisibilityDocument;
 import android.content.AttributionSource;
 import android.os.ParcelFileDescriptor;
 
@@ -50,7 +51,7 @@
      * @param callerAttributionSource The permission identity of the package that owns this schema.
      * @param databaseName  The name of the database where this schema lives.
      * @param schemaBundles List of {@link AppSearchSchema} bundles.
-     * @param visibilityBundles List of {@link VisibilityDocument} bundles.
+     * @param visibilityDocs List of {@link VisibilityDocument} docs.
      * @param forceOverride Whether to apply the new schema even if it is incompatible. All
      *     incompatible documents will be deleted.
      * @param schemaVersion  The overall schema version number of the request.
@@ -66,7 +67,7 @@
         in AppSearchAttributionSource callerAttributionSource,
         in String databaseName,
         in List<Bundle> schemaBundles,
-        in List<Bundle> visibilityBundles,
+        in List<VisibilityDocument> visibilityDocs,
         boolean forceOverride,
         in int schemaVersion,
         in UserHandle userHandle,
diff --git a/framework/java/external/android/app/appsearch/VisibilityDocument.java b/framework/java/external/android/app/appsearch/VisibilityDocument.java
index 8b8dd80..db389a8 100644
--- a/framework/java/external/android/app/appsearch/VisibilityDocument.java
+++ b/framework/java/external/android/app/appsearch/VisibilityDocument.java
@@ -18,10 +18,13 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.appsearch.annotation.CanIgnoreReturnValue;
-import android.os.Bundle;
+import android.app.appsearch.safeparcel.AbstractSafeParcelable;
+import android.app.appsearch.safeparcel.SafeParcelable;
+import android.os.Parcel;
 import android.util.ArraySet;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -33,7 +36,11 @@
  *
  * @hide
  */
-public class VisibilityDocument extends GenericDocument {
+@SafeParcelable.Class(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.
      */
@@ -99,17 +106,62 @@
                                     .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;
     }
 
     /**
@@ -119,7 +171,7 @@
      */
     @NonNull
     public String[] getPackageNames() {
-        return Objects.requireNonNull(getPropertyStringArray(PACKAGE_NAME_PROPERTY));
+        return mPackageNames;
     }
 
     /**
@@ -129,24 +181,32 @@
      */
     @NonNull
     public byte[][] getSha256Certs() {
-        return Objects.requireNonNull(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.
      */
-    @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);
             }
@@ -154,90 +214,6 @@
         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) {
-            Objects.requireNonNull(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) {
-            Objects.requireNonNull(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) {
-            Objects.requireNonNull(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. */
     @NonNull
     public static List<VisibilityDocument> toVisibilityDocuments(
@@ -248,9 +224,7 @@
                 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 =
@@ -270,4 +244,208 @@
         }
         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/framework/java/external/android/app/appsearch/VisibilityPermissionDocument.java b/framework/java/external/android/app/appsearch/VisibilityPermissionDocument.java
index d1ce699..fc3ccb0 100644
--- a/framework/java/external/android/app/appsearch/VisibilityPermissionDocument.java
+++ b/framework/java/external/android/app/appsearch/VisibilityPermissionDocument.java
@@ -19,8 +19,13 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.appsearch.annotation.CanIgnoreReturnValue;
+import android.app.appsearch.safeparcel.AbstractSafeParcelable;
+import android.app.appsearch.safeparcel.SafeParcelable;
+import android.os.Parcel;
 import android.util.ArraySet;
 
+import java.util.Arrays;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -29,7 +34,11 @@
  *
  * @hide
  */
-public class VisibilityPermissionDocument extends GenericDocument {
+@SafeParcelable.Class(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.
@@ -54,8 +63,64 @@
                                     .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;
+    }
+
+    /**
+     * 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;
     }
 
     /**
@@ -64,37 +129,12 @@
      */
     @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;
@@ -103,14 +143,122 @@
     }
 
     @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/service/java/com/android/server/appsearch/AppSearchManagerService.java b/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 93321cc..e32605c 100644
--- a/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -347,7 +347,7 @@
                 @NonNull AppSearchAttributionSource callerAttributionSource,
                 @NonNull String databaseName,
                 @NonNull List<Bundle> schemaBundles,
-                @NonNull List<Bundle> visibilityBundles,
+                @NonNull List<VisibilityDocument> visibilityDocuments,
                 boolean forceOverride,
                 int schemaVersion,
                 @NonNull UserHandle userHandle,
@@ -357,7 +357,7 @@
             Objects.requireNonNull(callerAttributionSource);
             Objects.requireNonNull(databaseName);
             Objects.requireNonNull(schemaBundles);
-            Objects.requireNonNull(visibilityBundles);
+            Objects.requireNonNull(visibilityDocuments);
             Objects.requireNonNull(userHandle);
             Objects.requireNonNull(callback);
 
@@ -395,12 +395,6 @@
                     for (int i = 0; i < schemaBundles.size(); i++) {
                         schemas.add(new AppSearchSchema(schemaBundles.get(i)));
                     }
-                    List<VisibilityDocument> visibilityDocuments =
-                            new ArrayList<>(visibilityBundles.size());
-                    for (int i = 0; i < visibilityBundles.size(); i++) {
-                        visibilityDocuments.add(
-                                new VisibilityDocument(visibilityBundles.get(i)));
-                    }
                     long rebuildFromBundleLatencyEndTimeMillis = SystemClock.elapsedRealtime();
 
                     instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
diff --git a/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index 8feb1de..1480893 100644
--- a/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -828,8 +828,8 @@
                 // they can do this via the public API too.
                 String prefixedSchemaType = prefix + unPrefixedDocument.getId();
                 prefixedVisibilityDocuments.add(
-                        new VisibilityDocument(
-                                unPrefixedDocument.toBuilder().setId(prefixedSchemaType).build()));
+                        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/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java b/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java
index 491969b..3bd1034 100644
--- a/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java
+++ b/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java
@@ -169,7 +169,7 @@
             mAppSearchImpl.putDocument(
                     VISIBILITY_PACKAGE_NAME,
                     VISIBILITY_DATABASE_NAME,
-                    prefixedVisibilityDocument,
+                    prefixedVisibilityDocument.toGenericDocument(),
                     /*sendChangeNotifications=*/ false,
                     /*logger=*/ null);
             mVisibilityDocumentMap.put(
@@ -237,13 +237,14 @@
             try {
                 // Note: We use the other clients' prefixed schema type as ids
                 visibilityDocument =
-                        new VisibilityDocument(
-                                mAppSearchImpl.getDocument(
-                                        VISIBILITY_PACKAGE_NAME,
-                                        VISIBILITY_DATABASE_NAME,
-                                        VisibilityDocument.NAMESPACE,
-                                        /*id=*/ prefixedSchemaType,
-                                        /*typePropertyPaths=*/ Collections.emptyMap()));
+                        new VisibilityDocument.Builder(
+                                        mAppSearchImpl.getDocument(
+                                                VISIBILITY_PACKAGE_NAME,
+                                                VISIBILITY_DATABASE_NAME,
+                                                VisibilityDocument.NAMESPACE,
+                                                /*id=*/ prefixedSchemaType,
+                                                /*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
@@ -285,7 +286,7 @@
             mAppSearchImpl.putDocument(
                     VISIBILITY_PACKAGE_NAME,
                     VISIBILITY_DATABASE_NAME,
-                    migratedDocument,
+                    migratedDocument.toGenericDocument(),
                     /*sendChangeNotifications=*/ false,
                     /*logger=*/ null);
         }
diff --git a/testing/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/testing/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index 961099c..e80c5a9 100644
--- a/testing/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ b/testing/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
@@ -2539,7 +2539,6 @@
                 new VisibilityDocument.Builder("schema")
                         .setNotDisplayedBySystem(true)
                         .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                        .setCreationTimestampMillis(12345L)
                         .build();
 
         // Insert schema for package A and B.
@@ -2589,13 +2588,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"))
                 .isEqualTo(expectedVisibilityDocumentA);
@@ -4836,7 +4833,6 @@
                 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());
@@ -4859,19 +4855,19 @@
                 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(
-                                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                                VisibilityDocument.NAMESPACE,
-                                /*id=*/ prefix + "Email",
-                                /*typePropertyPaths=*/ Collections.emptyMap()));
+                new VisibilityDocument.Builder(
+                                mAppSearchImpl.getDocument(
+                                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                                        VisibilityStore.VISIBILITY_DATABASE_NAME,
+                                        VisibilityDocument.NAMESPACE,
+                                        /*id=*/ prefix + "Email",
+                                        /*typePropertyPaths=*/ Collections.emptyMap()))
+                        .build();
         assertThat(actualDocument).isEqualTo(expectedDocument);
     }
 
@@ -4882,7 +4878,6 @@
                 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());
@@ -4905,19 +4900,19 @@
                 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(
-                                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                                VisibilityDocument.NAMESPACE,
-                                /*id=*/ prefix1 + "Email1",
-                                /*typePropertyPaths=*/ Collections.emptyMap()));
+                new VisibilityDocument.Builder(
+                                mAppSearchImpl.getDocument(
+                                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                                        VisibilityStore.VISIBILITY_DATABASE_NAME,
+                                        VisibilityDocument.NAMESPACE,
+                                        /*id=*/ prefix1 + "Email1",
+                                        /*typePropertyPaths=*/ Collections.emptyMap()))
+                        .build();
         assertThat(actualDocument1).isEqualTo(expectedDocument1);
 
         // Create Visibility Document for Email2
@@ -4925,7 +4920,6 @@
                 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());
@@ -4948,19 +4942,19 @@
                 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(
-                                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                                VisibilityDocument.NAMESPACE,
-                                /*id=*/ prefix2 + "Email2",
-                                /*typePropertyPaths=*/ Collections.emptyMap()));
+                new VisibilityDocument.Builder(
+                                mAppSearchImpl.getDocument(
+                                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                                        VisibilityStore.VISIBILITY_DATABASE_NAME,
+                                        VisibilityDocument.NAMESPACE,
+                                        /*id=*/ prefix2 + "Email2",
+                                        /*typePropertyPaths=*/ Collections.emptyMap()))
+                        .build();
         assertThat(actualDocument2).isEqualTo(expectedDocument2);
 
         // Check the existing visibility document retains.
@@ -4968,13 +4962,14 @@
                 .isEqualTo(expectedDocument1);
         // Verify the VisibilityDocument is saved to AppSearchImpl.
         actualDocument1 =
-                new VisibilityDocument(
-                        mAppSearchImpl.getDocument(
-                                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                                VisibilityDocument.NAMESPACE,
-                                /*id=*/ prefix1 + "Email1",
-                                /*typePropertyPaths=*/ Collections.emptyMap()));
+                new VisibilityDocument.Builder(
+                                mAppSearchImpl.getDocument(
+                                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                                        VisibilityStore.VISIBILITY_DATABASE_NAME,
+                                        VisibilityDocument.NAMESPACE,
+                                        /*id=*/ prefix1 + "Email1",
+                                        /*typePropertyPaths=*/ Collections.emptyMap()))
+                        .build();
         assertThat(actualDocument1).isEqualTo(expectedDocument1);
     }
 
@@ -4985,7 +4980,6 @@
                 new VisibilityDocument.Builder("Email")
                         .setNotDisplayedBySystem(true)
                         .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                        .setCreationTimestampMillis(12345L)
                         .build();
 
         List<AppSearchSchema> schemas =
@@ -5007,19 +5001,19 @@
                 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(
-                                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                                VisibilityDocument.NAMESPACE,
-                                /*id=*/ prefix + "Email",
-                                /*typePropertyPaths=*/ Collections.emptyMap()));
+                new VisibilityDocument.Builder(
+                                mAppSearchImpl.getDocument(
+                                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                                        VisibilityStore.VISIBILITY_DATABASE_NAME,
+                                        VisibilityDocument.NAMESPACE,
+                                        /*id=*/ prefix + "Email",
+                                        /*typePropertyPaths=*/ Collections.emptyMap()))
+                        .build();
         assertThat(actualDocument).isEqualTo(expectedDocument);
 
         // Set schema Email and its all-default visibility document to AppSearch database1
@@ -5058,7 +5052,6 @@
                 new VisibilityDocument.Builder("Email")
                         .setNotDisplayedBySystem(true)
                         .addVisibleToPackage(new PackageIdentifier("pkgBar", new byte[32]))
-                        .setCreationTimestampMillis(12345L)
                         .build();
 
         List<AppSearchSchema> schemas =
@@ -5078,19 +5071,19 @@
                 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(
-                                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                                VisibilityDocument.NAMESPACE,
-                                /*id=*/ prefix + "Email",
-                                /*typePropertyPaths=*/ Collections.emptyMap()));
+                new VisibilityDocument.Builder(
+                                mAppSearchImpl.getDocument(
+                                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                                        VisibilityStore.VISIBILITY_DATABASE_NAME,
+                                        VisibilityDocument.NAMESPACE,
+                                        /*id=*/ prefix + "Email",
+                                        /*typePropertyPaths=*/ Collections.emptyMap()))
+                        .build();
         assertThat(actualDocument).isEqualTo(expectedDocument);
 
         // remove the schema and visibility setting from AppSearch
@@ -5137,7 +5130,6 @@
                 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());
@@ -5168,20 +5160,20 @@
                 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(
-                                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                                VisibilityDocument.NAMESPACE,
-                                /*id=*/ prefix + "Email",
-                                /*typePropertyPaths=*/ Collections.emptyMap()));
+                new VisibilityDocument.Builder(
+                                mAppSearchImpl.getDocument(
+                                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                                        VisibilityStore.VISIBILITY_DATABASE_NAME,
+                                        VisibilityDocument.NAMESPACE,
+                                        /*id=*/ prefix + "Email",
+                                        /*typePropertyPaths=*/ Collections.emptyMap()))
+                        .build();
         assertThat(actualDocument).isEqualTo(expectedDocument);
 
         // remove schema and visibility document
diff --git a/testing/servicestests/src/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java b/testing/servicestests/src/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java
index 8493339..ada02f7 100644
--- a/testing/servicestests/src/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java
+++ b/testing/servicestests/src/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV0Test.java
@@ -148,32 +148,32 @@
                         /*visibilityChecker=*/ null);
 
         VisibilityDocument actualDocument1 =
-                new VisibilityDocument(
-                        appSearchImpl.getDocument(
-                                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                                VisibilityDocument.NAMESPACE,
-                                /*id=*/ prefix + "Schema1",
-                                /*typePropertyPaths=*/ Collections.emptyMap()));
+                new VisibilityDocument.Builder(
+                                appSearchImpl.getDocument(
+                                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                                        VisibilityStore.VISIBILITY_DATABASE_NAME,
+                                        VisibilityDocument.NAMESPACE,
+                                        /*id=*/ prefix + "Schema1",
+                                        /*typePropertyPaths=*/ Collections.emptyMap()))
+                        .build();
         VisibilityDocument actualDocument2 =
-                new VisibilityDocument(
-                        appSearchImpl.getDocument(
-                                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                                VisibilityDocument.NAMESPACE,
-                                /*id=*/ prefix + "Schema2",
-                                /*typePropertyPaths=*/ Collections.emptyMap()));
+                new VisibilityDocument.Builder(
+                                appSearchImpl.getDocument(
+                                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                                        VisibilityStore.VISIBILITY_DATABASE_NAME,
+                                        VisibilityDocument.NAMESPACE,
+                                        /*id=*/ prefix + "Schema2",
+                                        /*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/testing/servicestests/src/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java b/testing/servicestests/src/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java
index de60688..91d0d88 100644
--- a/testing/servicestests/src/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java
+++ b/testing/servicestests/src/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStoreMigrationHelperFromV1Test.java
@@ -137,13 +137,14 @@
                         /*visibilityChecker=*/ null);
 
         VisibilityDocument actualDocument =
-                new VisibilityDocument(
-                        appSearchImpl.getDocument(
-                                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                                VisibilityDocument.NAMESPACE,
-                                /*id=*/ prefix + "Schema",
-                                /*typePropertyPaths=*/ Collections.emptyMap()));
+                new VisibilityDocument.Builder(
+                                appSearchImpl.getDocument(
+                                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                                        VisibilityStore.VISIBILITY_DATABASE_NAME,
+                                        VisibilityDocument.NAMESPACE,
+                                        /*id=*/ prefix + "Schema",
+                                        /*typePropertyPaths=*/ Collections.emptyMap()))
+                        .build();
 
         assertThat(actualDocument.isNotDisplayedBySystem()).isTrue();
         assertThat(actualDocument.getPackageNames())
diff --git a/testing/servicestests/src/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStoreTest.java b/testing/servicestests/src/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStoreTest.java
index fe47ad8..7e7be9f 100644
--- a/testing/servicestests/src/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStoreTest.java
+++ b/testing/servicestests/src/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStoreTest.java
@@ -111,13 +111,14 @@
         assertThat(mVisibilityStore.getVisibility(prefix + "Email")).isEqualTo(visibilityDocument);
         // Verify the VisibilityDocument is saved to AppSearchImpl.
         VisibilityDocument actualDocument =
-                new VisibilityDocument(
-                        mAppSearchImpl.getDocument(
-                                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                                VisibilityDocument.NAMESPACE,
-                                /*id=*/ prefix + "Email",
-                                /*typePropertyPaths=*/ Collections.emptyMap()));
+                new VisibilityDocument.Builder(
+                                mAppSearchImpl.getDocument(
+                                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                                        VisibilityStore.VISIBILITY_DATABASE_NAME,
+                                        VisibilityDocument.NAMESPACE,
+                                        /*id=*/ prefix + "Email",
+                                        /*typePropertyPaths=*/ Collections.emptyMap()))
+                        .build();
         assertThat(actualDocument).isEqualTo(visibilityDocument);
     }
 
@@ -133,13 +134,14 @@
         assertThat(mVisibilityStore.getVisibility("Email")).isEqualTo(visibilityDocument);
         // Verify the VisibilityDocument is saved to AppSearchImpl.
         VisibilityDocument actualDocument =
-                new VisibilityDocument(
-                        mAppSearchImpl.getDocument(
-                                VisibilityStore.VISIBILITY_PACKAGE_NAME,
-                                VisibilityStore.VISIBILITY_DATABASE_NAME,
-                                VisibilityDocument.NAMESPACE,
-                                /*id=*/ "Email",
-                                /*typePropertyPaths=*/ Collections.emptyMap()));
+                new VisibilityDocument.Builder(
+                                mAppSearchImpl.getDocument(
+                                        VisibilityStore.VISIBILITY_PACKAGE_NAME,
+                                        VisibilityStore.VISIBILITY_DATABASE_NAME,
+                                        VisibilityDocument.NAMESPACE,
+                                        /*id=*/ "Email",
+                                        /*typePropertyPaths=*/ Collections.emptyMap()))
+                        .build();
         assertThat(actualDocument).isEqualTo(visibilityDocument);
 
         mVisibilityStore.removeVisibility(ImmutableSet.of(visibilityDocument.getId()));