Sync AppSearch from framework.

Contains changes:
* 37a4e9: Split indexing configs into per-type configs.
* 69d6e3: Add projection to the Get api.

Bug: 175039682
Bug: 177572431
Bug: 171857731
Test: Presubmit

Change-Id: Ida457b2d617be6e85557023bfb4716ad78c8db6c
diff --git a/framework/api/current.txt b/framework/api/current.txt
index 401624f..ebb0070 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -43,6 +43,15 @@
     method @IntRange(from=0) public int getVersion();
   }
 
+  public static final class AppSearchSchema.BooleanPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+  }
+
+  public static final class AppSearchSchema.BooleanPropertyConfig.Builder {
+    ctor public AppSearchSchema.BooleanPropertyConfig.Builder(@NonNull String);
+    method @NonNull public android.app.appsearch.AppSearchSchema.BooleanPropertyConfig build();
+    method @NonNull public android.app.appsearch.AppSearchSchema.BooleanPropertyConfig.Builder setCardinality(int);
+  }
+
   public static final class AppSearchSchema.Builder {
     ctor public AppSearchSchema.Builder(@NonNull String);
     method @NonNull public android.app.appsearch.AppSearchSchema.Builder addProperty(@NonNull android.app.appsearch.AppSearchSchema.PropertyConfig);
@@ -50,22 +59,58 @@
     method @NonNull public android.app.appsearch.AppSearchSchema.Builder setVersion(@IntRange(from=0) int);
   }
 
-  public static final class AppSearchSchema.PropertyConfig {
+  public static final class AppSearchSchema.BytesPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+  }
+
+  public static final class AppSearchSchema.BytesPropertyConfig.Builder {
+    ctor public AppSearchSchema.BytesPropertyConfig.Builder(@NonNull String);
+    method @NonNull public android.app.appsearch.AppSearchSchema.BytesPropertyConfig build();
+    method @NonNull public android.app.appsearch.AppSearchSchema.BytesPropertyConfig.Builder setCardinality(int);
+  }
+
+  public static final class AppSearchSchema.DocumentPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+    method @NonNull public String getSchemaType();
+    method public boolean isIndexNestedProperties();
+  }
+
+  public static final class AppSearchSchema.DocumentPropertyConfig.Builder {
+    ctor public AppSearchSchema.DocumentPropertyConfig.Builder(@NonNull String);
+    method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig build();
+    method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder setCardinality(int);
+    method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder setIndexNestedProperties(boolean);
+    method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder setSchemaType(@NonNull String);
+  }
+
+  public static final class AppSearchSchema.DoublePropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+  }
+
+  public static final class AppSearchSchema.DoublePropertyConfig.Builder {
+    ctor public AppSearchSchema.DoublePropertyConfig.Builder(@NonNull String);
+    method @NonNull public android.app.appsearch.AppSearchSchema.DoublePropertyConfig build();
+    method @NonNull public android.app.appsearch.AppSearchSchema.DoublePropertyConfig.Builder setCardinality(int);
+  }
+
+  public static final class AppSearchSchema.Int64PropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+  }
+
+  public static final class AppSearchSchema.Int64PropertyConfig.Builder {
+    ctor public AppSearchSchema.Int64PropertyConfig.Builder(@NonNull String);
+    method @NonNull public android.app.appsearch.AppSearchSchema.Int64PropertyConfig build();
+    method @NonNull public android.app.appsearch.AppSearchSchema.Int64PropertyConfig.Builder setCardinality(int);
+  }
+
+  public abstract static class AppSearchSchema.PropertyConfig {
     method public int getCardinality();
     method public int getDataType();
-    method public int getIndexingType();
     method @NonNull public String getName();
-    method @Nullable public String getSchemaType();
-    method public int getTokenizerType();
     field public static final int CARDINALITY_OPTIONAL = 2; // 0x2
     field public static final int CARDINALITY_REPEATED = 1; // 0x1
     field public static final int CARDINALITY_REQUIRED = 3; // 0x3
-    field public static final int DATA_TYPE_BOOLEAN = 4; // 0x4
-    field public static final int DATA_TYPE_BYTES = 5; // 0x5
-    field public static final int DATA_TYPE_DOCUMENT = 6; // 0x6
-    field public static final int DATA_TYPE_DOUBLE = 3; // 0x3
-    field public static final int DATA_TYPE_INT64 = 2; // 0x2
-    field public static final int DATA_TYPE_STRING = 1; // 0x1
+  }
+
+  public static final class AppSearchSchema.StringPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig {
+    method public int getIndexingType();
+    method public int getTokenizerType();
     field public static final int INDEXING_TYPE_EXACT_TERMS = 1; // 0x1
     field public static final int INDEXING_TYPE_NONE = 0; // 0x0
     field public static final int INDEXING_TYPE_PREFIXES = 2; // 0x2
@@ -73,14 +118,12 @@
     field public static final int TOKENIZER_TYPE_PLAIN = 1; // 0x1
   }
 
-  public static final class AppSearchSchema.PropertyConfig.Builder {
-    ctor public AppSearchSchema.PropertyConfig.Builder(@NonNull String);
-    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig build();
-    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setCardinality(int);
-    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setDataType(int);
-    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setIndexingType(int);
-    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setSchemaType(@NonNull String);
-    method @NonNull public android.app.appsearch.AppSearchSchema.PropertyConfig.Builder setTokenizerType(int);
+  public static final class AppSearchSchema.StringPropertyConfig.Builder {
+    ctor public AppSearchSchema.StringPropertyConfig.Builder(@NonNull String);
+    method @NonNull public android.app.appsearch.AppSearchSchema.StringPropertyConfig build();
+    method @NonNull public android.app.appsearch.AppSearchSchema.StringPropertyConfig.Builder setCardinality(int);
+    method @NonNull public android.app.appsearch.AppSearchSchema.StringPropertyConfig.Builder setIndexingType(int);
+    method @NonNull public android.app.appsearch.AppSearchSchema.StringPropertyConfig.Builder setTokenizerType(int);
   }
 
   public final class AppSearchSession implements java.io.Closeable {
@@ -143,11 +186,15 @@
 
   public final class GetByUriRequest {
     method @NonNull public String getNamespace();
+    method @NonNull public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getProjections();
     method @NonNull public java.util.Set<java.lang.String> getUris();
+    field public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
   }
 
   public static final class GetByUriRequest.Builder {
     ctor public GetByUriRequest.Builder();
+    method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.lang.String...);
+    method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
     method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@NonNull java.lang.String...);
     method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUri(@NonNull java.util.Collection<java.lang.String>);
     method @NonNull public android.app.appsearch.GetByUriRequest build();
diff --git a/framework/java/android/app/appsearch/AppSearchManager.java b/framework/java/android/app/appsearch/AppSearchManager.java
index ed4e034..814800e 100644
--- a/framework/java/android/app/appsearch/AppSearchManager.java
+++ b/framework/java/android/app/appsearch/AppSearchManager.java
@@ -330,6 +330,7 @@
                     DEFAULT_DATABASE_NAME,
                     request.getNamespace(),
                     uris,
+                    request.getProjectionsVisibleToPackagesInternal(),
                     mContext.getUserId(),
                     new IAppSearchBatchResultCallback.Stub() {
                         public void onResult(AppSearchBatchResult result) {
diff --git a/framework/java/android/app/appsearch/AppSearchSession.java b/framework/java/android/app/appsearch/AppSearchSession.java
index 928bf7d..c4c123c 100644
--- a/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/framework/java/android/app/appsearch/AppSearchSession.java
@@ -311,8 +311,13 @@
         Objects.requireNonNull(callback);
         Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
         try {
-            mService.getDocuments(mPackageName, mDatabaseName, request.getNamespace(),
-                    new ArrayList<>(request.getUris()), mUserId,
+            mService.getDocuments(
+                    mPackageName,
+                    mDatabaseName,
+                    request.getNamespace(),
+                    new ArrayList<>(request.getUris()),
+                    request.getProjectionsVisibleToPackagesInternal(),
+                    mUserId,
                     new IAppSearchBatchResultCallback.Stub() {
                         public void onResult(AppSearchBatchResult result) {
                             executor.execute(() -> {
diff --git a/framework/java/android/app/appsearch/IAppSearchManager.aidl b/framework/java/android/app/appsearch/IAppSearchManager.aidl
index be76f42..68ae23f 100644
--- a/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -97,6 +97,8 @@
      * @param databaseName  The databaseName this document resides in.
      * @param namespace    The namespace this document resides in.
      * @param uris The URIs of the documents to retrieve
+     * @param typePropertyPaths A map of schema type to a list of property paths to return in the
+     *     result.
      * @param userId Id of the calling user
      * @param callback
      *     If the call fails to start, {@link IAppSearchBatchResultCallback#onSystemError}
@@ -110,6 +112,7 @@
         in String databaseName,
         in String namespace,
         in List<String> uris,
+        in Map<String, List<String>> typePropertyPaths,
         in int userId,
         in IAppSearchBatchResultCallback callback);
 
diff --git a/framework/java/external/android/app/appsearch/AppSearchEmail.java b/framework/java/external/android/app/appsearch/AppSearchEmail.java
index 9ca363e..d394904 100644
--- a/framework/java/external/android/app/appsearch/AppSearchEmail.java
+++ b/framework/java/external/android/app/appsearch/AppSearchEmail.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.appsearch.AppSearchSchema.PropertyConfig;
+import android.app.appsearch.AppSearchSchema.StringPropertyConfig;
 
 /**
  * Encapsulates a {@link GenericDocument} that represent an email.
@@ -41,46 +42,40 @@
     public static final AppSearchSchema SCHEMA =
             new AppSearchSchema.Builder(SCHEMA_TYPE)
                     .addProperty(
-                            new PropertyConfig.Builder(KEY_FROM)
-                                    .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                            new StringPropertyConfig.Builder(KEY_FROM)
                                     .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                                    .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                    .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+                                    .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                    .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
                                     .build())
                     .addProperty(
-                            new PropertyConfig.Builder(KEY_TO)
-                                    .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                            new StringPropertyConfig.Builder(KEY_TO)
                                     .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
-                                    .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                    .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+                                    .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                    .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
                                     .build())
                     .addProperty(
-                            new PropertyConfig.Builder(KEY_CC)
-                                    .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                            new StringPropertyConfig.Builder(KEY_CC)
                                     .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
-                                    .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                    .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+                                    .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                    .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
                                     .build())
                     .addProperty(
-                            new PropertyConfig.Builder(KEY_BCC)
-                                    .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                            new StringPropertyConfig.Builder(KEY_BCC)
                                     .setCardinality(PropertyConfig.CARDINALITY_REPEATED)
-                                    .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                    .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+                                    .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                    .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
                                     .build())
                     .addProperty(
-                            new PropertyConfig.Builder(KEY_SUBJECT)
-                                    .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                            new StringPropertyConfig.Builder(KEY_SUBJECT)
                                     .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                                    .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                    .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+                                    .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                    .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
                                     .build())
                     .addProperty(
-                            new PropertyConfig.Builder(KEY_BODY)
-                                    .setDataType(PropertyConfig.DATA_TYPE_STRING)
+                            new StringPropertyConfig.Builder(KEY_BODY)
                                     .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
-                                    .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
-                                    .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
+                                    .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                    .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES)
                                     .build())
                     .build();
 
diff --git a/framework/java/external/android/app/appsearch/AppSearchSchema.java b/framework/java/external/android/app/appsearch/AppSearchSchema.java
index 1f882ec..7f5d202 100644
--- a/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -98,7 +98,7 @@
         }
         List<PropertyConfig> ret = new ArrayList<>(propertyBundles.size());
         for (int i = 0; i < propertyBundles.size(); i++) {
-            ret.add(new PropertyConfig(propertyBundles.get(i)));
+            ret.add(PropertyConfig.fromBundle(propertyBundles.get(i)));
         }
         return ret;
     }
@@ -210,18 +210,15 @@
     }
 
     /**
-     * Configuration for a single property (field) of a document type.
+     * Common configuration for a single property (field) in a Document.
      *
      * <p>For example, an {@code EmailMessage} would be a type and the {@code subject} would be a
      * property.
      */
-    public static final class PropertyConfig {
-        private static final String NAME_FIELD = "name";
-        private static final String DATA_TYPE_FIELD = "dataType";
-        private static final String SCHEMA_TYPE_FIELD = "schemaType";
-        private static final String CARDINALITY_FIELD = "cardinality";
-        private static final String INDEXING_TYPE_FIELD = "indexingType";
-        private static final String TOKENIZER_TYPE_FIELD = "tokenizerType";
+    public abstract static class PropertyConfig {
+        static final String NAME_FIELD = "name";
+        static final String DATA_TYPE_FIELD = "dataType";
+        static final String CARDINALITY_FIELD = "cardinality";
 
         /**
          * Physical data-types of the contents of the property.
@@ -242,18 +239,31 @@
         @Retention(RetentionPolicy.SOURCE)
         public @interface DataType {}
 
+        /** @hide */
         public static final int DATA_TYPE_STRING = 1;
+
+        /** @hide */
         public static final int DATA_TYPE_INT64 = 2;
+
+        /** @hide */
         public static final int DATA_TYPE_DOUBLE = 3;
+
+        /** @hide */
         public static final int DATA_TYPE_BOOLEAN = 4;
 
-        /** Unstructured BLOB. */
+        /**
+         * Unstructured BLOB.
+         *
+         * @hide
+         */
         public static final int DATA_TYPE_BYTES = 5;
 
         /**
          * Indicates that the property is itself a {@link GenericDocument}, making it part of a
          * hierarchical schema. Any property using this DataType MUST have a valid {@link
          * PropertyConfig#getSchemaType}.
+         *
+         * @hide
          */
         public static final int DATA_TYPE_DOCUMENT = 6;
 
@@ -282,6 +292,97 @@
         /** Exactly one value [1]. */
         public static final int CARDINALITY_REQUIRED = 3;
 
+        final Bundle mBundle;
+
+        @Nullable private Integer mHashCode;
+
+        PropertyConfig(@NonNull Bundle bundle) {
+            mBundle = Preconditions.checkNotNull(bundle);
+        }
+
+        @Override
+        public String toString() {
+            return mBundle.toString();
+        }
+
+        /** Returns the name of this property. */
+        @NonNull
+        public String getName() {
+            return mBundle.getString(NAME_FIELD, "");
+        }
+
+        /** Returns the type of data the property contains (e.g. string, int, bytes, etc). */
+        public @DataType int getDataType() {
+            return mBundle.getInt(DATA_TYPE_FIELD, -1);
+        }
+
+        /**
+         * Returns the cardinality of the property (whether it is optional, required or repeated).
+         */
+        public @Cardinality int getCardinality() {
+            return mBundle.getInt(CARDINALITY_FIELD, -1);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (this == other) {
+                return true;
+            }
+            if (!(other instanceof PropertyConfig)) {
+                return false;
+            }
+            PropertyConfig otherProperty = (PropertyConfig) other;
+            return BundleUtil.deepEquals(this.mBundle, otherProperty.mBundle);
+        }
+
+        @Override
+        public int hashCode() {
+            if (mHashCode == null) {
+                mHashCode = BundleUtil.deepHashCode(mBundle);
+            }
+            return mHashCode;
+        }
+
+        /**
+         * Converts a {@link Bundle} into a {@link PropertyConfig} depending on its internal data
+         * type.
+         *
+         * <p>The bundle is not cloned.
+         *
+         * @throws IllegalArgumentException if the bundle does no contain a recognized value in its
+         *     {@code DATA_TYPE_FIELD}.
+         * @hide
+         */
+        @NonNull
+        public static PropertyConfig fromBundle(@NonNull Bundle propertyBundle) {
+            switch (propertyBundle.getInt(PropertyConfig.DATA_TYPE_FIELD)) {
+                case PropertyConfig.DATA_TYPE_STRING:
+                    return new StringPropertyConfig(propertyBundle);
+                case PropertyConfig.DATA_TYPE_INT64:
+                    return new Int64PropertyConfig(propertyBundle);
+                case PropertyConfig.DATA_TYPE_DOUBLE:
+                    return new DoublePropertyConfig(propertyBundle);
+                case PropertyConfig.DATA_TYPE_BOOLEAN:
+                    return new BooleanPropertyConfig(propertyBundle);
+                case PropertyConfig.DATA_TYPE_BYTES:
+                    return new BytesPropertyConfig(propertyBundle);
+                case PropertyConfig.DATA_TYPE_DOCUMENT:
+                    return new DocumentPropertyConfig(propertyBundle);
+                default:
+                    throw new IllegalArgumentException(
+                            "Unsupported property bundle of type "
+                                    + propertyBundle.getInt(PropertyConfig.DATA_TYPE_FIELD)
+                                    + "; contents: "
+                                    + propertyBundle);
+            }
+        }
+    }
+
+    /** Configuration for a property of type String in a Document. */
+    public static final class StringPropertyConfig extends PropertyConfig {
+        private static final String INDEXING_TYPE_FIELD = "indexingType";
+        private static final String TOKENIZER_TYPE_FIELD = "tokenizerType";
+
         /**
          * Encapsulates the configurations on how AppSearch should query/index these terms.
          *
@@ -296,14 +397,7 @@
         @Retention(RetentionPolicy.SOURCE)
         public @interface IndexingType {}
 
-        /**
-         * Content in this property will not be tokenized or indexed.
-         *
-         * <p>Useful if the data type is not made up of terms (e.g. {@link
-         * PropertyConfig#DATA_TYPE_DOCUMENT} or {@link PropertyConfig#DATA_TYPE_BYTES} type). None
-         * of the properties inside the nested property will be indexed regardless of the value of
-         * {@code indexingType} for the nested properties.
-         */
+        /** Content in this property will not be tokenized or indexed. */
         public static final int INDEXING_TYPE_NONE = 0;
 
         /**
@@ -338,54 +432,16 @@
         public @interface TokenizerType {}
 
         /**
-         * It is only valid for tokenizer_type to be 'NONE' if the data type is {@link
-         * PropertyConfig#DATA_TYPE_DOCUMENT}.
+         * It is only valid for tokenizer_type to be 'NONE' if {@link #getIndexingType} is {@link
+         * #INDEXING_TYPE_NONE}.
          */
         public static final int TOKENIZER_TYPE_NONE = 0;
 
         /** Tokenization for plain text. */
         public static final int TOKENIZER_TYPE_PLAIN = 1;
 
-        final Bundle mBundle;
-
-        @Nullable private Integer mHashCode;
-
-        PropertyConfig(@NonNull Bundle bundle) {
-            mBundle = Preconditions.checkNotNull(bundle);
-        }
-
-        @Override
-        public String toString() {
-            return mBundle.toString();
-        }
-
-        /** Returns the name of this property. */
-        @NonNull
-        public String getName() {
-            return mBundle.getString(NAME_FIELD, "");
-        }
-
-        /** Returns the type of data the property contains (e.g. string, int, bytes, etc). */
-        public @DataType int getDataType() {
-            return mBundle.getInt(DATA_TYPE_FIELD, -1);
-        }
-
-        /**
-         * Returns the logical schema-type of the contents of this property.
-         *
-         * <p>Only set when {@link #getDataType} is set to {@link #DATA_TYPE_DOCUMENT}. Otherwise,
-         * it is {@code null}.
-         */
-        @Nullable
-        public String getSchemaType() {
-            return mBundle.getString(SCHEMA_TYPE_FIELD);
-        }
-
-        /**
-         * Returns the cardinality of the property (whether it is optional, required or repeated).
-         */
-        public @Cardinality int getCardinality() {
-            return mBundle.getInt(CARDINALITY_FIELD, -1);
+        StringPropertyConfig(@NonNull Bundle bundle) {
+            super(bundle);
         }
 
         /** Returns how the property is indexed. */
@@ -398,75 +454,19 @@
             return mBundle.getInt(TOKENIZER_TYPE_FIELD);
         }
 
-        @Override
-        public boolean equals(@Nullable Object other) {
-            if (this == other) {
-                return true;
-            }
-            if (!(other instanceof PropertyConfig)) {
-                return false;
-            }
-            PropertyConfig otherProperty = (PropertyConfig) other;
-            return BundleUtil.deepEquals(this.mBundle, otherProperty.mBundle);
-        }
-
-        @Override
-        public int hashCode() {
-            if (mHashCode == null) {
-                mHashCode = BundleUtil.deepHashCode(mBundle);
-            }
-            return mHashCode;
-        }
-
         /**
-         * Builder for {@link PropertyConfig}.
+         * Builder for {@link StringPropertyConfig}.
          *
-         * <p>The following properties must be set, or {@link PropertyConfig} construction will
-         * fail:
-         *
-         * <ul>
-         *   <li>dataType
-         *   <li>cardinality
-         * </ul>
-         *
-         * <p>In addition, if {@code schemaType} is {@link #DATA_TYPE_DOCUMENT}, {@code schemaType}
-         * is also required.
+         * <p>{@link #setCardinality} must be called or {@link #build} will fail.
          */
         public static final class Builder {
             private final Bundle mBundle = new Bundle();
             private boolean mBuilt = false;
 
-            /** Creates a new {@link PropertyConfig.Builder}. */
+            /** Creates a new {@link StringPropertyConfig.Builder}. */
             public Builder(@NonNull String propertyName) {
                 mBundle.putString(NAME_FIELD, propertyName);
-            }
-
-            /**
-             * Type of data the property contains (e.g. string, int, bytes, etc).
-             *
-             * <p>This property must be set.
-             */
-            @NonNull
-            public PropertyConfig.Builder setDataType(@DataType int dataType) {
-                Preconditions.checkState(!mBuilt, "Builder has already been used");
-                Preconditions.checkArgumentInRange(
-                        dataType, DATA_TYPE_STRING, DATA_TYPE_DOCUMENT, "dataType");
-                mBundle.putInt(DATA_TYPE_FIELD, dataType);
-                return this;
-            }
-
-            /**
-             * The logical schema-type of the contents of this property.
-             *
-             * <p>Only required when {@link #setDataType} is set to {@link #DATA_TYPE_DOCUMENT}.
-             * Otherwise, it is ignored.
-             */
-            @NonNull
-            public PropertyConfig.Builder setSchemaType(@NonNull String schemaType) {
-                Preconditions.checkState(!mBuilt, "Builder has already been used");
-                Preconditions.checkNotNull(schemaType);
-                mBundle.putString(SCHEMA_TYPE_FIELD, schemaType);
-                return this;
+                mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_STRING);
             }
 
             /**
@@ -474,8 +474,9 @@
              *
              * <p>This property must be set.
              */
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
             @NonNull
-            public PropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
+            public StringPropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
                 Preconditions.checkState(!mBuilt, "Builder has already been used");
                 Preconditions.checkArgumentInRange(
                         cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
@@ -487,7 +488,7 @@
              * Configures how a property should be indexed so that it can be retrieved by queries.
              */
             @NonNull
-            public PropertyConfig.Builder setIndexingType(@IndexingType int indexingType) {
+            public StringPropertyConfig.Builder setIndexingType(@IndexingType int indexingType) {
                 Preconditions.checkState(!mBuilt, "Builder has already been used");
                 Preconditions.checkArgumentInRange(
                         indexingType, INDEXING_TYPE_NONE, INDEXING_TYPE_PREFIXES, "indexingType");
@@ -497,7 +498,7 @@
 
             /** Configures how this property should be tokenized (split into words). */
             @NonNull
-            public PropertyConfig.Builder setTokenizerType(@TokenizerType int tokenizerType) {
+            public StringPropertyConfig.Builder setTokenizerType(@TokenizerType int tokenizerType) {
                 Preconditions.checkState(!mBuilt, "Builder has already been used");
                 Preconditions.checkArgumentInRange(
                         tokenizerType, TOKENIZER_TYPE_NONE, TOKENIZER_TYPE_PLAIN, "tokenizerType");
@@ -506,6 +507,337 @@
             }
 
             /**
+             * Constructs a new {@link StringPropertyConfig} from the contents of this builder.
+             *
+             * <p>After calling this method, the builder must no longer be used.
+             *
+             * @throws IllegalSchemaException if the property is not correctly populated
+             */
+            @NonNull
+            public StringPropertyConfig build() {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                // TODO(b/147692920): Send the schema to Icing Lib for official validation, instead
+                //     of partially reimplementing some of the validation Icing does here.
+                if (!mBundle.containsKey(CARDINALITY_FIELD)) {
+                    throw new IllegalSchemaException("Missing field: cardinality");
+                }
+                mBuilt = true;
+                return new StringPropertyConfig(mBundle);
+            }
+        }
+    }
+
+    /** Configuration for a property containing a 64-bit integer. */
+    public static final class Int64PropertyConfig extends PropertyConfig {
+        Int64PropertyConfig(@NonNull Bundle bundle) {
+            super(bundle);
+        }
+
+        /**
+         * Builder for {@link Int64PropertyConfig}.
+         *
+         * <p>{@link #setCardinality} must be called or {@link #build} will fail.
+         */
+        public static final class Builder {
+            private final Bundle mBundle = new Bundle();
+            private boolean mBuilt = false;
+
+            /** Creates a new {@link Int64PropertyConfig.Builder}. */
+            public Builder(@NonNull String propertyName) {
+                mBundle.putString(NAME_FIELD, propertyName);
+                mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_INT64);
+            }
+
+            /**
+             * The cardinality of the property (whether it is optional, required or repeated).
+             *
+             * <p>This property must be set.
+             */
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public Int64PropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkArgumentInRange(
+                        cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
+                mBundle.putInt(CARDINALITY_FIELD, cardinality);
+                return this;
+            }
+
+            /**
+             * Constructs a new {@link Int64PropertyConfig} from the contents of this builder.
+             *
+             * <p>After calling this method, the builder must no longer be used.
+             *
+             * @throws IllegalSchemaException if the property is not correctly populated
+             */
+            @NonNull
+            public Int64PropertyConfig build() {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                if (!mBundle.containsKey(CARDINALITY_FIELD)) {
+                    throw new IllegalSchemaException("Missing field: cardinality");
+                }
+                mBuilt = true;
+                return new Int64PropertyConfig(mBundle);
+            }
+        }
+    }
+
+    /** Configuration for a property containing a double-precision decimal number. */
+    public static final class DoublePropertyConfig extends PropertyConfig {
+        DoublePropertyConfig(@NonNull Bundle bundle) {
+            super(bundle);
+        }
+
+        /**
+         * Builder for {@link DoublePropertyConfig}.
+         *
+         * <p>{@link #setCardinality} must be called or {@link #build} will fail.
+         */
+        public static final class Builder {
+            private final Bundle mBundle = new Bundle();
+            private boolean mBuilt = false;
+
+            /** Creates a new {@link DoublePropertyConfig.Builder}. */
+            public Builder(@NonNull String propertyName) {
+                mBundle.putString(NAME_FIELD, propertyName);
+                mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_DOUBLE);
+            }
+
+            /**
+             * The cardinality of the property (whether it is optional, required or repeated).
+             *
+             * <p>This property must be set.
+             */
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public DoublePropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkArgumentInRange(
+                        cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
+                mBundle.putInt(CARDINALITY_FIELD, cardinality);
+                return this;
+            }
+
+            /**
+             * Constructs a new {@link DoublePropertyConfig} from the contents of this builder.
+             *
+             * <p>After calling this method, the builder must no longer be used.
+             *
+             * @throws IllegalSchemaException if the property is not correctly populated
+             */
+            @NonNull
+            public DoublePropertyConfig build() {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                if (!mBundle.containsKey(CARDINALITY_FIELD)) {
+                    throw new IllegalSchemaException("Missing field: cardinality");
+                }
+                mBuilt = true;
+                return new DoublePropertyConfig(mBundle);
+            }
+        }
+    }
+
+    /** Configuration for a property containing a boolean. */
+    public static final class BooleanPropertyConfig extends PropertyConfig {
+        BooleanPropertyConfig(@NonNull Bundle bundle) {
+            super(bundle);
+        }
+
+        /**
+         * Builder for {@link BooleanPropertyConfig}.
+         *
+         * <p>{@link #setCardinality} must be called or {@link #build} will fail.
+         */
+        public static final class Builder {
+            private final Bundle mBundle = new Bundle();
+            private boolean mBuilt = false;
+
+            /** Creates a new {@link BooleanPropertyConfig.Builder}. */
+            public Builder(@NonNull String propertyName) {
+                mBundle.putString(NAME_FIELD, propertyName);
+                mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_BOOLEAN);
+            }
+
+            /**
+             * The cardinality of the property (whether it is optional, required or repeated).
+             *
+             * <p>This property must be set.
+             */
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public BooleanPropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkArgumentInRange(
+                        cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
+                mBundle.putInt(CARDINALITY_FIELD, cardinality);
+                return this;
+            }
+
+            /**
+             * Constructs a new {@link BooleanPropertyConfig} from the contents of this builder.
+             *
+             * <p>After calling this method, the builder must no longer be used.
+             *
+             * @throws IllegalSchemaException if the property is not correctly populated
+             */
+            @NonNull
+            public BooleanPropertyConfig build() {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                if (!mBundle.containsKey(CARDINALITY_FIELD)) {
+                    throw new IllegalSchemaException("Missing field: cardinality");
+                }
+                mBuilt = true;
+                return new BooleanPropertyConfig(mBundle);
+            }
+        }
+    }
+
+    /** Configuration for a property containing a byte array. */
+    public static final class BytesPropertyConfig extends PropertyConfig {
+        BytesPropertyConfig(@NonNull Bundle bundle) {
+            super(bundle);
+        }
+
+        /**
+         * Builder for {@link BytesPropertyConfig}.
+         *
+         * <p>{@link #setCardinality} must be called or {@link #build} will fail.
+         */
+        public static final class Builder {
+            private final Bundle mBundle = new Bundle();
+            private boolean mBuilt = false;
+
+            /** Creates a new {@link BytesPropertyConfig.Builder}. */
+            public Builder(@NonNull String propertyName) {
+                mBundle.putString(NAME_FIELD, propertyName);
+                mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_BYTES);
+            }
+
+            /**
+             * The cardinality of the property (whether it is optional, required or repeated).
+             *
+             * <p>This property must be set.
+             */
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public BytesPropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkArgumentInRange(
+                        cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
+                mBundle.putInt(CARDINALITY_FIELD, cardinality);
+                return this;
+            }
+
+            /**
+             * Constructs a new {@link BytesPropertyConfig} from the contents of this builder.
+             *
+             * <p>After calling this method, the builder must no longer be used.
+             *
+             * @throws IllegalSchemaException if the property is not correctly populated
+             */
+            @NonNull
+            public BytesPropertyConfig build() {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                if (!mBundle.containsKey(CARDINALITY_FIELD)) {
+                    throw new IllegalSchemaException("Missing field: cardinality");
+                }
+                mBuilt = true;
+                return new BytesPropertyConfig(mBundle);
+            }
+        }
+    }
+
+    /** Configuration for a property containing another Document. */
+    public static final class DocumentPropertyConfig extends PropertyConfig {
+        private static final String SCHEMA_TYPE_FIELD = "schemaType";
+        private static final String INDEX_NESTED_PROPERTIES_FIELD = "indexNestedProperties";
+
+        DocumentPropertyConfig(@NonNull Bundle bundle) {
+            super(bundle);
+        }
+
+        /** Returns the logical schema-type of the contents of this document property. */
+        @NonNull
+        public String getSchemaType() {
+            return Preconditions.checkNotNull(mBundle.getString(SCHEMA_TYPE_FIELD));
+        }
+
+        /**
+         * Returns whether fields in the nested document should be indexed according to that
+         * document's schema.
+         *
+         * <p>If false, the nested document's properties are not indexed regardless of its own
+         * schema.
+         */
+        public boolean isIndexNestedProperties() {
+            return mBundle.getBoolean(INDEX_NESTED_PROPERTIES_FIELD);
+        }
+
+        /**
+         * Builder for {@link DocumentPropertyConfig}.
+         *
+         * <p>The following properties must be set, or {@link DocumentPropertyConfig} construction
+         * will fail:
+         *
+         * <ul>
+         *   <li>cardinality
+         *   <li>schemaType
+         * </ul>
+         */
+        public static final class Builder {
+            private final Bundle mBundle = new Bundle();
+            private boolean mBuilt = false;
+
+            /** Creates a new {@link DocumentPropertyConfig.Builder}. */
+            public Builder(@NonNull String propertyName) {
+                mBundle.putString(NAME_FIELD, propertyName);
+                mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_DOCUMENT);
+            }
+
+            /**
+             * The logical schema-type of the contents of this property.
+             *
+             * <p>This property must be set.
+             */
+            @NonNull
+            public DocumentPropertyConfig.Builder setSchemaType(@NonNull String schemaType) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkNotNull(schemaType);
+                mBundle.putString(SCHEMA_TYPE_FIELD, schemaType);
+                return this;
+            }
+
+            /**
+             * The cardinality of the property (whether it is optional, required or repeated).
+             *
+             * <p>This property must be set.
+             */
+            @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass
+            @NonNull
+            public DocumentPropertyConfig.Builder setCardinality(@Cardinality int cardinality) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                Preconditions.checkArgumentInRange(
+                        cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality");
+                mBundle.putInt(CARDINALITY_FIELD, cardinality);
+                return this;
+            }
+
+            /**
+             * Configures whether fields in the nested document should be indexed according to that
+             * document's schema.
+             *
+             * <p>If false, the nested document's properties are not indexed regardless of its own
+             * schema.
+             */
+            @NonNull
+            public DocumentPropertyConfig.Builder setIndexNestedProperties(
+                    boolean indexNestedProperties) {
+                Preconditions.checkState(!mBuilt, "Builder has already been used");
+                mBundle.putBoolean(INDEX_NESTED_PROPERTIES_FIELD, indexNestedProperties);
+                return this;
+            }
+
+            /**
              * Constructs a new {@link PropertyConfig} from the contents of this builder.
              *
              * <p>After calling this method, the builder must no longer be used.
@@ -514,24 +846,16 @@
              *     missing {@code dataType}).
              */
             @NonNull
-            public PropertyConfig build() {
+            public DocumentPropertyConfig build() {
                 Preconditions.checkState(!mBuilt, "Builder has already been used");
-                // TODO(b/147692920): Send the schema to Icing Lib for official validation, instead
-                //     of partially reimplementing some of the validation Icing does here.
-                if (!mBundle.containsKey(DATA_TYPE_FIELD)) {
-                    throw new IllegalSchemaException("Missing field: dataType");
-                }
-                if (mBundle.getString(SCHEMA_TYPE_FIELD, "").isEmpty()
-                        && mBundle.getInt(DATA_TYPE_FIELD) == DATA_TYPE_DOCUMENT) {
-                    throw new IllegalSchemaException(
-                            "Missing field: schemaType (required for configs with "
-                                    + "dataType = DOCUMENT)");
+                if (mBundle.getString(SCHEMA_TYPE_FIELD, "").isEmpty()) {
+                    throw new IllegalSchemaException("Missing field: schemaType");
                 }
                 if (!mBundle.containsKey(CARDINALITY_FIELD)) {
                     throw new IllegalSchemaException("Missing field: cardinality");
                 }
                 mBuilt = true;
-                return new PropertyConfig(mBundle);
+                return new DocumentPropertyConfig(mBundle);
             }
         }
     }
diff --git a/framework/java/external/android/app/appsearch/GenericDocument.java b/framework/java/external/android/app/appsearch/GenericDocument.java
index 11e7fab..2f02808 100644
--- a/framework/java/external/android/app/appsearch/GenericDocument.java
+++ b/framework/java/external/android/app/appsearch/GenericDocument.java
@@ -80,9 +80,9 @@
     /**
      * The maximum number of indexed properties a document can have.
      *
-     * <p>Indexed properties are properties where the {@link
-     * AppSearchSchema.PropertyConfig#getIndexingType()} constant is anything other than {@link
-     * AppSearchSchema.PropertyConfig.IndexingType#INDEXING_TYPE_NONE}.
+     * <p>Indexed properties are properties which are strings where the {@link
+     * AppSearchSchema.StringPropertyConfig#getIndexingType} value is anything other than {@link
+     * AppSearchSchema.StringPropertyConfig.IndexingType#INDEXING_TYPE_NONE}.
      */
     public static int getMaxIndexedProperties() {
         return MAX_INDEXED_PROPERTIES;
diff --git a/framework/java/external/android/app/appsearch/GetByUriRequest.java b/framework/java/external/android/app/appsearch/GetByUriRequest.java
index 74afdd2..38e0046 100644
--- a/framework/java/external/android/app/appsearch/GetByUriRequest.java
+++ b/framework/java/external/android/app/appsearch/GetByUriRequest.java
@@ -17,13 +17,17 @@
 package android.app.appsearch;
 
 import android.annotation.NonNull;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 
 import com.android.internal.util.Preconditions;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -32,12 +36,24 @@
  * @see AppSearchSession#getByUri
  */
 public final class GetByUriRequest {
+    /**
+     * Schema type to be used in {@link android.app.appsearch.GetByUriRequest.Builder#addProjection}
+     * to apply property paths to all results, excepting any types that have had their own, specific
+     * property paths set.
+     */
+    public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
+
     private final String mNamespace;
     private final Set<String> mUris;
+    private final Map<String, List<String>> mTypePropertyPathsMap;
 
-    GetByUriRequest(@NonNull String namespace, @NonNull Set<String> uris) {
-        mNamespace = namespace;
-        mUris = uris;
+    GetByUriRequest(
+            @NonNull String namespace,
+            @NonNull Set<String> uris,
+            @NonNull Map<String, List<String>> typePropertyPathsMap) {
+        mNamespace = Preconditions.checkNotNull(namespace);
+        mUris = Preconditions.checkNotNull(uris);
+        mTypePropertyPathsMap = Preconditions.checkNotNull(typePropertyPathsMap);
     }
 
     /** Returns the namespace to get documents from. */
@@ -52,10 +68,43 @@
         return Collections.unmodifiableSet(mUris);
     }
 
+    /**
+     * Returns a map from schema type to property paths to be used for projection.
+     *
+     * <p>If the map is empty, then all properties will be retrieved for all results.
+     *
+     * <p>Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this
+     * function, rather than calling it multiple times.
+     */
+    @NonNull
+    public Map<String, List<String>> getProjections() {
+        Map<String, List<String>> copy = new ArrayMap<>();
+        for (String key : mTypePropertyPathsMap.keySet()) {
+            copy.put(key, new ArrayList<>(mTypePropertyPathsMap.get(key)));
+        }
+        return copy;
+    }
+
+    /**
+     * Returns a map from schema type to property paths to be used for projection.
+     *
+     * <p>If the map is empty, then all properties will be retrieved for all results.
+     *
+     * <p>A more efficient version of {@link #getProjections}, but it returns a modifiable map. This
+     * is not meant to be unhidden and should only be used by internal classes.
+     *
+     * @hide
+     */
+    @NonNull
+    public Map<String, List<String>> getProjectionsVisibleToPackagesInternal() {
+        return mTypePropertyPathsMap;
+    }
+
     /** Builder for {@link GetByUriRequest} objects. */
     public static final class Builder {
         private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
         private final Set<String> mUris = new ArraySet<>();
+        private final Map<String, List<String>> mProjectionTypePropertyPaths = new ArrayMap<>();
         private boolean mBuilt = false;
 
         /**
@@ -87,12 +136,63 @@
             return this;
         }
 
+        /**
+         * Adds property paths for the specified type to be used for projection. If property paths
+         * are added for a type, then only the properties referred to will be retrieved for results
+         * of that type. If a property path that is specified isn't present in a result, it will be
+         * ignored for that result. Property paths cannot be null.
+         *
+         * <p>If no property paths are added for a particular type, then all properties of results
+         * of that type will be retrieved.
+         *
+         * <p>If property path is added for the {@link
+         * GetByUriRequest#PROJECTION_SCHEMA_TYPE_WILDCARD}, then those property paths will apply to
+         * all results, excepting any types that have their own, specific property paths set.
+         *
+         * <p>{@see SearchSpec.Builder#addProjection(String, String...)}
+         */
+        @NonNull
+        public Builder addProjection(@NonNull String schemaType, @NonNull String... propertyPaths) {
+            Preconditions.checkNotNull(propertyPaths);
+            return addProjection(schemaType, Arrays.asList(propertyPaths));
+        }
+
+        /**
+         * Adds property paths for the specified type to be used for projection. If property paths
+         * are added for a type, then only the properties referred to will be retrieved for results
+         * of that type. If a property path that is specified isn't present in a result, it will be
+         * ignored for that result. Property paths cannot be null.
+         *
+         * <p>If no property paths are added for a particular type, then all properties of results
+         * of that type will be retrieved.
+         *
+         * <p>If property path is added for the {@link
+         * GetByUriRequest#PROJECTION_SCHEMA_TYPE_WILDCARD}, then those property paths will apply to
+         * all results, excepting any types that have their own, specific property paths set.
+         *
+         * <p>{@see SearchSpec.Builder#addProjection(String, String...)}
+         */
+        @NonNull
+        public Builder addProjection(
+                @NonNull String schemaType, @NonNull Collection<String> propertyPaths) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(schemaType);
+            Preconditions.checkNotNull(propertyPaths);
+            List<String> propertyPathsList = new ArrayList<>(propertyPaths.size());
+            for (String propertyPath : propertyPaths) {
+                Preconditions.checkNotNull(propertyPath);
+                propertyPathsList.add(propertyPath);
+            }
+            mProjectionTypePropertyPaths.put(schemaType, propertyPathsList);
+            return this;
+        }
+
         /** Builds a new {@link GetByUriRequest}. */
         @NonNull
         public GetByUriRequest build() {
             Preconditions.checkState(!mBuilt, "Builder has already been used");
             mBuilt = true;
-            return new GetByUriRequest(mNamespace, mUris);
+            return new GetByUriRequest(mNamespace, mUris, mProjectionTypePropertyPaths);
         }
     }
 }
diff --git a/service/java/com/android/server/appsearch/AppSearchManagerService.java b/service/java/com/android/server/appsearch/AppSearchManagerService.java
index cfee7a4..ec41353 100644
--- a/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -190,6 +190,7 @@
                 @NonNull String databaseName,
                 @NonNull String namespace,
                 @NonNull List<String> uris,
+                @NonNull Map<String, List<String>> typePropertyPaths,
                 @UserIdInt int userId,
                 @NonNull IAppSearchBatchResultCallback callback) {
             Preconditions.checkNotNull(packageName);
@@ -208,8 +209,8 @@
                 for (int i = 0; i < uris.size(); i++) {
                     String uri = uris.get(i);
                     try {
-                        GenericDocument document =
-                                impl.getDocument(packageName, databaseName, namespace, uri);
+                        GenericDocument document = impl.getDocument(packageName, databaseName,
+                                namespace, uri, typePropertyPaths);
                         resultBuilder.setSuccess(uri, document.getBundle());
                     } catch (Throwable t) {
                         resultBuilder.setResult(uri, throwableToFailedResult(t));
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 1581847..1665b1c 100644
--- a/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -21,6 +21,7 @@
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.AppSearchSchema;
 import android.app.appsearch.GenericDocument;
+import android.app.appsearch.GetByUriRequest;
 import android.app.appsearch.PackageIdentifier;
 import android.app.appsearch.SearchResultPage;
 import android.app.appsearch.SearchSpec;
@@ -38,6 +39,7 @@
 import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter;
 import com.android.server.appsearch.external.localstorage.converter.SearchResultToProtoConverter;
 import com.android.server.appsearch.external.localstorage.converter.SearchSpecToProtoConverter;
+import com.android.server.appsearch.external.localstorage.converter.TypePropertyPathToProtoConverter;
 
 import com.google.android.icing.IcingSearchEngine;
 import com.google.android.icing.proto.DeleteByQueryResultProto;
@@ -428,6 +430,8 @@
      * @param databaseName The databaseName this document resides in.
      * @param namespace The namespace this document resides in.
      * @param uri The URI of the document to get.
+     * @param typePropertyPaths A map of schema type to a list of property paths to return in the
+     *     result.
      * @return The Document contents
      * @throws AppSearchException on IcingSearchEngine error.
      */
@@ -436,16 +440,35 @@
             @NonNull String packageName,
             @NonNull String databaseName,
             @NonNull String namespace,
-            @NonNull String uri)
+            @NonNull String uri,
+            @NonNull Map<String, List<String>> typePropertyPaths)
             throws AppSearchException {
         GetResultProto getResultProto;
+        List<TypePropertyMask> nonPrefixedPropertyMasks =
+                TypePropertyPathToProtoConverter.toTypePropertyMaskList(typePropertyPaths);
+        List<TypePropertyMask> prefixedPropertyMasks =
+                new ArrayList<>(nonPrefixedPropertyMasks.size());
+        for (int i = 0; i < nonPrefixedPropertyMasks.size(); ++i) {
+            TypePropertyMask typePropertyMask = nonPrefixedPropertyMasks.get(i);
+            String nonPrefixedType = typePropertyMask.getSchemaType();
+            String prefixedType =
+                    nonPrefixedType.equals(GetByUriRequest.PROJECTION_SCHEMA_TYPE_WILDCARD)
+                            ? nonPrefixedType
+                            : createPrefix(packageName, databaseName) + nonPrefixedType;
+            prefixedPropertyMasks.add(
+                    typePropertyMask.toBuilder().setSchemaType(prefixedType).build());
+        }
+        GetResultSpecProto getResultSpec =
+                GetResultSpecProto.newBuilder()
+                        .addAllTypePropertyMasks(prefixedPropertyMasks)
+                        .build();
         mReadWriteLock.readLock().lock();
         try {
             getResultProto =
                     mIcingSearchEngineLocked.get(
                             createPrefix(packageName, databaseName) + namespace,
                             uri,
-                            GetResultSpecProto.getDefaultInstance());
+                            getResultSpec);
         } finally {
             mReadWriteLock.readLock().unlock();
         }
diff --git a/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java b/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
index 1c86cd6..24d64a0 100644
--- a/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
+++ b/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java
@@ -85,15 +85,14 @@
     private static final AppSearchSchema VISIBILITY_SCHEMA =
             new AppSearchSchema.Builder(VISIBILITY_TYPE)
                     .addProperty(
-                            new AppSearchSchema.PropertyConfig.Builder(
+                            new AppSearchSchema.StringPropertyConfig.Builder(
                                             NOT_PLATFORM_SURFACEABLE_PROPERTY)
-                                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
                                     .setCardinality(
                                             AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
                                     .build())
                     .addProperty(
-                            new AppSearchSchema.PropertyConfig.Builder(PACKAGE_ACCESSIBLE_PROPERTY)
-                                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT)
+                            new AppSearchSchema.DocumentPropertyConfig.Builder(
+                                            PACKAGE_ACCESSIBLE_PROPERTY)
                                     .setSchemaType(PACKAGE_ACCESSIBLE_TYPE)
                                     .setCardinality(
                                             AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
@@ -107,22 +106,20 @@
     private static final AppSearchSchema PACKAGE_ACCESSIBLE_SCHEMA =
             new AppSearchSchema.Builder(PACKAGE_ACCESSIBLE_TYPE)
                     .addProperty(
-                            new AppSearchSchema.PropertyConfig.Builder(PACKAGE_NAME_PROPERTY)
+                            new AppSearchSchema.StringPropertyConfig.Builder(PACKAGE_NAME_PROPERTY)
                                     .setCardinality(
                                             AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
                                     .build())
                     .addProperty(
-                            new AppSearchSchema.PropertyConfig.Builder(SHA_256_CERT_PROPERTY)
+                            new AppSearchSchema.BytesPropertyConfig.Builder(SHA_256_CERT_PROPERTY)
                                     .setCardinality(
                                             AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
                                     .build())
                     .addProperty(
-                            new AppSearchSchema.PropertyConfig.Builder(ACCESSIBLE_SCHEMA_PROPERTY)
+                            new AppSearchSchema.StringPropertyConfig.Builder(
+                                            ACCESSIBLE_SCHEMA_PROPERTY)
                                     .setCardinality(
                                             AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
                                     .build())
                     .build();
 
@@ -238,7 +235,8 @@
                                 PACKAGE_NAME,
                                 DATABASE_NAME,
                                 NAMESPACE,
-                                /*uri=*/ addUriPrefix(prefix));
+                                /*uri=*/ addUriPrefix(prefix),
+                                /*typePropertyPaths=*/ Collections.emptyMap());
 
                 // Update platform visibility settings
                 String[] schemas =
diff --git a/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java b/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
index 69bf3d7..ce1c9f4 100644
--- a/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
+++ b/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java
@@ -22,6 +22,7 @@
 
 import com.android.internal.util.Preconditions;
 
+import com.google.android.icing.proto.DocumentIndexingConfig;
 import com.google.android.icing.proto.PropertyConfigProto;
 import com.google.android.icing.proto.SchemaTypeConfigProto;
 import com.google.android.icing.proto.SchemaTypeConfigProtoOrBuilder;
@@ -65,7 +66,6 @@
         Preconditions.checkNotNull(property);
         PropertyConfigProto.Builder builder =
                 PropertyConfigProto.newBuilder().setPropertyName(property.getName());
-        StringIndexingConfig.Builder indexingConfig = StringIndexingConfig.newBuilder();
 
         // Set dataType
         @AppSearchSchema.PropertyConfig.DataType int dataType = property.getDataType();
@@ -76,12 +76,6 @@
         }
         builder.setDataType(dataTypeProto);
 
-        // Set schemaType
-        String schemaType = property.getSchemaType();
-        if (schemaType != null) {
-            builder.setSchemaType(schemaType);
-        }
-
         // Set cardinality
         @AppSearchSchema.PropertyConfig.Cardinality int cardinality = property.getCardinality();
         PropertyConfigProto.Cardinality.Code cardinalityProto =
@@ -91,36 +85,27 @@
         }
         builder.setCardinality(cardinalityProto);
 
-        // Set indexingType
-        @AppSearchSchema.PropertyConfig.IndexingType int indexingType = property.getIndexingType();
-        TermMatchType.Code termMatchTypeProto;
-        switch (indexingType) {
-            case AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE:
-                termMatchTypeProto = TermMatchType.Code.UNKNOWN;
-                break;
-            case AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS:
-                termMatchTypeProto = TermMatchType.Code.EXACT_ONLY;
-                break;
-            case AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES:
-                termMatchTypeProto = TermMatchType.Code.PREFIX;
-                break;
-            default:
-                throw new IllegalArgumentException("Invalid indexingType: " + indexingType);
-        }
-        indexingConfig.setTermMatchType(termMatchTypeProto);
+        if (property instanceof AppSearchSchema.StringPropertyConfig) {
+            AppSearchSchema.StringPropertyConfig stringProperty =
+                    (AppSearchSchema.StringPropertyConfig) property;
+            StringIndexingConfig stringIndexingConfig =
+                    StringIndexingConfig.newBuilder()
+                            .setTermMatchType(
+                                    convertTermMatchTypeToProto(stringProperty.getIndexingType()))
+                            .setTokenizerType(
+                                    convertTokenizerTypeToProto(stringProperty.getTokenizerType()))
+                            .build();
+            builder.setStringIndexingConfig(stringIndexingConfig);
 
-        // Set tokenizerType
-        @AppSearchSchema.PropertyConfig.TokenizerType
-        int tokenizerType = property.getTokenizerType();
-        StringIndexingConfig.TokenizerType.Code tokenizerTypeProto =
-                StringIndexingConfig.TokenizerType.Code.forNumber(tokenizerType);
-        if (tokenizerTypeProto == null) {
-            throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType);
+        } else if (property instanceof AppSearchSchema.DocumentPropertyConfig) {
+            AppSearchSchema.DocumentPropertyConfig documentProperty =
+                    (AppSearchSchema.DocumentPropertyConfig) property;
+            builder.setSchemaType(documentProperty.getSchemaType())
+                    .setDocumentIndexingConfig(
+                            DocumentIndexingConfig.newBuilder()
+                                    .setIndexNestedProperties(
+                                            documentProperty.isIndexNestedProperties()));
         }
-        indexingConfig.setTokenizerType(tokenizerTypeProto);
-
-        // Build!
-        builder.setStringIndexingConfig(indexingConfig);
         return builder.build();
     }
 
@@ -145,39 +130,99 @@
     private static AppSearchSchema.PropertyConfig toPropertyConfig(
             @NonNull PropertyConfigProto proto) {
         Preconditions.checkNotNull(proto);
-        AppSearchSchema.PropertyConfig.Builder builder =
-                new AppSearchSchema.PropertyConfig.Builder(proto.getPropertyName())
-                        .setDataType(proto.getDataType().getNumber())
+        switch (proto.getDataType()) {
+            case STRING:
+                return toStringPropertyConfig(proto);
+            case INT64:
+                return new AppSearchSchema.Int64PropertyConfig.Builder(proto.getPropertyName())
+                        .setCardinality(proto.getCardinality().getNumber())
+                        .build();
+            case DOUBLE:
+                return new AppSearchSchema.DoublePropertyConfig.Builder(proto.getPropertyName())
+                        .setCardinality(proto.getCardinality().getNumber())
+                        .build();
+            case BOOLEAN:
+                return new AppSearchSchema.BooleanPropertyConfig.Builder(proto.getPropertyName())
+                        .setCardinality(proto.getCardinality().getNumber())
+                        .build();
+            case BYTES:
+                return new AppSearchSchema.BytesPropertyConfig.Builder(proto.getPropertyName())
+                        .setCardinality(proto.getCardinality().getNumber())
+                        .build();
+            case DOCUMENT:
+                return toDocumentPropertyConfig(proto);
+            default:
+                throw new IllegalArgumentException("Invalid dataType: " + proto.getDataType());
+        }
+    }
+
+    @NonNull
+    private static AppSearchSchema.StringPropertyConfig toStringPropertyConfig(
+            @NonNull PropertyConfigProto proto) {
+        AppSearchSchema.StringPropertyConfig.Builder builder =
+                new AppSearchSchema.StringPropertyConfig.Builder(proto.getPropertyName())
                         .setCardinality(proto.getCardinality().getNumber())
                         .setTokenizerType(
                                 proto.getStringIndexingConfig().getTokenizerType().getNumber());
 
-        // Set schema
-        if (!proto.getSchemaType().isEmpty()) {
-            builder.setSchemaType(proto.getSchemaType());
-        }
-
         // Set indexingType
-        @AppSearchSchema.PropertyConfig.IndexingType int indexingType;
         TermMatchType.Code termMatchTypeProto = proto.getStringIndexingConfig().getTermMatchType();
-        switch (termMatchTypeProto) {
+        builder.setIndexingType(convertTermMatchTypeFromProto(termMatchTypeProto));
+
+        return builder.build();
+    }
+
+    @NonNull
+    private static AppSearchSchema.DocumentPropertyConfig toDocumentPropertyConfig(
+            @NonNull PropertyConfigProto proto) {
+        return new AppSearchSchema.DocumentPropertyConfig.Builder(proto.getPropertyName())
+                .setCardinality(proto.getCardinality().getNumber())
+                .setSchemaType(proto.getSchemaType())
+                .setIndexNestedProperties(
+                        proto.getDocumentIndexingConfig().getIndexNestedProperties())
+                .build();
+    }
+
+    @NonNull
+    private static TermMatchType.Code convertTermMatchTypeToProto(
+            @AppSearchSchema.StringPropertyConfig.IndexingType int indexingType) {
+        switch (indexingType) {
+            case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE:
+                return TermMatchType.Code.UNKNOWN;
+            case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS:
+                return TermMatchType.Code.EXACT_ONLY;
+            case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES:
+                return TermMatchType.Code.PREFIX;
+            default:
+                throw new IllegalArgumentException("Invalid indexingType: " + indexingType);
+        }
+    }
+
+    @AppSearchSchema.StringPropertyConfig.IndexingType
+    private static int convertTermMatchTypeFromProto(@NonNull TermMatchType.Code termMatchType) {
+        switch (termMatchType) {
             case UNKNOWN:
-                indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE;
-                break;
+                return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE;
             case EXACT_ONLY:
-                indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS;
-                break;
+                return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS;
             case PREFIX:
-                indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES;
-                break;
+                return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES;
             default:
                 // Avoid crashing in the 'read' path; we should try to interpret the document to the
                 // extent possible.
-                Log.w(TAG, "Invalid indexingType: " + termMatchTypeProto.getNumber());
-                indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE;
+                Log.w(TAG, "Invalid indexingType: " + termMatchType.getNumber());
+                return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE;
         }
-        builder.setIndexingType(indexingType);
+    }
 
-        return builder.build();
+    @NonNull
+    private static StringIndexingConfig.TokenizerType.Code convertTokenizerTypeToProto(
+            @AppSearchSchema.StringPropertyConfig.TokenizerType int tokenizerType) {
+        StringIndexingConfig.TokenizerType.Code tokenizerTypeProto =
+                StringIndexingConfig.TokenizerType.Code.forNumber(tokenizerType);
+        if (tokenizerTypeProto == null) {
+            throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType);
+        }
+        return tokenizerTypeProto;
     }
 }
diff --git a/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java b/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
index 417ea24..07d50ae 100644
--- a/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
+++ b/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java
@@ -25,10 +25,6 @@
 import com.google.android.icing.proto.ScoringSpecProto;
 import com.google.android.icing.proto.SearchSpecProto;
 import com.google.android.icing.proto.TermMatchType;
-import com.google.android.icing.proto.TypePropertyMask;
-
-import java.util.List;
-import java.util.Map;
 
 /**
  * Translates a {@link SearchSpec} into icing search protos.
@@ -61,22 +57,17 @@
     @NonNull
     public static ResultSpecProto toResultSpecProto(@NonNull SearchSpec spec) {
         Preconditions.checkNotNull(spec);
-        ResultSpecProto.Builder builder =
-                ResultSpecProto.newBuilder()
-                        .setNumPerPage(spec.getResultCountPerPage())
-                        .setSnippetSpec(
-                                ResultSpecProto.SnippetSpecProto.newBuilder()
-                                        .setNumToSnippet(spec.getSnippetCount())
-                                        .setNumMatchesPerProperty(spec.getSnippetCountPerProperty())
-                                        .setMaxWindowBytes(spec.getMaxSnippetSize()));
-        Map<String, List<String>> projectionTypePropertyPaths = spec.getProjections();
-        for (Map.Entry<String, List<String>> e : projectionTypePropertyPaths.entrySet()) {
-            builder.addTypePropertyMasks(
-                    TypePropertyMask.newBuilder()
-                            .setSchemaType(e.getKey())
-                            .addAllPaths(e.getValue()));
-        }
-        return builder.build();
+        return ResultSpecProto.newBuilder()
+                .setNumPerPage(spec.getResultCountPerPage())
+                .setSnippetSpec(
+                        ResultSpecProto.SnippetSpecProto.newBuilder()
+                                .setNumToSnippet(spec.getSnippetCount())
+                                .setNumMatchesPerProperty(spec.getSnippetCountPerProperty())
+                                .setMaxWindowBytes(spec.getMaxSnippetSize()))
+                .addAllTypePropertyMasks(
+                        TypePropertyPathToProtoConverter.toTypePropertyMaskList(
+                                spec.getProjections()))
+                .build();
     }
 
     /** Extracts {@link ScoringSpecProto} information from a {@link SearchSpec}. */
diff --git a/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java b/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java
new file mode 100644
index 0000000..6f6dad2
--- /dev/null
+++ b/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021 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 com.android.server.appsearch.external.localstorage.converter;
+
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import com.google.android.icing.proto.TypePropertyMask;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Translates a <code>Map<String, List<String>></code> into <code>List<TypePropertyMask></code>.
+ *
+ * @hide
+ */
+public final class TypePropertyPathToProtoConverter {
+    private TypePropertyPathToProtoConverter() {}
+
+    /** Extracts {@link TypePropertyMask} information from a {@link Map}. */
+    @NonNull
+    public static List<TypePropertyMask> toTypePropertyMaskList(
+            @NonNull Map<String, List<String>> typePropertyPaths) {
+        Preconditions.checkNotNull(typePropertyPaths);
+        List<TypePropertyMask> typePropertyMasks = new ArrayList<>(typePropertyPaths.size());
+        for (Map.Entry<String, List<String>> e : typePropertyPaths.entrySet()) {
+            typePropertyMasks.add(
+                    TypePropertyMask.newBuilder()
+                            .setSchemaType(e.getKey())
+                            .addAllPaths(e.getValue())
+                            .build());
+        }
+        return typePropertyMasks;
+    }
+}
diff --git a/synced_jetpack_changeid.txt b/synced_jetpack_changeid.txt
index bca9424..5ab3450 100644
--- a/synced_jetpack_changeid.txt
+++ b/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I87d10a6c5f7b8e22a633fa910385061fbca0db24
+Iff96eae150c7cdd281c9ecb5d93f4ef697e89f1a
diff --git a/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java b/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
index 459fd15..13858a3 100644
--- a/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
+++ b/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
@@ -28,6 +28,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Future;
 
 public class AppSearchTestUtils {
@@ -59,6 +60,20 @@
         return list;
     }
 
+    public static List<GenericDocument> doGet(AppSearchSessionShim session, GetByUriRequest request)
+            throws Exception {
+        AppSearchBatchResult<String, GenericDocument> result =
+                checkIsBatchResultSuccess(session.getByUri(request));
+        Set<String> uris = request.getUris();
+        assertThat(result.getSuccesses()).hasSize(uris.size());
+        assertThat(result.getFailures()).isEmpty();
+        List<GenericDocument> list = new ArrayList<>(uris.size());
+        for (String uri : uris) {
+            list.add(result.getSuccesses().get(uri));
+        }
+        return list;
+    }
+
     public static List<GenericDocument> convertSearchResultsToDocuments(
             SearchResultsShim searchResults) throws Exception {
         List<SearchResult> results = searchResults.getNextPage().get();
diff --git a/testing/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java b/testing/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
index 7fcf877..dc225f1 100644
--- a/testing/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
+++ b/testing/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java
@@ -34,28 +34,26 @@
                 new AppSearchSchema.Builder("Email")
                         .setVersion(12345)
                         .addProperty(
-                                new AppSearchSchema.PropertyConfig.Builder("subject")
-                                        .setDataType(
-                                                AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                                new AppSearchSchema.StringPropertyConfig.Builder("subject")
                                         .setCardinality(
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
                                         .setIndexingType(
-                                                AppSearchSchema.PropertyConfig
+                                                AppSearchSchema.StringPropertyConfig
                                                         .INDEXING_TYPE_PREFIXES)
                                         .setTokenizerType(
-                                                AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .TOKENIZER_TYPE_PLAIN)
                                         .build())
                         .addProperty(
-                                new AppSearchSchema.PropertyConfig.Builder("body")
-                                        .setDataType(
-                                                AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                                new AppSearchSchema.StringPropertyConfig.Builder("body")
                                         .setCardinality(
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
                                         .setIndexingType(
-                                                AppSearchSchema.PropertyConfig
+                                                AppSearchSchema.StringPropertyConfig
                                                         .INDEXING_TYPE_PREFIXES)
                                         .setTokenizerType(
-                                                AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .TOKENIZER_TYPE_PLAIN)
                                         .build())
                         .build();
 
@@ -102,26 +100,20 @@
         AppSearchSchema musicRecordingSchema =
                 new AppSearchSchema.Builder("MusicRecording")
                         .addProperty(
-                                new AppSearchSchema.PropertyConfig.Builder("artist")
-                                        .setDataType(
-                                                AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                                new AppSearchSchema.StringPropertyConfig.Builder("artist")
                                         .setCardinality(
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
                                         .setIndexingType(
-                                                AppSearchSchema.PropertyConfig
+                                                AppSearchSchema.StringPropertyConfig
                                                         .INDEXING_TYPE_PREFIXES)
                                         .setTokenizerType(
-                                                AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                                                AppSearchSchema.StringPropertyConfig
+                                                        .TOKENIZER_TYPE_PLAIN)
                                         .build())
                         .addProperty(
-                                new AppSearchSchema.PropertyConfig.Builder("pubDate")
-                                        .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
+                                new AppSearchSchema.Int64PropertyConfig.Builder("pubDate")
                                         .setCardinality(
                                                 AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
-                                        .setIndexingType(
-                                                AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
-                                        .setTokenizerType(
-                                                AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
                                         .build())
                         .build();
 
@@ -147,14 +139,7 @@
                                         .setPropertyName("pubDate")
                                         .setDataType(PropertyConfigProto.DataType.Code.INT64)
                                         .setCardinality(
-                                                PropertyConfigProto.Cardinality.Code.OPTIONAL)
-                                        .setStringIndexingConfig(
-                                                StringIndexingConfig.newBuilder()
-                                                        .setTokenizerType(
-                                                                StringIndexingConfig.TokenizerType
-                                                                        .Code.NONE)
-                                                        .setTermMatchType(
-                                                                TermMatchType.Code.UNKNOWN)))
+                                                PropertyConfigProto.Cardinality.Code.OPTIONAL))
                         .build();
 
         assertThat(SchemaToProtoConverter.toSchemaTypeConfigProto(musicRecordingSchema))