Merge "Make BiometricPrompt credential UI closer to Settings" into rvc-dev
diff --git a/Android.bp b/Android.bp
index 3f06ffd..6653bd4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -577,25 +577,6 @@
     src: ":framework-annotation-proc",
 }
 
-// A library including just UnsupportedAppUsage.java classes.
-//
-// Provided for target so that libraries can use it without depending on
-// the whole of framework or the core platform API.
-//
-// Built for host so that the annotation processor can also use this annotation.
-java_library {
-    name: "unsupportedappusage-annotation",
-    host_supported: true,
-    srcs: [
-        "core/java/android/annotation/IntDef.java",
-    ],
-    static_libs: [
-        "art.module.api.annotations",
-    ],
-
-    sdk_version: "core_current",
-}
-
 // A temporary build target that is conditionally included on the bootclasspath if
 // android.test.base library has been removed and which provides support for
 // maintaining backwards compatibility for APKs that target pre-P and depend on
@@ -722,6 +703,7 @@
     srcs: [
         "core/java/android/annotation/StringDef.java",
         "core/java/android/net/annotations/PolicyDirection.java",
+        "core/java/com/android/internal/util/HexDump.java",
         "core/java/com/android/internal/util/IState.java",
         "core/java/com/android/internal/util/State.java",
         "core/java/com/android/internal/util/StateMachine.java",
@@ -970,7 +952,6 @@
         "core/java/android/content/pm/InstallationFileLocation.aidl",
         "core/java/android/content/pm/IDataLoaderStatusListener.aidl",
         "core/java/android/content/pm/IPackageInstallerSessionFileSystemConnector.aidl",
-        "core/java/android/content/pm/NamedParcelFileDescriptor.aidl",
     ],
     path: "core/java",
 }
@@ -1133,16 +1114,6 @@
     output: "framework-aidl-mappings.txt",
 }
 
-genrule {
-    name: "framework-annotation-proc-index",
-    srcs: [":framework-annotation-proc"],
-    cmd: "unzip -qp $(in) unsupportedappusage/unsupportedappusage_index.csv > $(out)",
-    out: ["unsupportedappusage_index.csv"],
-    dist: {
-        targets: ["droidcore"],
-    },
-}
-
 // Avoid including Parcelable classes as we don't want to have two copies of
 // Parcelable cross the libraries. This is used by telephony-common (frameworks/opt/telephony)
 // and TeleService app (packages/services/Telephony).
diff --git a/Android.mk b/Android.mk
index aea0c95..3b30714 100644
--- a/Android.mk
+++ b/Android.mk
@@ -39,11 +39,6 @@
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE))
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE):apistubs/android/public/api/android.txt)
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE):apistubs/android/system/api/android.txt)
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE):apistubs/android/test/api/android.txt)
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_MODULE_LIB_API_FILE):apistubs/android/module-lib/api/android.txt)
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_SERVER_API_FILE):apistubs/android/system-server/api/android.txt)
 
 # sdk.atree needs to copy the whole dir: $(OUT_DOCS)/offline-sdk to the final zip.
 # So keep offline-sdk-timestamp target here, and unzip offline-sdk-docs.zip to
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 12f211d..ccd87335 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -115,6 +115,11 @@
             baseline_file: "api/lint-baseline.txt",
         },
     },
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/public/api",
+        dest: "android.txt",
+    },
     jdiff_enabled: true,
 }
 
@@ -156,6 +161,11 @@
             baseline_file: "api/system-lint-baseline.txt",
         },
     },
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/system/api",
+        dest: "android.txt",
+    },
     jdiff_enabled: true,
 }
 
@@ -179,6 +189,11 @@
             baseline_file: "api/test-lint-baseline.txt",
         },
     },
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/test/api",
+        dest: "android.txt",
+    },
 }
 
 /////////////////////////////////////////////////////////////////////
@@ -214,6 +229,11 @@
             baseline_file: "api/module-lib-lint-baseline.txt",
         },
     },
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/module-lib/api",
+        dest: "android.txt",
+    },
 }
 
 
diff --git a/apex/Android.bp b/apex/Android.bp
index cd34f98..d0889ef 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -84,6 +84,36 @@
     },
 }
 
+java_defaults {
+    name: "framework-module-stubs-lib-defaults-publicapi",
+    installable: false,
+    sdk_version: "module_current",
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/public",
+    },
+}
+
+java_defaults {
+    name: "framework-module-stubs-lib-defaults-systemapi",
+    installable: false,
+    sdk_version: "module_current",
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/system",
+    },
+}
+
+java_defaults {
+    name: "framework-module-stubs-lib-defaults-module_libs_api",
+    installable: false,
+    sdk_version: "module_current",
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/module-lib",
+    },
+}
+
 // The defaults for module_libs comes in two parts - defaults for API checks
 // and defaults for stub generation. This is because we want the API txt
 // files to *only* include the module_libs_api, but the stubs to include
@@ -119,6 +149,7 @@
     name: "service-module-stubs-srcs-defaults",
     args: mainline_service_stubs_args,
     installable: false,
+    filter_packages: ["com.android."],
     check_api: {
         current: {
             api_file: "api/current.txt",
@@ -135,4 +166,8 @@
 // module java_library system_server stub libs.
 java_defaults {
     name: "service-module-stubs-defaults",
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/system-server",
+    },
 }
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index e5a685f..c8ca44b 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -116,7 +116,7 @@
         return mUserId;
     }
 
-    void addCommitter(@NonNull Committer committer) {
+    void addOrReplaceCommitter(@NonNull Committer committer) {
         synchronized (mMetadataLock) {
             // We need to override the committer data, so first remove any existing
             // committer before adding the new one.
@@ -139,6 +139,12 @@
         }
     }
 
+    void removeCommitter(@NonNull Committer committer) {
+        synchronized (mMetadataLock) {
+            mCommitters.remove(committer);
+        }
+    }
+
     void removeInvalidCommitters(SparseArray<String> packages) {
         synchronized (mMetadataLock) {
             mCommitters.removeIf(committer ->
@@ -154,7 +160,7 @@
         }
     }
 
-    void addLeasee(String callingPackage, int callingUid, int descriptionResId,
+    void addOrReplaceLeasee(String callingPackage, int callingUid, int descriptionResId,
             CharSequence description, long leaseExpiryTimeMillis) {
         synchronized (mMetadataLock) {
             // We need to override the leasee data, so first remove any existing
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 65ccb99..e472d05 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -401,7 +401,7 @@
                 throw new LimitExceededException("Total amount of data with an active lease"
                         + " is exceeding the max limit");
             }
-            blobMetadata.addLeasee(callingPackage, callingUid,
+            blobMetadata.addOrReplaceLeasee(callingPackage, callingUid,
                     descriptionResId, description, leaseExpiryTimeMillis);
             if (LOGV) {
                 Slog.v(TAG, "Acquired lease on " + blobHandle
@@ -573,12 +573,16 @@
                     final Committer newCommitter = new Committer(session.getOwnerPackageName(),
                             session.getOwnerUid(), session.getBlobAccessMode());
                     final Committer existingCommitter = blob.getExistingCommitter(newCommitter);
-                    blob.addCommitter(newCommitter);
+                    blob.addOrReplaceCommitter(newCommitter);
                     try {
                         writeBlobsInfoLocked();
                         session.sendCommitCallbackResult(COMMIT_RESULT_SUCCESS);
                     } catch (Exception e) {
-                        blob.addCommitter(existingCommitter);
+                        if (existingCommitter == null) {
+                            blob.removeCommitter(newCommitter);
+                        } else {
+                            blob.addOrReplaceCommitter(existingCommitter);
+                        }
                         session.sendCommitCallbackResult(COMMIT_RESULT_ERROR);
                     }
                     getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
@@ -1349,8 +1353,15 @@
 
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
                     "Caller is not allowed to call this; caller=" + Binder.getCallingUid());
-            mHandler.post(PooledLambda.obtainRunnable(remoteCallback::sendResult, null)
-                    .recycleOnUse());
+            // We post messages back and forth between mHandler thread and mBackgroundHandler
+            // thread while committing a blob. We need to replicate the same pattern here to
+            // ensure pending messages have been handled.
+            mHandler.post(() -> {
+                mBackgroundHandler.post(() -> {
+                    mHandler.post(PooledLambda.obtainRunnable(remoteCallback::sendResult, null)
+                            .recycleOnUse());
+                });
+            });
         }
 
         @Override
diff --git a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
index 8fbfb1d..d99830dc4 100644
--- a/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/TEST_MAPPING
@@ -7,6 +7,7 @@
       ],
       "options": [
         {"include-filter": "com.android.server.DeviceIdleControllerTest"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
         {"exclude-annotation": "androidx.test.filters.FlakyTest"}
       ]
     }
diff --git a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
index bc7a7d3..b76c582 100644
--- a/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/deviceidle/TEST_MAPPING
@@ -4,6 +4,7 @@
       "name": "FrameworksMockingServicesTests",
       "options": [
         {"include-filter": "com.android.server.DeviceIdleControllerTest"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
         {"exclude-annotation": "androidx.test.filters.FlakyTest"}
       ]
     }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
index e2e1180..484fec3 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/job/TEST_MAPPING
@@ -3,6 +3,7 @@
         {
             "name": "CtsJobSchedulerTestCases",
             "options": [
+                {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
                 {"exclude-annotation": "androidx.test.filters.FlakyTest"},
                 {"exclude-annotation": "androidx.test.filters.LargeTest"}
             ]
@@ -11,6 +12,7 @@
             "name": "FrameworksMockingServicesTests",
             "options": [
                 {"include-filter": "com.android.server.job"},
+                {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
                 {"exclude-annotation": "androidx.test.filters.FlakyTest"}
             ]
         },
@@ -18,6 +20,7 @@
             "name": "FrameworksServicesTests",
             "options": [
                 {"include-filter": "com.android.server.job"},
+                {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
                 {"exclude-annotation": "androidx.test.filters.FlakyTest"}
             ]
         }
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
index ba7572a..c5dc51c 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
@@ -4,6 +4,7 @@
       "name": "CtsUsageStatsTestCases",
       "options": [
         {"include-filter": "android.app.usage.cts.UsageStatsTest"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
         {"exclude-annotation": "androidx.test.filters.FlakyTest"}
       ]
     },
@@ -11,6 +12,7 @@
       "name": "FrameworksServicesTests",
       "options": [
         {"include-filter": "com.android.server.usage"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
         {"exclude-annotation": "androidx.test.filters.FlakyTest"}
       ]
     }
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 821dd9e..99e82e7 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -132,19 +132,19 @@
 java_library {
     name: "framework-media-stubs-publicapi",
     srcs: [":framework-media-stubs-srcs-publicapi"],
-    sdk_version: "current",
+    defaults: ["framework-module-stubs-lib-defaults-publicapi"],
 }
 
 java_library {
     name: "framework-media-stubs-systemapi",
     srcs: [":framework-media-stubs-srcs-systemapi"],
-    sdk_version: "system_current",
+    defaults: ["framework-module-stubs-lib-defaults-systemapi"],
 }
 
 java_library {
     name: "framework-media-stubs-module_libs_api",
     srcs: [":framework-media-stubs-srcs-module_libs_api"],
-    sdk_version: "system_current",
+    defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
 }
 
 java_library {
diff --git a/apex/media/framework/api/current.txt b/apex/media/framework/api/current.txt
index 839fb51..9cec748 100644
--- a/apex/media/framework/api/current.txt
+++ b/apex/media/framework/api/current.txt
@@ -29,7 +29,7 @@
     method public boolean advance(@NonNull android.media.MediaParser.SeekableInputReader) throws java.io.IOException;
     method @NonNull public static android.media.MediaParser create(@NonNull android.media.MediaParser.OutputConsumer, @NonNull java.lang.String...);
     method @NonNull public static android.media.MediaParser createByName(@NonNull String, @NonNull android.media.MediaParser.OutputConsumer);
-    method @Nullable public String getParserName();
+    method @NonNull public String getParserName();
     method @NonNull public static java.util.List<java.lang.String> getParserNames(@NonNull android.media.MediaFormat);
     method public void release();
     method public void seek(@NonNull android.media.MediaParser.SeekPoint);
@@ -65,6 +65,7 @@
     field public static final String PARSER_NAME_OGG = "android.media.mediaparser.OggParser";
     field public static final String PARSER_NAME_PS = "android.media.mediaparser.PsParser";
     field public static final String PARSER_NAME_TS = "android.media.mediaparser.TsParser";
+    field public static final String PARSER_NAME_UNKNOWN = "android.media.mediaparser.UNKNOWN";
     field public static final String PARSER_NAME_WAV = "android.media.mediaparser.WavParser";
   }
 
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 02c55b7..d8ae485 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -45,6 +45,7 @@
 import com.google.android.exoplayer2.extractor.ts.Ac3Extractor;
 import com.google.android.exoplayer2.extractor.ts.Ac4Extractor;
 import com.google.android.exoplayer2.extractor.ts.AdtsExtractor;
+import com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory;
 import com.google.android.exoplayer2.extractor.ts.PsExtractor;
 import com.google.android.exoplayer2.extractor.ts.TsExtractor;
 import com.google.android.exoplayer2.extractor.wav.WavExtractor;
@@ -52,6 +53,7 @@
 import com.google.android.exoplayer2.upstream.DataSpec;
 import com.google.android.exoplayer2.upstream.TransferListener;
 import com.google.android.exoplayer2.util.ParsableByteArray;
+import com.google.android.exoplayer2.util.Util;
 import com.google.android.exoplayer2.video.ColorInfo;
 
 import java.io.EOFException;
@@ -60,6 +62,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
@@ -450,6 +453,7 @@
     @StringDef(
             prefix = {"PARSER_NAME_"},
             value = {
+                PARSER_NAME_UNKNOWN,
                 PARSER_NAME_MATROSKA,
                 PARSER_NAME_FMP4,
                 PARSER_NAME_MP4,
@@ -467,6 +471,7 @@
             })
     public @interface ParserName {}
 
+    public static final String PARSER_NAME_UNKNOWN = "android.media.mediaparser.UNKNOWN";
     public static final String PARSER_NAME_MATROSKA = "android.media.mediaparser.MatroskaParser";
     public static final String PARSER_NAME_FMP4 = "android.media.mediaparser.FragmentedMp4Parser";
     public static final String PARSER_NAME_MP4 = "android.media.mediaparser.Mp4Parser";
@@ -642,6 +647,9 @@
 
     private static final Map<String, ExtractorFactory> EXTRACTOR_FACTORIES_BY_NAME;
     private static final Map<String, Class> EXPECTED_TYPE_BY_PARAMETER_NAME;
+    private static final String TS_MODE_SINGLE_PMT = "single_pmt";
+    private static final String TS_MODE_MULTI_PMT = "multi_pmt";
+    private static final String TS_MODE_HLS = "hls";
 
     // Instance creation methods.
 
@@ -688,12 +696,83 @@
      * Returns an immutable list with the names of the parsers that are suitable for container
      * formats with the given {@link MediaFormat}.
      *
-     * <p>TODO: List which properties are taken into account. E.g. MimeType.
+     * <p>A parser supports a {@link MediaFormat} if the mime type associated with {@link
+     * MediaFormat#KEY_MIME} corresponds to the supported container format.
+     *
+     * @param mediaFormat The {@link MediaFormat} to check support for.
+     * @return The parser names that support the given {@code mediaFormat}, or the list of all
+     *     parsers available if no container specific format information is provided.
      */
     @NonNull
     @ParserName
     public static List<String> getParserNames(@NonNull MediaFormat mediaFormat) {
-        throw new UnsupportedOperationException();
+        String mimeType = mediaFormat.getString(MediaFormat.KEY_MIME);
+        mimeType = mimeType == null ? null : Util.toLowerInvariant(mimeType.trim());
+        if (TextUtils.isEmpty(mimeType)) {
+            // No MIME type provided. Return all.
+            return Collections.unmodifiableList(
+                    new ArrayList<>(EXTRACTOR_FACTORIES_BY_NAME.keySet()));
+        }
+        ArrayList<String> result = new ArrayList<>();
+        switch (mimeType) {
+            case "video/x-matroska":
+            case "audio/x-matroska":
+            case "video/x-webm":
+            case "audio/x-webm":
+                result.add(PARSER_NAME_MATROSKA);
+                break;
+            case "video/mp4":
+            case "audio/mp4":
+            case "application/mp4":
+                result.add(PARSER_NAME_MP4);
+                result.add(PARSER_NAME_FMP4);
+                break;
+            case "audio/mpeg":
+                result.add(PARSER_NAME_MP3);
+                break;
+            case "audio/aac":
+                result.add(PARSER_NAME_ADTS);
+                break;
+            case "audio/ac3":
+                result.add(PARSER_NAME_AC3);
+                break;
+            case "video/mp2t":
+            case "audio/mp2t":
+                result.add(PARSER_NAME_TS);
+                break;
+            case "video/x-flv":
+                result.add(PARSER_NAME_FLV);
+                break;
+            case "video/ogg":
+            case "audio/ogg":
+            case "application/ogg":
+                result.add(PARSER_NAME_OGG);
+                break;
+            case "video/mp2p":
+            case "video/mp1s":
+                result.add(PARSER_NAME_PS);
+                break;
+            case "audio/vnd.wave":
+            case "audio/wav":
+            case "audio/wave":
+            case "audio/x-wav":
+                result.add(PARSER_NAME_WAV);
+                break;
+            case "audio/amr":
+                result.add(PARSER_NAME_AMR);
+                break;
+            case "audio/ac4":
+                result.add(PARSER_NAME_AC4);
+                break;
+            case "audio/flac":
+            case "audio/x-flac":
+                result.add(PARSER_NAME_FLAC);
+                break;
+            default:
+                // No parsers support the given mime type. Do nothing.
+                break;
+        }
+        return Collections.unmodifiableList(result);
     }
 
     // Private fields.
@@ -744,6 +823,12 @@
                             + value.getClass().getSimpleName()
                             + " was passed.");
         }
+        if (PARAMETER_TS_MODE.equals(parameterName)
+                && !TS_MODE_SINGLE_PMT.equals(value)
+                && !TS_MODE_HLS.equals(value)
+                && !TS_MODE_MULTI_PMT.equals(value)) {
+            throw new IllegalArgumentException(PARAMETER_TS_MODE + " does not accept: " + value);
+        }
         mParserParameters.put(parameterName, value);
         return this;
     }
@@ -763,14 +848,14 @@
      * Returns the name of the backing parser implementation.
      *
      * <p>If this instance was creating using {@link #createByName}, the provided name is returned.
-     * If this instance was created using {@link #create}, this method will return null until the
-     * first call to {@link #advance}, after which the name of the backing parser implementation is
-     * returned.
+     * If this instance was created using {@link #create}, this method will return {@link
+     * #PARSER_NAME_UNKNOWN} until the first call to {@link #advance}, after which the name of the
+     * backing parser implementation is returned.
      *
      * @return The name of the backing parser implementation, or null if the backing parser
      *     implementation has not yet been selected.
      */
-    @Nullable
+    @NonNull
     @ParserName
     public String getParserName() {
         return mExtractorName;
@@ -807,13 +892,12 @@
 
         // TODO: Apply parameters when creating extractor instances.
         if (mExtractor == null) {
-            if (mExtractorName != null) {
+            if (!mExtractorName.equals(PARSER_NAME_UNKNOWN)) {
                 mExtractor = EXTRACTOR_FACTORIES_BY_NAME.get(mExtractorName).createInstance();
                 mExtractor.init(new ExtractorOutputAdapter());
             } else {
                 for (String parserName : mParserNamesPool) {
-                    Extractor extractor =
-                            EXTRACTOR_FACTORIES_BY_NAME.get(parserName).createInstance();
+                    Extractor extractor = createExtractor(parserName);
                     try {
                         if (extractor.sniff(mExtractorInput)) {
                             mExtractorName = parserName;
@@ -901,9 +985,7 @@
         mParserParameters = new HashMap<>();
         mOutputConsumer = outputConsumer;
         mParserNamesPool = parserNamesPool;
-        if (!sniff) {
-            mExtractorName = parserNamesPool[0];
-        }
+        mExtractorName = sniff ? PARSER_NAME_UNKNOWN : parserNamesPool[0];
         mPositionHolder = new PositionHolder();
         mDataSource = new InputReadingDataSource();
         removePendingSeek();
@@ -920,6 +1002,124 @@
         mPendingSeekTimeMicros = -1;
     }
 
+    private Extractor createExtractor(String parserName) {
+        int flags = 0;
+        switch (parserName) {
+            case PARSER_NAME_MATROSKA:
+                flags =
+                        getBooleanParameter(PARAMETER_MATROSKA_DISABLE_CUES_SEEKING)
+                                ? MatroskaExtractor.FLAG_DISABLE_SEEK_FOR_CUES
+                                : 0;
+                return new MatroskaExtractor(flags);
+            case PARSER_NAME_FMP4:
+                flags |=
+                        getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS)
+                                ? FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS
+                                : 0;
+                flags |=
+                        getBooleanParameter(PARAMETER_MP4_IGNORE_TFDT_BOX)
+                                ? FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX
+                                : 0;
+                flags |=
+                        getBooleanParameter(PARAMETER_MP4_TREAT_VIDEO_FRAMES_AS_KEYFRAMES)
+                                ? FragmentedMp4Extractor
+                                        .FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME
+                                : 0;
+                return new FragmentedMp4Extractor(flags);
+            case PARSER_NAME_MP4:
+                flags |=
+                        getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS)
+                                ? Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS
+                                : 0;
+                return new Mp4Extractor();
+            case PARSER_NAME_MP3:
+                flags |=
+                        getBooleanParameter(PARAMETER_MP3_DISABLE_ID3)
+                                ? Mp3Extractor.FLAG_DISABLE_ID3_METADATA
+                                : 0;
+                flags |=
+                        getBooleanParameter(PARAMETER_MP3_ENABLE_CBR_SEEKING)
+                                ? Mp3Extractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
+                                : 0;
+                // TODO: Add index seeking once we update the ExoPlayer version.
+                return new Mp3Extractor(flags);
+            case PARSER_NAME_ADTS:
+                flags |=
+                        getBooleanParameter(PARAMETER_ADTS_ENABLE_CBR_SEEKING)
+                                ? AdtsExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
+                                : 0;
+                return new AdtsExtractor(flags);
+            case PARSER_NAME_AC3:
+                return new Ac3Extractor();
+            case PARSER_NAME_TS:
+                flags |=
+                        getBooleanParameter(PARAMETER_TS_ALLOW_NON_IDR_AVC_KEYFRAMES)
+                                ? DefaultTsPayloadReaderFactory.FLAG_ALLOW_NON_IDR_KEYFRAMES
+                                : 0;
+                flags |=
+                        getBooleanParameter(PARAMETER_TS_DETECT_ACCESS_UNITS)
+                                ? DefaultTsPayloadReaderFactory.FLAG_DETECT_ACCESS_UNITS
+                                : 0;
+                flags |=
+                        getBooleanParameter(PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS)
+                                ? DefaultTsPayloadReaderFactory.FLAG_ENABLE_HDMV_DTS_AUDIO_STREAMS
+                                : 0;
+                flags |=
+                        getBooleanParameter(PARAMETER_TS_IGNORE_AAC_STREAM)
+                                ? DefaultTsPayloadReaderFactory.FLAG_IGNORE_AAC_STREAM
+                                : 0;
+                flags |=
+                        getBooleanParameter(PARAMETER_TS_IGNORE_AVC_STREAM)
+                                ? DefaultTsPayloadReaderFactory.FLAG_IGNORE_H264_STREAM
+                                : 0;
+                flags |=
+                        getBooleanParameter(PARAMETER_TS_IGNORE_SPLICE_INFO_STREAM)
+                                ? DefaultTsPayloadReaderFactory.FLAG_IGNORE_SPLICE_INFO_STREAM
+                                : 0;
+                String tsMode = getStringParameter(PARAMETER_TS_MODE, TS_MODE_SINGLE_PMT);
+                int hlsMode =
+                        TS_MODE_SINGLE_PMT.equals(tsMode)
+                                ? TsExtractor.MODE_SINGLE_PMT
+                                : TS_MODE_HLS.equals(tsMode)
+                                        ? TsExtractor.MODE_HLS
+                                        : TsExtractor.MODE_MULTI_PMT;
+                return new TsExtractor(hlsMode, flags);
+            case PARSER_NAME_FLV:
+                return new FlvExtractor();
+            case PARSER_NAME_OGG:
+                return new OggExtractor();
+            case PARSER_NAME_PS:
+                return new PsExtractor();
+            case PARSER_NAME_WAV:
+                return new WavExtractor();
+            case PARSER_NAME_AMR:
+                flags |=
+                        getBooleanParameter(PARAMETER_AMR_ENABLE_CBR_SEEKING)
+                                ? AmrExtractor.FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
+                                : 0;
+                return new AmrExtractor(flags);
+            case PARSER_NAME_AC4:
+                return new Ac4Extractor();
+            case PARSER_NAME_FLAC:
+                flags |=
+                        getBooleanParameter(PARAMETER_FLAC_DISABLE_ID3)
+                                ? FlacExtractor.FLAG_DISABLE_ID3_METADATA
+                                : 0;
+                return new FlacExtractor(flags);
+            default:
+                // Should never happen.
+                throw new IllegalStateException("Unexpected attempt to create: " + parserName);
+        }
+    }
+
+    private boolean getBooleanParameter(String name) {
+        return (boolean) mParserParameters.getOrDefault(name, false);
+    }
+
+    private String getStringParameter(String name, String defaultValue) {
+        return (String) mParserParameters.getOrDefault(name, defaultValue);
+    }
+
     // Private classes.
 
     private static final class InputReadingDataSource implements DataSource {
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
index 6d96200..793247e 100644
--- a/apex/permission/framework/Android.bp
+++ b/apex/permission/framework/Android.bp
@@ -31,6 +31,10 @@
         "com.android.permission",
         "test_com.android.permission",
     ],
+    permitted_packages: [
+        "android.permission",
+        "android.app.role",
+    ],
     hostdex: true,
     installable: true,
     visibility: [
@@ -84,20 +88,17 @@
 java_library {
     name: "framework-permission-stubs-publicapi",
     srcs: [ ":framework-permission-stubs-srcs-publicapi" ],
-    sdk_version: "system_current",
-    installable: false,
+    defaults: ["framework-module-stubs-lib-defaults-publicapi"],
 }
 
 java_library {
     name: "framework-permission-stubs-systemapi",
     srcs: [ ":framework-permission-stubs-srcs-systemapi" ],
-    sdk_version: "system_current",
-    installable: false,
+    defaults: ["framework-module-stubs-lib-defaults-systemapi"],
 }
 
 java_library {
     name: "framework-permission-stubs-module_libs_api",
     srcs: [ ":framework-permission-stubs-srcs-module_libs_api" ],
-    sdk_version: "system_current",
-    installable: false,
+    defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
 }
diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp
index 679c98d..5cdcdd3 100644
--- a/apex/permission/service/Android.bp
+++ b/apex/permission/service/Android.bp
@@ -17,6 +17,7 @@
     srcs: [
         "java/**/*.java",
     ],
+    path: "java",
 }
 
 java_library {
diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp
index 86f4ab7..707113b 100644
--- a/apex/sdkextensions/framework/Android.bp
+++ b/apex/sdkextensions/framework/Android.bp
@@ -86,7 +86,7 @@
 java_library {
     name: "framework-sdkextensions-stubs-publicapi",
     srcs: [":framework-sdkextensions-stubs-srcs-publicapi"],
-    sdk_version: "current",
+    defaults: ["framework-module-stubs-lib-defaults-publicapi"],
     visibility: [
         "//frameworks/base", // Framework
         "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
@@ -96,7 +96,7 @@
 java_library {
     name: "framework-sdkextensions-stubs-systemapi",
     srcs: [":framework-sdkextensions-stubs-srcs-systemapi"],
-    sdk_version: "system_current",
+    defaults: ["framework-module-stubs-lib-defaults-systemapi"],
     visibility: [
         "//frameworks/base", // Framework
         "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
@@ -106,7 +106,7 @@
 java_library {
     name: "framework-sdkextensions-stubs-module_libs_api",
     srcs: [":framework-sdkextensions-stubs-srcs-module_libs_api"],
-    sdk_version: "system_current",
+    defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
     visibility: [
         "//frameworks/base", // Framework
         "//frameworks/base/apex/sdkextensions", // sdkextensions SDK
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 8185bb0..804eb03 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -46,19 +46,11 @@
         "//frameworks/base/apex/statsd:__subpackages__",
     ],
 }
-
-java_defaults {
-    name: "framework-statsd-defaults",
-    sdk_version: "module_current",
-    libs: [ "framework-annotations-lib" ],
-}
-
 java_library {
     name: "framework-statsd",
-    defaults: [
-        "framework-statsd-defaults",
-    ],
     installable: true,
+    sdk_version: "module_current",
+    libs: [ "framework-annotations-lib" ],
 
     srcs: [
         ":framework-statsd-sources",
@@ -129,39 +121,33 @@
 
 java_library {
     name: "framework-statsd-stubs-publicapi",
-    defaults: [
-        "framework-statsd-defaults",
-    ],
+    defaults: ["framework-module-stubs-lib-defaults-publicapi"],
     srcs: [ ":framework-statsd-stubs-srcs-publicapi" ],
     visibility: [
         "//frameworks/base", // Framework
         "//frameworks/base/apex/statsd", // statsd apex
-    ]
+    ],
 }
 
 java_library {
     name: "framework-statsd-stubs-systemapi",
-    defaults: [
-        "framework-statsd-defaults",
-    ],
+    defaults: ["framework-module-stubs-lib-defaults-systemapi"],
     srcs: [ ":framework-statsd-stubs-srcs-systemapi" ],
     visibility: [
         "//frameworks/base", // Framework
         "//frameworks/base/apex/statsd", // statsd apex
-    ]
+    ],
 }
 
 java_library {
     name: "framework-statsd-stubs-module_libs_api",
-    defaults: [
-        "framework-statsd-defaults",
-    ],
+    defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
     srcs: [ ":framework-statsd-stubs-srcs-module_libs_api" ],
     visibility: [
         "//frameworks/base", // Framework
         "//frameworks/base/apex/statsd", // statsd apex
         "//frameworks/opt/net/wifi/service" // wifi service
-    ]
+    ],
 }
 
 android_test {
diff --git a/api/current.txt b/api/current.txt
index 680b224..2a13e2e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9,7 +9,6 @@
     ctor public Manifest.permission();
     field public static final String ACCEPT_HANDOVER = "android.permission.ACCEPT_HANDOVER";
     field public static final String ACCESS_BACKGROUND_LOCATION = "android.permission.ACCESS_BACKGROUND_LOCATION";
-    field public static final String ACCESS_CALL_AUDIO = "android.permission.ACCESS_CALL_AUDIO";
     field public static final String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES";
     field public static final String ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION";
     field public static final String ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION";
@@ -278,7 +277,6 @@
     field public static final int activityCloseExitAnimation = 16842939; // 0x10100bb
     field public static final int activityOpenEnterAnimation = 16842936; // 0x10100b8
     field public static final int activityOpenExitAnimation = 16842937; // 0x10100b9
-    field public static final int actor = 16844313; // 0x1010619
     field public static final int addPrintersActivity = 16843750; // 0x10103e6
     field public static final int addStatesFromChildren = 16842992; // 0x10100f0
     field public static final int adjustViewBounds = 16843038; // 0x101011e
@@ -289,7 +287,6 @@
     field public static final int alignmentMode = 16843642; // 0x101037a
     field public static final int allContactsName = 16843468; // 0x10102cc
     field public static final int allowAudioPlaybackCapture = 16844289; // 0x1010601
-    field public static final int allowAutoRevokePermissionsExemption = 16844309; // 0x1010615
     field public static final int allowBackup = 16843392; // 0x1010280
     field public static final int allowClearUserData = 16842757; // 0x1010005
     field public static final int allowEmbedded = 16843765; // 0x10103f5
@@ -329,6 +326,7 @@
     field public static final int autoLink = 16842928; // 0x10100b0
     field public static final int autoMirrored = 16843754; // 0x10103ea
     field public static final int autoRemoveFromRecents = 16843847; // 0x1010447
+    field public static final int autoRevokePermissions = 16844309; // 0x1010615
     field public static final int autoSizeMaxTextSize = 16844102; // 0x1010546
     field public static final int autoSizeMinTextSize = 16844088; // 0x1010538
     field public static final int autoSizePresetSizes = 16844087; // 0x1010537
@@ -1141,7 +1139,6 @@
     field public static final int reqKeyboardType = 16843304; // 0x1010228
     field public static final int reqNavigation = 16843306; // 0x101022a
     field public static final int reqTouchScreen = 16843303; // 0x1010227
-    field public static final int requestAutoRevokePermissionsExemption = 16844308; // 0x1010614
     field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
     field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
     field public static final int required = 16843406; // 0x101028e
@@ -24359,7 +24356,7 @@
   }
 
   public final class AudioMetadata {
-    method @NonNull public static android.media.AudioMetadata.Map createMap();
+    method @NonNull public static android.media.AudioMetadataMap createMap();
   }
 
   public static class AudioMetadata.Format {
@@ -24377,16 +24374,16 @@
     method @NonNull public Class<T> getValueClass();
   }
 
-  public static interface AudioMetadata.Map extends android.media.AudioMetadata.ReadMap {
+  public interface AudioMetadataMap extends android.media.AudioMetadataReadMap {
     method @Nullable public <T> T remove(@NonNull android.media.AudioMetadata.Key<T>);
     method @Nullable public <T> T set(@NonNull android.media.AudioMetadata.Key<T>, @NonNull T);
   }
 
-  public static interface AudioMetadata.ReadMap {
+  public interface AudioMetadataReadMap {
     method public <T> boolean containsKey(@NonNull android.media.AudioMetadata.Key<T>);
-    method @NonNull public android.media.AudioMetadata.Map dup();
+    method @NonNull public android.media.AudioMetadataMap dup();
     method @Nullable public <T> T get(@NonNull android.media.AudioMetadata.Key<T>);
-    method public int size();
+    method @IntRange(from=0) public int size();
   }
 
   public final class AudioPlaybackCaptureConfiguration {
@@ -24701,7 +24698,7 @@
   }
 
   public static interface AudioTrack.OnCodecFormatChangedListener {
-    method public void onCodecFormatChanged(@NonNull android.media.AudioTrack, @Nullable android.media.AudioMetadata.ReadMap);
+    method public void onCodecFormatChanged(@NonNull android.media.AudioTrack, @Nullable android.media.AudioMetadataReadMap);
   }
 
   public static interface AudioTrack.OnPlaybackPositionUpdateListener {
@@ -25341,11 +25338,12 @@
   public final class MediaCodec.QueueRequest {
     method public void queue();
     method @NonNull public android.media.MediaCodec.QueueRequest setByteBufferParameter(@NonNull String, @NonNull java.nio.ByteBuffer);
+    method @NonNull public android.media.MediaCodec.QueueRequest setEncryptedLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @NonNull android.media.MediaCodec.CryptoInfo);
     method @NonNull public android.media.MediaCodec.QueueRequest setFlags(int);
     method @NonNull public android.media.MediaCodec.QueueRequest setFloatParameter(@NonNull String, float);
     method @NonNull public android.media.MediaCodec.QueueRequest setHardwareBuffer(@NonNull android.hardware.HardwareBuffer);
     method @NonNull public android.media.MediaCodec.QueueRequest setIntegerParameter(@NonNull String, int);
-    method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int, @Nullable android.media.MediaCodec.CryptoInfo);
+    method @NonNull public android.media.MediaCodec.QueueRequest setLinearBlock(@NonNull android.media.MediaCodec.LinearBlock, int, int);
     method @NonNull public android.media.MediaCodec.QueueRequest setLongParameter(@NonNull String, long);
     method @NonNull public android.media.MediaCodec.QueueRequest setPresentationTimeUs(long);
     method @NonNull public android.media.MediaCodec.QueueRequest setStringParameter(@NonNull String, @NonNull String);
@@ -26401,7 +26399,7 @@
     method public boolean advance(@NonNull android.media.MediaParser.SeekableInputReader) throws java.io.IOException;
     method @NonNull public static android.media.MediaParser create(@NonNull android.media.MediaParser.OutputConsumer, @NonNull java.lang.String...);
     method @NonNull public static android.media.MediaParser createByName(@NonNull String, @NonNull android.media.MediaParser.OutputConsumer);
-    method @Nullable public String getParserName();
+    method @NonNull public String getParserName();
     method @NonNull public static java.util.List<java.lang.String> getParserNames(@NonNull android.media.MediaFormat);
     method public void release();
     method public void seek(@NonNull android.media.MediaParser.SeekPoint);
@@ -26437,6 +26435,7 @@
     field public static final String PARSER_NAME_OGG = "android.media.mediaparser.OggParser";
     field public static final String PARSER_NAME_PS = "android.media.mediaparser.PsParser";
     field public static final String PARSER_NAME_TS = "android.media.mediaparser.TsParser";
+    field public static final String PARSER_NAME_UNKNOWN = "android.media.mediaparser.UNKNOWN";
     field public static final String PARSER_NAME_WAV = "android.media.mediaparser.WavParser";
   }
 
@@ -30348,6 +30347,7 @@
     method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
     method public boolean hasCapability(int);
     method public boolean hasTransport(int);
+    method public boolean satisfiedBy(@Nullable android.net.NetworkCapabilities);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR;
   }
@@ -37146,11 +37146,10 @@
     field public static final int EFFECT_TICK = 2; // 0x2
   }
 
-  public static class VibrationEffect.Composition {
-    ctor public VibrationEffect.Composition();
-    method @Nullable public android.os.VibrationEffect.Composition addPrimitive(int);
-    method @Nullable public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float);
-    method @Nullable public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int);
+  public static final class VibrationEffect.Composition {
+    method @NonNull public android.os.VibrationEffect.Composition addPrimitive(int);
+    method @NonNull public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float);
+    method @NonNull public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int);
     method @NonNull public android.os.VibrationEffect compose();
     field public static final int PRIMITIVE_CLICK = 1; // 0x1
     field public static final int PRIMITIVE_QUICK_FALL = 6; // 0x6
@@ -37160,9 +37159,9 @@
   }
 
   public abstract class Vibrator {
-    method @Nullable public Boolean areAllEffectsSupported(@NonNull int...);
-    method public boolean areAllPrimitivesSupported(@NonNull int...);
-    method @Nullable public boolean[] areEffectsSupported(@NonNull int...);
+    method public final int areAllEffectsSupported(@NonNull int...);
+    method public final boolean areAllPrimitivesSupported(@NonNull int...);
+    method @NonNull public int[] areEffectsSupported(@NonNull int...);
     method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...);
     method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
     method public abstract boolean hasAmplitudeControl();
@@ -37173,6 +37172,9 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long[], int, android.media.AudioAttributes);
     method @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(android.os.VibrationEffect);
     method @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(android.os.VibrationEffect, android.media.AudioAttributes);
+    field public static final int VIBRATION_EFFECT_SUPPORT_NO = 2; // 0x2
+    field public static final int VIBRATION_EFFECT_SUPPORT_UNKNOWN = 0; // 0x0
+    field public static final int VIBRATION_EFFECT_SUPPORT_YES = 1; // 0x1
   }
 
   public class WorkSource implements android.os.Parcelable {
@@ -45923,13 +45925,9 @@
     method public void onConference(android.telecom.Connection, android.telecom.Connection);
     method public void onConnectionServiceFocusGained();
     method public void onConnectionServiceFocusLost();
-    method @Nullable public android.telecom.Conference onCreateIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
-    method public void onCreateIncomingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
-    method @Nullable public android.telecom.Conference onCreateOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
-    method public void onCreateOutgoingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
@@ -46419,14 +46417,26 @@
     field public static final int BAND_46 = 46; // 0x2e
     field public static final int BAND_47 = 47; // 0x2f
     field public static final int BAND_48 = 48; // 0x30
+    field public static final int BAND_49 = 49; // 0x31
     field public static final int BAND_5 = 5; // 0x5
+    field public static final int BAND_50 = 50; // 0x32
+    field public static final int BAND_51 = 51; // 0x33
+    field public static final int BAND_52 = 52; // 0x34
+    field public static final int BAND_53 = 53; // 0x35
     field public static final int BAND_6 = 6; // 0x6
     field public static final int BAND_65 = 65; // 0x41
     field public static final int BAND_66 = 66; // 0x42
     field public static final int BAND_68 = 68; // 0x44
     field public static final int BAND_7 = 7; // 0x7
     field public static final int BAND_70 = 70; // 0x46
+    field public static final int BAND_71 = 71; // 0x47
+    field public static final int BAND_72 = 72; // 0x48
+    field public static final int BAND_73 = 73; // 0x49
+    field public static final int BAND_74 = 74; // 0x4a
     field public static final int BAND_8 = 8; // 0x8
+    field public static final int BAND_85 = 85; // 0x55
+    field public static final int BAND_87 = 87; // 0x57
+    field public static final int BAND_88 = 88; // 0x58
     field public static final int BAND_9 = 9; // 0x9
   }
 
@@ -46490,7 +46500,13 @@
     field public static final int BAND_83 = 83; // 0x53
     field public static final int BAND_84 = 84; // 0x54
     field public static final int BAND_86 = 86; // 0x56
+    field public static final int BAND_89 = 89; // 0x59
     field public static final int BAND_90 = 90; // 0x5a
+    field public static final int BAND_91 = 91; // 0x5b
+    field public static final int BAND_92 = 92; // 0x5c
+    field public static final int BAND_93 = 93; // 0x5d
+    field public static final int BAND_94 = 94; // 0x5e
+    field public static final int BAND_95 = 95; // 0x5f
   }
 
   public static final class AccessNetworkConstants.UtranBand {
@@ -47778,7 +47794,7 @@
     method public String createAppSpecificSmsToken(android.app.PendingIntent);
     method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent);
     method public java.util.ArrayList<java.lang.String> divideMessage(String);
-    method @Deprecated public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent);
+    method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent);
     method @NonNull public android.os.Bundle getCarrierConfigValues();
     method public static android.telephony.SmsManager getDefault();
     method public static int getDefaultSmsSubscriptionId();
@@ -47788,7 +47804,7 @@
     method public int getSubscriptionId();
     method public void injectSmsPdu(byte[], String, android.app.PendingIntent);
     method public void sendDataMessage(String, String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
-    method @Deprecated public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent);
+    method public void sendMultimediaMessage(android.content.Context, android.net.Uri, String, android.os.Bundle, android.app.PendingIntent);
     method public void sendMultipartTextMessage(String, String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
     method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, long);
     method public void sendMultipartTextMessage(@NonNull String, @Nullable String, @NonNull java.util.List<java.lang.String>, @Nullable java.util.List<android.app.PendingIntent>, @Nullable java.util.List<android.app.PendingIntent>, @NonNull String, @Nullable String);
@@ -55744,7 +55760,7 @@
     field public int softInputMode;
     field @Deprecated public int systemUiVisibility;
     field public android.os.IBinder token;
-    field @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION, to="BASE_APPLICATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION, to="APPLICATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING, to="APPLICATION_STARTING"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION, to="DRAWN_APPLICATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL, to="APPLICATION_PANEL"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA, to="APPLICATION_MEDIA"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL, to="APPLICATION_SUB_PANEL"), @android.view.ViewDebug.IntToString(from=0x3ed, to="APPLICATION_ABOVE_SUB_PANEL"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG, to="APPLICATION_ATTACHED_DIALOG"), @android.view.ViewDebug.IntToString(from=0x3ec, to="APPLICATION_MEDIA_OVERLAY"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR, to="STATUS_BAR"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR, to="SEARCH_BAR"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_PHONE, to="PHONE"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, to="SYSTEM_ALERT"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_TOAST, to="TOAST"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, to="SYSTEM_OVERLAY"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE, to="PRIORITY_PHONE"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG, to="SYSTEM_DIALOG"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, to="KEYGUARD_DIALOG"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR, to="SYSTEM_ERROR"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD, to="INPUT_METHOD"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG, to="INPUT_METHOD_DIALOG"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_WALLPAPER, to="WALLPAPER"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, to="STATUS_BAR_PANEL"), @android.view.ViewDebug.IntToString(from=0x7df, to="SECURE_SYSTEM_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7e0, to="DRAG"), @android.view.ViewDebug.IntToString(from=0x7e1, to="STATUS_BAR_SUB_PANEL"), @android.view.ViewDebug.IntToString(from=0x7e2, to="POINTER"), @android.view.ViewDebug.IntToString(from=0x7e3, to="NAVIGATION_BAR"), @android.view.ViewDebug.IntToString(from=0x7e4, to="VOLUME_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7e5, to="BOOT_PROGRESS"), @android.view.ViewDebug.IntToString(from=0x7e6, to="INPUT_CONSUMER"), @android.view.ViewDebug.IntToString(from=0x7e7, to="DREAM"), @android.view.ViewDebug.IntToString(from=0x7e8, to="NAVIGATION_BAR_PANEL"), @android.view.ViewDebug.IntToString(from=0x7ea, to="DISPLAY_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7eb, to="MAGNIFICATION_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7f5, to="PRESENTATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION, to="PRIVATE_PRESENTATION"), @android.view.ViewDebug.IntToString(from=0x7ef, to="VOICE_INTERACTION"), @android.view.ViewDebug.IntToString(from=0x7f1, to="VOICE_INTERACTION_STARTING"), @android.view.ViewDebug.IntToString(from=0x7f2, to="DOCK_DIVIDER"), @android.view.ViewDebug.IntToString(from=0x7f3, to="QS_DIALOG"), @android.view.ViewDebug.IntToString(from=0x7f4, to="SCREENSHOT"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, to="APPLICATION_OVERLAY")}) public int type;
+    field @android.view.ViewDebug.ExportedProperty(mapping={@android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION, to="BASE_APPLICATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION, to="APPLICATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING, to="APPLICATION_STARTING"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION, to="DRAWN_APPLICATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL, to="APPLICATION_PANEL"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA, to="APPLICATION_MEDIA"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL, to="APPLICATION_SUB_PANEL"), @android.view.ViewDebug.IntToString(from=0x3ed, to="APPLICATION_ABOVE_SUB_PANEL"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG, to="APPLICATION_ATTACHED_DIALOG"), @android.view.ViewDebug.IntToString(from=0x3ec, to="APPLICATION_MEDIA_OVERLAY"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR, to="STATUS_BAR"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR, to="SEARCH_BAR"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_PHONE, to="PHONE"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, to="SYSTEM_ALERT"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_TOAST, to="TOAST"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, to="SYSTEM_OVERLAY"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE, to="PRIORITY_PHONE"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG, to="SYSTEM_DIALOG"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, to="KEYGUARD_DIALOG"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR, to="SYSTEM_ERROR"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD, to="INPUT_METHOD"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG, to="INPUT_METHOD_DIALOG"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_WALLPAPER, to="WALLPAPER"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, to="STATUS_BAR_PANEL"), @android.view.ViewDebug.IntToString(from=0x7df, to="SECURE_SYSTEM_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7e0, to="DRAG"), @android.view.ViewDebug.IntToString(from=0x7e1, to="STATUS_BAR_SUB_PANEL"), @android.view.ViewDebug.IntToString(from=0x7e2, to="POINTER"), @android.view.ViewDebug.IntToString(from=0x7e3, to="NAVIGATION_BAR"), @android.view.ViewDebug.IntToString(from=0x7e4, to="VOLUME_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7e5, to="BOOT_PROGRESS"), @android.view.ViewDebug.IntToString(from=0x7e6, to="INPUT_CONSUMER"), @android.view.ViewDebug.IntToString(from=0x7e8, to="NAVIGATION_BAR_PANEL"), @android.view.ViewDebug.IntToString(from=0x7ea, to="DISPLAY_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7eb, to="MAGNIFICATION_OVERLAY"), @android.view.ViewDebug.IntToString(from=0x7f5, to="PRESENTATION"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION, to="PRIVATE_PRESENTATION"), @android.view.ViewDebug.IntToString(from=0x7ef, to="VOICE_INTERACTION"), @android.view.ViewDebug.IntToString(from=0x7f1, to="VOICE_INTERACTION_STARTING"), @android.view.ViewDebug.IntToString(from=0x7f2, to="DOCK_DIVIDER"), @android.view.ViewDebug.IntToString(from=0x7f3, to="QS_DIALOG"), @android.view.ViewDebug.IntToString(from=0x7f4, to="SCREENSHOT"), @android.view.ViewDebug.IntToString(from=android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, to="APPLICATION_OVERLAY")}) public int type;
     field public float verticalMargin;
     field @android.view.ViewDebug.ExportedProperty public float verticalWeight;
     field public int windowAnimations;
@@ -55753,8 +55769,8 @@
   }
 
   public final class WindowMetrics {
-    ctor public WindowMetrics(@NonNull android.util.Size, @NonNull android.view.WindowInsets);
-    method @NonNull public android.util.Size getSize();
+    ctor public WindowMetrics(@NonNull android.graphics.Rect, @NonNull android.view.WindowInsets);
+    method @NonNull public android.graphics.Rect getBounds();
     method @NonNull public android.view.WindowInsets getWindowInsets();
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index a1fd0db..caa74bf 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -368,7 +368,6 @@
     method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(@NonNull String, int, int);
     field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
     field public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
-    field public static final String OPSTR_ACCESS_CALL_AUDIO = "android:access_call_audio";
     field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
     field public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
     field public static final String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot";
@@ -2030,10 +2029,10 @@
   }
 
   public static class PackageInstaller.Session implements java.io.Closeable {
-    method public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]);
+    method @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public void addFile(int, @NonNull String, long, @NonNull byte[], @Nullable byte[]);
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void commitTransferred(@NonNull android.content.IntentSender);
-    method @Nullable public android.content.pm.DataLoaderParams getDataLoaderParams();
-    method public void removeFile(int, @NonNull String);
+    method @Nullable @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public android.content.pm.DataLoaderParams getDataLoaderParams();
+    method @RequiresPermission("com.android.permission.USE_INSTALLER_V2") public void removeFile(int, @NonNull String);
   }
 
   public static class PackageInstaller.SessionInfo implements android.os.Parcelable {
@@ -2054,7 +2053,7 @@
   public static class PackageInstaller.SessionParams implements android.os.Parcelable {
     method @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public void setAllocateAggressive(boolean);
     method @Deprecated public void setAllowDowngrade(boolean);
-    method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams);
+    method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.USE_INSTALLER_V2"}) public void setDataLoaderParams(@NonNull android.content.pm.DataLoaderParams);
     method public void setDontKillApp(boolean);
     method public void setEnableRollback(boolean);
     method public void setEnableRollback(boolean, int);
@@ -3576,23 +3575,23 @@
 
   public static final class SoundTrigger.Keyphrase implements android.os.Parcelable {
     ctor public SoundTrigger.Keyphrase(int, int, @NonNull java.util.Locale, @NonNull String, @Nullable int[]);
+    method public int getId();
+    method @NonNull public java.util.Locale getLocale();
+    method public int getRecognitionModes();
+    method @NonNull public String getText();
+    method @NonNull public int[] getUsers();
     method @NonNull public static android.hardware.soundtrigger.SoundTrigger.Keyphrase readFromParcel(@NonNull android.os.Parcel);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.Keyphrase> CREATOR;
-    field public final int id;
-    field @NonNull public final java.util.Locale locale;
-    field public final int recognitionModes;
-    field @NonNull public final String text;
-    field @NonNull public final int[] users;
   }
 
   public static final class SoundTrigger.KeyphraseSoundModel extends android.hardware.soundtrigger.SoundTrigger.SoundModel implements android.os.Parcelable {
     ctor public SoundTrigger.KeyphraseSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], @Nullable android.hardware.soundtrigger.SoundTrigger.Keyphrase[], int);
     ctor public SoundTrigger.KeyphraseSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], @Nullable android.hardware.soundtrigger.SoundTrigger.Keyphrase[]);
+    method @NonNull public android.hardware.soundtrigger.SoundTrigger.Keyphrase[] getKeyphrases();
     method @NonNull public static android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel readFromParcel(@NonNull android.os.Parcel);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel> CREATOR;
-    field @NonNull public final android.hardware.soundtrigger.SoundTrigger.Keyphrase[] keyphrases;
   }
 
   public static final class SoundTrigger.ModelParamRange implements android.os.Parcelable {
@@ -3604,26 +3603,26 @@
 
   public static final class SoundTrigger.ModuleProperties implements android.os.Parcelable {
     method public int describeContents();
+    method public int getAudioCapabilities();
+    method @NonNull public String getDescription();
+    method public int getId();
+    method @NonNull public String getImplementor();
+    method public int getMaxBufferMillis();
+    method public int getMaxKeyphrases();
+    method public int getMaxSoundModels();
+    method public int getMaxUsers();
+    method public int getPowerConsumptionMw();
+    method public int getRecognitionModes();
+    method @NonNull public String getSupportedModelArch();
+    method @NonNull public java.util.UUID getUuid();
+    method public int getVersion();
+    method public boolean isCaptureTransitionSupported();
+    method public boolean isConcurrentCaptureSupported();
+    method public boolean isTriggerReturnedInEvent();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
     field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.ModuleProperties> CREATOR;
-    field public final int audioCapabilities;
-    field @NonNull public final String description;
-    field public final int id;
-    field @NonNull public final String implementor;
-    field public final int maxBufferMs;
-    field public final int maxKeyphrases;
-    field public final int maxSoundModels;
-    field public final int maxUsers;
-    field public final int powerConsumptionMw;
-    field public final int recognitionModes;
-    field public final boolean returnsTriggerInEvent;
-    field @NonNull public final String supportedModelArch;
-    field public final boolean supportsCaptureTransition;
-    field public final boolean supportsConcurrentCapture;
-    field @NonNull public final java.util.UUID uuid;
-    field public final int version;
   }
 
   public static class SoundTrigger.RecognitionEvent {
@@ -3634,13 +3633,13 @@
   }
 
   public static class SoundTrigger.SoundModel {
+    method @NonNull public byte[] getData();
+    method public int getType();
+    method @NonNull public java.util.UUID getUuid();
+    method @NonNull public java.util.UUID getVendorUuid();
+    method public int getVersion();
     field public static final int TYPE_GENERIC_SOUND = 1; // 0x1
     field public static final int TYPE_KEYPHRASE = 0; // 0x0
-    field @NonNull public final byte[] data;
-    field public final int type;
-    field @NonNull public final java.util.UUID uuid;
-    field @NonNull public final java.util.UUID vendorUuid;
-    field public final int version;
   }
 
 }
@@ -4179,11 +4178,11 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
     method public void clearAudioServerStateCallback();
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
-    method @IntRange(from=0) public int getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
+    method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies();
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups();
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDeviceAttributes> getDevicesForAttributes(@NonNull android.media.AudioAttributes);
-    method @IntRange(from=0) public int getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
+    method @IntRange(from=0) public long getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDeviceAttributes getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
@@ -4198,7 +4197,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int requestAudioFocus(@NonNull android.media.AudioFocusRequest, @Nullable android.media.audiopolicy.AudioPolicy);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) long);
     method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
@@ -4273,17 +4272,11 @@
   }
 
   public static class AudioTrack.TunerConfiguration {
+    ctor @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public AudioTrack.TunerConfiguration(@IntRange(from=1) int, @IntRange(from=1) int);
     method @IntRange(from=1) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getContentId();
     method @IntRange(from=1) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getSyncId();
   }
 
-  public static class AudioTrack.TunerConfiguration.Builder {
-    ctor public AudioTrack.TunerConfiguration.Builder();
-    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioTrack.TunerConfiguration build();
-    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioTrack.TunerConfiguration.Builder setContentId(@IntRange(from=1) int);
-    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioTrack.TunerConfiguration.Builder setSyncId(@IntRange(from=1) int);
-  }
-
   public class HwAudioSource {
     method public boolean isPlaying();
     method public void start();
@@ -6061,7 +6054,7 @@
     method public void release();
   }
 
-  public class InvalidPacketException extends java.lang.Exception {
+  public final class InvalidPacketException extends java.lang.Exception {
     ctor public InvalidPacketException(int);
     method public int getError();
     field public static final int ERROR_INVALID_IP_ADDRESS = -21; // 0xffffffeb
@@ -6211,14 +6204,14 @@
     method public void onRemoveKeepalivePacketFilter(int);
     method public void onSaveAcceptUnvalidated(boolean);
     method public void onSignalStrengthThresholdsUpdated(@NonNull int[]);
-    method public void onStartSocketKeepalive(int, int, @NonNull android.net.KeepalivePacketData);
+    method public void onStartSocketKeepalive(int, @IntRange(from=10, to=3600) int, @NonNull android.net.KeepalivePacketData);
     method public void onStopSocketKeepalive(int);
-    method public void onValidationStatus(int, @Nullable String);
+    method public void onValidationStatus(int, @Nullable android.net.Uri);
     method @NonNull public android.net.Network register();
-    method public void sendLinkProperties(@NonNull android.net.LinkProperties);
-    method public void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
-    method public void sendNetworkScore(int);
-    method public void sendSocketKeepaliveEvent(int, int);
+    method public final void sendLinkProperties(@NonNull android.net.LinkProperties);
+    method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
+    method public final void sendNetworkScore(@IntRange(from=0, to=99) int);
+    method public final void sendSocketKeepaliveEvent(int, int);
     method public void setConnected();
     method public void unregister();
     field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
@@ -6236,7 +6229,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR;
   }
 
-  public static class NetworkAgentConfig.Builder {
+  public static final class NetworkAgentConfig.Builder {
     ctor public NetworkAgentConfig.Builder();
     method @NonNull public android.net.NetworkAgentConfig build();
     method @NonNull public android.net.NetworkAgentConfig.Builder setExplicitlySelected(boolean);
@@ -6255,7 +6248,7 @@
     field public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24; // 0x18
   }
 
-  public static class NetworkCapabilities.Builder {
+  public static final class NetworkCapabilities.Builder {
     ctor public NetworkCapabilities.Builder();
     ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
     method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
@@ -6304,7 +6297,6 @@
   public class NetworkRequest implements android.os.Parcelable {
     method @Nullable public String getRequestorPackageName();
     method public int getRequestorUid();
-    method public boolean satisfiedBy(@Nullable android.net.NetworkCapabilities);
   }
 
   public static class NetworkRequest.Builder {
@@ -8395,12 +8387,12 @@
 
   public class RecoverySystem {
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void cancelScheduledUpdate(android.content.Context) throws java.io.IOException;
-    method @RequiresPermission(android.Manifest.permission.RECOVERY) public static boolean clearPrepareForUnattendedUpdate(@NonNull android.content.Context) throws java.io.IOException;
+    method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void clearPrepareForUnattendedUpdate(@NonNull android.content.Context) throws java.io.IOException;
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void installPackage(android.content.Context, java.io.File, boolean) throws java.io.IOException;
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void prepareForUnattendedUpdate(@NonNull android.content.Context, @NonNull String, @Nullable android.content.IntentSender) throws java.io.IOException;
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener, android.os.Handler) throws java.io.IOException;
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener) throws java.io.IOException;
-    method @RequiresPermission(android.Manifest.permission.RECOVERY) public static boolean rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException;
+    method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException;
     method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException;
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException;
     method public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException;
@@ -12713,9 +12705,6 @@
 
   public final class ContentCaptureManager {
     method public boolean isContentCaptureFeatureEnabled();
-  }
-
-  public abstract class ContentCaptureSession implements java.lang.AutoCloseable {
     field public static final int NO_SESSION_ID = 0; // 0x0
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 0bd8a19..ba6feed 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -535,6 +535,7 @@
     method public void setWindowingMode(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ACTIVITY_TYPE_ASSISTANT = 4; // 0x4
+    field public static final int ACTIVITY_TYPE_DREAM = 5; // 0x5
     field public static final int ACTIVITY_TYPE_HOME = 2; // 0x2
     field public static final int ACTIVITY_TYPE_RECENTS = 3; // 0x3
     field public static final int ACTIVITY_TYPE_STANDARD = 1; // 0x1
@@ -1833,7 +1834,7 @@
     field public static final int TRANSPORT_TEST = 7; // 0x7
   }
 
-  public static class NetworkCapabilities.Builder {
+  public static final class NetworkCapabilities.Builder {
     ctor public NetworkCapabilities.Builder();
     ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities);
     method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int);
diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java
index 59544a9..ca1d598 100644
--- a/cmds/content/src/com/android/commands/content/Content.java
+++ b/cmds/content/src/com/android/commands/content/Content.java
@@ -32,8 +32,11 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.Pair;
 
 import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * This class is a command line utility for manipulating content. A client
@@ -72,7 +75,7 @@
             "usage: adb shell content [subcommand] [options]\n"
                     + "\n"
                     + "usage: adb shell content insert --uri <URI> [--user <USER_ID>]"
-                    + " --bind <BINDING> [--bind <BINDING>...]\n"
+                    + " --bind <BINDING> [--bind <BINDING>...] [--extra <BINDING>...]\n"
                     + "  <URI> a content provider URI.\n"
                     + "  <BINDING> binds a typed value to a column and is formatted:\n"
                     + "  <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n"
@@ -84,7 +87,8 @@
                     + "  adb shell content insert --uri content://settings/secure --bind name:s:new_setting"
                     + " --bind value:s:new_value\n"
                     + "\n"
-                    + "usage: adb shell content update --uri <URI> [--user <USER_ID>] [--where <WHERE>]\n"
+                    + "usage: adb shell content update --uri <URI> [--user <USER_ID>]"
+                    + " [--where <WHERE>] [--extra <BINDING>...]\n"
                     + "  <WHERE> is a SQL style where clause in quotes (You have to escape single quotes"
                     + " - see example below).\n"
                     + "  Example:\n"
@@ -93,14 +97,15 @@
                     + " value:s:newer_value --where \"name=\'new_setting\'\"\n"
                     + "\n"
                     + "usage: adb shell content delete --uri <URI> [--user <USER_ID>] --bind <BINDING>"
-                    + " [--bind <BINDING>...] [--where <WHERE>]\n"
+                    + " [--bind <BINDING>...] [--where <WHERE>] [--extra <BINDING>...]\n"
                     + "  Example:\n"
                     + "  # Remove \"new_setting\" secure setting.\n"
                     + "  adb shell content delete --uri content://settings/secure "
                     + "--where \"name=\'new_setting\'\"\n"
                     + "\n"
                     + "usage: adb shell content query --uri <URI> [--user <USER_ID>]"
-                    + " [--projection <PROJECTION>] [--where <WHERE>] [--sort <SORT_ORDER>]\n"
+                    + " [--projection <PROJECTION>] [--where <WHERE>] [--sort <SORT_ORDER>]"
+                    + " [--extra <BINDING>...]\n"
                     + "  <PROJECTION> is a list of colon separated column names and is formatted:\n"
                     + "  <COLUMN_NAME>[:<COLUMN_NAME>...]\n"
                     + "  <SORT_ORDER> is the order in which rows in the result should be sorted.\n"
@@ -196,6 +201,7 @@
             Uri uri = null;
             int userId = UserHandle.USER_SYSTEM;
             ContentValues values = new ContentValues();
+            Bundle extras = new Bundle();
             for (String argument; (argument = mTokenizer.nextArg()) != null;) {
                 if (ARGUMENT_URI.equals(argument)) {
                     uri = Uri.parse(argumentValueRequired(argument));
@@ -203,6 +209,8 @@
                     userId = Integer.parseInt(argumentValueRequired(argument));
                 } else if (ARGUMENT_BIND.equals(argument)) {
                     parseBindValue(values);
+                } else if (ARGUMENT_EXTRA.equals(argument)) {
+                    parseBindValue(extras);
                 } else {
                     throw new IllegalArgumentException("Unsupported argument: " + argument);
                 }
@@ -215,20 +223,23 @@
                 throw new IllegalArgumentException("Bindings not specified."
                         + " Did you specify --bind argument(s)?");
             }
-            return new InsertCommand(uri, userId, values);
+            return new InsertCommand(uri, userId, values, extras);
         }
 
         private DeleteCommand parseDeleteCommand() {
             Uri uri = null;
             int userId = UserHandle.USER_SYSTEM;
-            String where = null;
+            Bundle extras = new Bundle();
             for (String argument; (argument = mTokenizer.nextArg())!= null;) {
                 if (ARGUMENT_URI.equals(argument)) {
                     uri = Uri.parse(argumentValueRequired(argument));
                 } else if (ARGUMENT_USER.equals(argument)) {
                     userId = Integer.parseInt(argumentValueRequired(argument));
                 } else if (ARGUMENT_WHERE.equals(argument)) {
-                    where = argumentValueRequired(argument);
+                    extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION,
+                            argumentValueRequired(argument));
+                } else if (ARGUMENT_EXTRA.equals(argument)) {
+                    parseBindValue(extras);
                 } else {
                     throw new IllegalArgumentException("Unsupported argument: " + argument);
                 }
@@ -237,23 +248,26 @@
                 throw new IllegalArgumentException("Content provider URI not specified."
                         + " Did you specify --uri argument?");
             }
-            return new DeleteCommand(uri, userId, where);
+            return new DeleteCommand(uri, userId, extras);
         }
 
         private UpdateCommand parseUpdateCommand() {
             Uri uri = null;
             int userId = UserHandle.USER_SYSTEM;
-            String where = null;
             ContentValues values = new ContentValues();
+            Bundle extras = new Bundle();
             for (String argument; (argument = mTokenizer.nextArg())!= null;) {
                 if (ARGUMENT_URI.equals(argument)) {
                     uri = Uri.parse(argumentValueRequired(argument));
                 } else if (ARGUMENT_USER.equals(argument)) {
                     userId = Integer.parseInt(argumentValueRequired(argument));
                 } else if (ARGUMENT_WHERE.equals(argument)) {
-                    where = argumentValueRequired(argument);
+                    extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION,
+                            argumentValueRequired(argument));
                 } else if (ARGUMENT_BIND.equals(argument)) {
                     parseBindValue(values);
+                } else if (ARGUMENT_EXTRA.equals(argument)) {
+                    parseBindValue(extras);
                 } else {
                     throw new IllegalArgumentException("Unsupported argument: " + argument);
                 }
@@ -266,7 +280,7 @@
                 throw new IllegalArgumentException("Bindings not specified."
                         + " Did you specify --bind argument(s)?");
             }
-            return new UpdateCommand(uri, userId, values, where);
+            return new UpdateCommand(uri, userId, values, extras);
         }
 
         public CallCommand parseCallCommand() {
@@ -274,7 +288,7 @@
             int userId = UserHandle.USER_SYSTEM;
             String arg = null;
             Uri uri = null;
-            ContentValues values = new ContentValues();
+            Bundle extras = new Bundle();
             for (String argument; (argument = mTokenizer.nextArg())!= null;) {
                 if (ARGUMENT_URI.equals(argument)) {
                     uri = Uri.parse(argumentValueRequired(argument));
@@ -285,11 +299,10 @@
                 } else if (ARGUMENT_ARG.equals(argument)) {
                     arg = argumentValueRequired(argument);
                 } else if (ARGUMENT_EXTRA.equals(argument)) {
-                    parseBindValue(values);
+                    parseBindValue(extras);
                 } else {
                     throw new IllegalArgumentException("Unsupported argument: " + argument);
                 }
-
             }
             if (uri == null) {
                 throw new IllegalArgumentException("Content provider URI not specified."
@@ -298,7 +311,7 @@
             if (method == null) {
                 throw new IllegalArgumentException("Content provider method not specified.");
             }
-            return new CallCommand(uri, userId, method, arg, values);
+            return new CallCommand(uri, userId, method, arg, extras);
         }
 
         private GetTypeCommand parseGetTypeCommand() {
@@ -363,19 +376,22 @@
             Uri uri = null;
             int userId = UserHandle.USER_SYSTEM;
             String[] projection = null;
-            String sort = null;
-            String where = null;
+            Bundle extras = new Bundle();
             for (String argument; (argument = mTokenizer.nextArg())!= null;) {
                 if (ARGUMENT_URI.equals(argument)) {
                     uri = Uri.parse(argumentValueRequired(argument));
                 } else if (ARGUMENT_USER.equals(argument)) {
                     userId = Integer.parseInt(argumentValueRequired(argument));
                 } else if (ARGUMENT_WHERE.equals(argument)) {
-                    where = argumentValueRequired(argument);
+                    extras.putString(ContentResolver.QUERY_ARG_SQL_SELECTION,
+                            argumentValueRequired(argument));
                 } else if (ARGUMENT_SORT.equals(argument)) {
-                    sort = argumentValueRequired(argument);
+                    extras.putString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER,
+                            argumentValueRequired(argument));
                 } else if (ARGUMENT_PROJECTION.equals(argument)) {
                     projection = argumentValueRequired(argument).split("[\\s]*:[\\s]*");
+                } else if (ARGUMENT_EXTRA.equals(argument)) {
+                    parseBindValue(extras);
                 } else {
                     throw new IllegalArgumentException("Unsupported argument: " + argument);
                 }
@@ -384,40 +400,76 @@
                 throw new IllegalArgumentException("Content provider URI not specified."
                         + " Did you specify --uri argument?");
             }
-            return new QueryCommand(uri, userId, projection, where, sort);
+            return new QueryCommand(uri, userId, projection, extras);
         }
 
-        private void parseBindValue(ContentValues values) {
+        private List<String> splitWithEscaping(String argument, char splitChar) {
+            final List<String> res = new ArrayList<>();
+            final StringBuilder cur = new StringBuilder();
+            for (int i = 0; i < argument.length(); i++) {
+                char c = argument.charAt(i);
+                if (c == '\\') {
+                    if (++i == argument.length()) {
+                        throw new IllegalArgumentException("Invalid escaping");
+                    } else {
+                        // Skip escaping char and insert next
+                        c = argument.charAt(i);
+                        cur.append(c);
+                    }
+                } else if (c == splitChar) {
+                    // Splitting char means next string
+                    res.add(cur.toString());
+                    cur.setLength(0);
+                } else {
+                    // Copy non-escaping and non-splitting char
+                    cur.append(c);
+                }
+            }
+            res.add(cur.toString());
+            return res;
+        }
+
+        private Pair<String, Object> parseBindValue() {
             String argument = mTokenizer.nextArg();
             if (TextUtils.isEmpty(argument)) {
                 throw new IllegalArgumentException("Binding not well formed: " + argument);
             }
-            final int firstColonIndex = argument.indexOf(COLON);
-            if (firstColonIndex < 0) {
+            final List<String> split = splitWithEscaping(argument, COLON.charAt(0));
+            if (split.size() != 3) {
                 throw new IllegalArgumentException("Binding not well formed: " + argument);
             }
-            final int secondColonIndex = argument.indexOf(COLON, firstColonIndex + 1);
-            if (secondColonIndex < 0) {
-                throw new IllegalArgumentException("Binding not well formed: " + argument);
-            }
-            String column = argument.substring(0, firstColonIndex);
-            String type = argument.substring(firstColonIndex + 1, secondColonIndex);
-            String value = argument.substring(secondColonIndex + 1);
+            String column = split.get(0);
+            String type = split.get(1);
+            String value = split.get(2);
             if (TYPE_STRING.equals(type)) {
-                values.put(column, value);
+                return Pair.create(column, value);
             } else if (TYPE_BOOLEAN.equalsIgnoreCase(type)) {
-                values.put(column, Boolean.parseBoolean(value));
-            } else if (TYPE_INTEGER.equalsIgnoreCase(type) || TYPE_LONG.equalsIgnoreCase(type)) {
-                values.put(column, Long.parseLong(value));
-            } else if (TYPE_FLOAT.equalsIgnoreCase(type) || TYPE_DOUBLE.equalsIgnoreCase(type)) {
-                values.put(column, Double.parseDouble(value));
+                return Pair.create(column, Boolean.parseBoolean(value));
+            } else if (TYPE_INTEGER.equalsIgnoreCase(type)) {
+                return Pair.create(column, Integer.parseInt(value));
+            } else if (TYPE_LONG.equalsIgnoreCase(type)) {
+                return Pair.create(column, Long.parseLong(value));
+            } else if (TYPE_FLOAT.equalsIgnoreCase(type)) {
+                return Pair.create(column, Float.parseFloat(value));
+            } else if (TYPE_DOUBLE.equalsIgnoreCase(type)) {
+                return Pair.create(column, Double.parseDouble(value));
             } else if (TYPE_NULL.equalsIgnoreCase(type)) {
-                values.putNull(column);
+                return Pair.create(column, null);
             } else {
                 throw new IllegalArgumentException("Unsupported type: " + type);
             }
         }
 
+        private void parseBindValue(ContentValues values) {
+            final Pair<String, Object> columnValue = parseBindValue();
+            values.putObject(columnValue.first, columnValue.second);
+        }
+
+        private void parseBindValue(Bundle extras) {
+            final Pair<String, Object> columnValue = parseBindValue();
+            extras.putObject(columnValue.first, columnValue.second);
+        }
+
         private String argumentValueRequired(String argument) {
             String value = mTokenizer.nextArg();
             if (TextUtils.isEmpty(value) || value.startsWith(ARGUMENT_PREFIX)) {
@@ -500,60 +552,43 @@
 
     private static class InsertCommand extends Command {
         final ContentValues mContentValues;
+        final Bundle mExtras;
 
-        public InsertCommand(Uri uri, int userId, ContentValues contentValues) {
+        public InsertCommand(Uri uri, int userId, ContentValues contentValues, Bundle extras) {
             super(uri, userId);
             mContentValues = contentValues;
+            mExtras = extras;
         }
 
         @Override
         public void onExecute(IContentProvider provider) throws Exception {
-            provider.insert(resolveCallingPackage(), null, mUri, mContentValues, null);
+            provider.insert(resolveCallingPackage(), null, mUri, mContentValues, mExtras);
         }
     }
 
     private static class DeleteCommand extends Command {
-        final String mWhere;
+        final Bundle mExtras;
 
-        public DeleteCommand(Uri uri, int userId, String where) {
+        public DeleteCommand(Uri uri, int userId, Bundle extras) {
             super(uri, userId);
-            mWhere = where;
+            mExtras = extras;
         }
 
         @Override
         public void onExecute(IContentProvider provider) throws Exception {
-            provider.delete(resolveCallingPackage(), null, mUri,
-                    ContentResolver.createSqlQueryBundle(mWhere, null));
+            provider.delete(resolveCallingPackage(), null, mUri, mExtras);
         }
     }
 
     private static class CallCommand extends Command {
         final String mMethod, mArg;
-        Bundle mExtras = null;
+        final Bundle mExtras;
 
-        public CallCommand(Uri uri, int userId, String method, String arg, ContentValues values) {
+        public CallCommand(Uri uri, int userId, String method, String arg, Bundle extras) {
             super(uri, userId);
             mMethod = method;
             mArg = arg;
-            if (values != null) {
-                mExtras = new Bundle();
-                for (String key : values.keySet()) {
-                    final Object val = values.get(key);
-                    if (val instanceof String) {
-                        mExtras.putString(key, (String) val);
-                    } else if (val instanceof Float) {
-                        mExtras.putFloat(key, (Float) val);
-                    } else if (val instanceof Double) {
-                        mExtras.putDouble(key, (Double) val);
-                    } else if (val instanceof Boolean) {
-                        mExtras.putBoolean(key, (Boolean) val);
-                    } else if (val instanceof Integer) {
-                        mExtras.putInt(key, (Integer) val);
-                    } else if (val instanceof Long) {
-                        mExtras.putLong(key, (Long) val);
-                    }
-                }
-            }
+            mExtras = extras;
         }
 
         @Override
@@ -604,21 +639,20 @@
         }
     }
 
-    private static class QueryCommand extends DeleteCommand {
+    private static class QueryCommand extends Command {
         final String[] mProjection;
-        final String mSortOrder;
+        final Bundle mExtras;
 
-        public QueryCommand(
-                Uri uri, int userId, String[] projection, String where, String sortOrder) {
-            super(uri, userId, where);
+        public QueryCommand(Uri uri, int userId, String[] projection, Bundle extras) {
+            super(uri, userId);
             mProjection = projection;
-            mSortOrder = sortOrder;
+            mExtras = extras;
         }
 
         @Override
         public void onExecute(IContentProvider provider) throws Exception {
             Cursor cursor = provider.query(resolveCallingPackage(), null, mUri, mProjection,
-                    ContentResolver.createSqlQueryBundle(mWhere, null, mSortOrder), null);
+                    mExtras, null);
             if (cursor == null) {
                 System.out.println("No result found.");
                 return;
@@ -670,18 +704,19 @@
         }
     }
 
-    private static class UpdateCommand extends InsertCommand {
-        final String mWhere;
+    private static class UpdateCommand extends Command {
+        final ContentValues mValues;
+        final Bundle mExtras;
 
-        public UpdateCommand(Uri uri, int userId, ContentValues contentValues, String where) {
-            super(uri, userId, contentValues);
-            mWhere = where;
+        public UpdateCommand(Uri uri, int userId, ContentValues values, Bundle extras) {
+            super(uri, userId);
+            mValues = values;
+            mExtras = extras;
         }
 
         @Override
         public void onExecute(IContentProvider provider) throws Exception {
-            provider.update(resolveCallingPackage(), null, mUri, mContentValues,
-                    ContentResolver.createSqlQueryBundle(mWhere, null));
+            provider.update(resolveCallingPackage(), null, mUri, mValues, mExtras);
         }
     }
 
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 4529dff..45f21ae 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -97,6 +97,7 @@
         "src/stats_log_util.cpp",
         "src/statscompanion_util.cpp",
         "src/statsd_config.proto",
+        "src/statsd_metadata.proto",
         "src/StatsLogProcessor.cpp",
         "src/StatsService.cpp",
         "src/storage/StorageManager.cpp",
@@ -378,55 +379,55 @@
 // statsd micro benchmark
 //#############################
 
-//cc_benchmark {
-//    name: "statsd_benchmark",
-//    defaults: ["statsd_defaults"],
-//
-//    srcs: [
-//        // atom_field_options.proto needs field_options.proto, but that is
-//        // not included in libprotobuf-cpp-lite, so compile it here.
-//        ":libprotobuf-internal-protos",
-//
-//        "benchmark/duration_metric_benchmark.cpp",
-//        "benchmark/filter_value_benchmark.cpp",
-//        "benchmark/get_dimensions_for_condition_benchmark.cpp",
-//        "benchmark/hello_world_benchmark.cpp",
-//        "benchmark/log_event_benchmark.cpp",
-//        "benchmark/main.cpp",
-//        "benchmark/metric_util.cpp",
-//        "benchmark/stats_write_benchmark.cpp",
-//        "src/atom_field_options.proto",
-//        "src/atoms.proto",
-//        "src/stats_log.proto",
-//    ],
-//
-//    proto: {
-//        type: "lite",
-//        include_dirs: ["external/protobuf/src"],
-//    },
-//
-//    cflags: [
-//        "-Wall",
-//        "-Werror",
-//        "-Wno-unused-parameter",
-//        "-Wno-unused-variable",
-//        "-Wno-unused-function",
-//
-//        // Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
-//        "-Wno-varargs",
-//    ],
-//
-//    static_libs: [
-//        "libplatformprotos",
-//    ],
-//
-//    shared_libs: [
-//        "libgtest_prod",
-//        "libprotobuf-cpp-lite",
-//        "libstatslog",
-//        "libstatssocket",
-//    ],
-//}
+cc_benchmark {
+    name: "statsd_benchmark",
+    defaults: ["statsd_defaults"],
+
+    srcs: [
+        // atom_field_options.proto needs field_options.proto, but that is
+        // not included in libprotobuf-cpp-lite, so compile it here.
+        ":libprotobuf-internal-protos",
+
+        "benchmark/duration_metric_benchmark.cpp",
+        "benchmark/filter_value_benchmark.cpp",
+        "benchmark/get_dimensions_for_condition_benchmark.cpp",
+        "benchmark/hello_world_benchmark.cpp",
+        "benchmark/log_event_benchmark.cpp",
+        "benchmark/main.cpp",
+        "benchmark/metric_util.cpp",
+        "benchmark/stats_write_benchmark.cpp",
+        "src/atom_field_options.proto",
+        "src/atoms.proto",
+        "src/stats_log.proto",
+    ],
+
+    proto: {
+        type: "lite",
+        include_dirs: ["external/protobuf/src"],
+    },
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wno-unused-variable",
+        "-Wno-unused-function",
+
+        // Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
+        "-Wno-varargs",
+    ],
+
+    static_libs: [
+        "libplatformprotos",
+        "libstatssocket_private",
+    ],
+
+    shared_libs: [
+        "libgtest_prod",
+        "libprotobuf-cpp-lite",
+        "libstatslog",
+    ],
+}
 
 // ====  java proto device library (for test only)  ==============================
 java_library {
diff --git a/cmds/statsd/benchmark/duration_metric_benchmark.cpp b/cmds/statsd/benchmark/duration_metric_benchmark.cpp
index 2631009..2d315d9 100644
--- a/cmds/statsd/benchmark/duration_metric_benchmark.cpp
+++ b/cmds/statsd/benchmark/duration_metric_benchmark.cpp
@@ -137,77 +137,74 @@
     int64_t bucketSizeNs =
             TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
 
-
-    std::vector<AttributionNodeInternal> attributions1 = {
-            CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-            CreateAttribution(222, "GMSCoreModule2")};
-
-    std::vector<AttributionNodeInternal> attributions2 = {
-            CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
-            CreateAttribution(555, "GMSCoreModule2")};
-
     std::vector<std::unique_ptr<LogEvent>> events;
 
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + 11));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                   bucketStartTimeNs + 40));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 11,
+                                                   android::view::DISPLAY_STATE_OFF));
+    events.push_back(
+            CreateScreenStateChangedEvent(bucketStartTimeNs + 40, android::view::DISPLAY_STATE_ON));
 
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + 102));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                   bucketStartTimeNs + 450));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 102,
+                                                   android::view::DISPLAY_STATE_OFF));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450,
+                                                   android::view::DISPLAY_STATE_ON));
 
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + 650));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                   bucketStartTimeNs + bucketSizeNs + 100));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 650,
+                                                   android::view::DISPLAY_STATE_OFF));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100,
+                                                   android::view::DISPLAY_STATE_ON));
 
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + bucketSizeNs + 640));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                   bucketStartTimeNs + bucketSizeNs + 650));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 640,
+                                                   android::view::DISPLAY_STATE_OFF));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 650,
+                                                   android::view::DISPLAY_STATE_ON));
 
-    events.push_back(CreateStartScheduledJobEvent(
-            {CreateAttribution(9999, "")}, "job0", bucketStartTimeNs + 2));
-    events.push_back(CreateFinishScheduledJobEvent(
-            {CreateAttribution(9999, "")}, "job0",bucketStartTimeNs + 101));
+    vector<int> attributionUids1 = {9999};
+    vector<string> attributionTags1 = {""};
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 2, attributionUids1,
+                                                  attributionTags1, "job0"));
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 101, attributionUids1,
+                                                   attributionTags1, "job0"));
 
-    events.push_back(CreateStartScheduledJobEvent(
-            {CreateAttribution(9999, "")}, "job2", bucketStartTimeNs + 201));
-    events.push_back(CreateFinishScheduledJobEvent(
-            {CreateAttribution(9999, "")}, "job2",bucketStartTimeNs + 500));
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids1,
+                                                  attributionTags1, "job2"));
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids1,
+                                                   attributionTags1, "job2"));
 
-    events.push_back(CreateStartScheduledJobEvent(
-            {CreateAttribution(8888, "")}, "job2", bucketStartTimeNs + 600));
-    events.push_back(CreateFinishScheduledJobEvent(
-            {CreateAttribution(8888, "")}, "job2",bucketStartTimeNs + bucketSizeNs + 850));
+    vector<int> attributionUids2 = {8888};
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 600, attributionUids2,
+                                                  attributionTags1, "job2"));
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 850,
+                                                   attributionUids2, attributionTags1, "job2"));
 
-    events.push_back(CreateStartScheduledJobEvent(
-            {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 600));
-    events.push_back(CreateFinishScheduledJobEvent(
-            {CreateAttribution(8888, "")}, "job1", bucketStartTimeNs + bucketSizeNs + 900));
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 600,
+                                                  attributionUids2, attributionTags1, "job1"));
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900,
+                                                   attributionUids2, attributionTags1, "job1"));
 
-    events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
-                                          bucketStartTimeNs + 10));
-    events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-                                        bucketStartTimeNs + 50));
+    vector<int> attributionUids3 = {111, 222, 222};
+    vector<string> attributionTags3 = {"App1", "GMSCoreModule1", "GMSCoreModule2"};
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 10, attributionUids3,
+                                          attributionTags3, "ReadEmail"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 50, attributionUids3, attributionTags3,
+                                        "ReadEmail"));
 
-    events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
-                                          bucketStartTimeNs + 200));
-    events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-                                        bucketStartTimeNs + bucketSizeNs + 300));
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 200, attributionUids3,
+                                          attributionTags3, "ReadEmail"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 300, attributionUids3,
+                                        attributionTags3, "ReadEmail"));
 
-    events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc",
-                                          bucketStartTimeNs + 400));
-    events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
-                                        bucketStartTimeNs + bucketSizeNs - 1));
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids3,
+                                          attributionTags3, "ReadDoc"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids3,
+                                        attributionTags3, "ReadDoc"));
 
-    events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
-                                          bucketStartTimeNs + 401));
-    events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
-                                        bucketStartTimeNs + bucketSizeNs + 700));
-
+    vector<int> attributionUids4 = {333, 222, 555};
+    vector<string> attributionTags4 = {"App2", "GMSCoreModule1", "GMSCoreModule2"};
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 401, attributionUids4,
+                                          attributionTags4, "ReadEmail"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids4,
+                                        attributionTags4, "ReadEmail"));
     sortLogEventsByTimestamp(&events);
 
     while (state.KeepRunning()) {
@@ -230,78 +227,75 @@
     int64_t bucketSizeNs =
             TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
 
-    std::vector<AttributionNodeInternal> attributions1 = {
-            CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-            CreateAttribution(222, "GMSCoreModule2")};
-
-    std::vector<AttributionNodeInternal> attributions2 = {
-            CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
-            CreateAttribution(555, "GMSCoreModule2")};
-
-    std::vector<AttributionNodeInternal> attributions3 = {
-            CreateAttribution(444, "App3"), CreateAttribution(222, "GMSCoreModule1"),
-            CreateAttribution(555, "GMSCoreModule2")};
-
     std::vector<std::unique_ptr<LogEvent>> events;
 
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + 55));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                   bucketStartTimeNs + 120));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + 121));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                   bucketStartTimeNs + 450));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 55,
+                                                   android::view::DISPLAY_STATE_OFF));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 120,
+                                                   android::view::DISPLAY_STATE_ON));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 121,
+                                                   android::view::DISPLAY_STATE_OFF));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 450,
+                                                   android::view::DISPLAY_STATE_ON));
 
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
-                                                   bucketStartTimeNs + 501));
-    events.push_back(CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
-                                                   bucketStartTimeNs + bucketSizeNs + 100));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + 501,
+                                                   android::view::DISPLAY_STATE_OFF));
+    events.push_back(CreateScreenStateChangedEvent(bucketStartTimeNs + bucketSizeNs + 100,
+                                                   android::view::DISPLAY_STATE_ON));
 
-    events.push_back(CreateStartScheduledJobEvent(
-            {CreateAttribution(111, "App1")}, "job1", bucketStartTimeNs + 1));
-    events.push_back(CreateFinishScheduledJobEvent(
-            {CreateAttribution(111, "App1")}, "job1",bucketStartTimeNs + 101));
+    vector<int> attributionUids1 = {111};
+    vector<string> attributionTags1 = {"App1"};
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 1, attributionUids1,
+                                                  attributionTags1, "job1"));
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 101, attributionUids1,
+                                                   attributionTags1, "job1"));
 
-    events.push_back(CreateStartScheduledJobEvent(
-            {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 201));
-    events.push_back(CreateFinishScheduledJobEvent(
-            {CreateAttribution(333, "App2")}, "job2",bucketStartTimeNs + 500));
-    events.push_back(CreateStartScheduledJobEvent(
-            {CreateAttribution(333, "App2")}, "job2", bucketStartTimeNs + 600));
-    events.push_back(CreateFinishScheduledJobEvent(
-            {CreateAttribution(333, "App2")}, "job2",
-            bucketStartTimeNs + bucketSizeNs + 850));
+    vector<int> attributionUids2 = {333};
+    vector<string> attributionTags2 = {"App2"};
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 201, attributionUids2,
+                                                  attributionTags2, "job2"));
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 500, attributionUids2,
+                                                   attributionTags2, "job2"));
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 600, attributionUids2,
+                                                  attributionTags2, "job2"));
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 850,
+                                                   attributionUids2, attributionTags2, "job2"));
 
-    events.push_back(
-        CreateStartScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
-                                     bucketStartTimeNs + bucketSizeNs - 2));
-    events.push_back(
-        CreateFinishScheduledJobEvent({CreateAttribution(444, "App3")}, "job3",
-                                      bucketStartTimeNs + bucketSizeNs + 900));
+    vector<int> attributionUids3 = {444};
+    vector<string> attributionTags3 = {"App3"};
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + bucketSizeNs - 2,
+                                                  attributionUids3, attributionTags3, "job3"));
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + bucketSizeNs + 900,
+                                                   attributionUids3, attributionTags3, "job3"));
 
-    events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail",
-                                          bucketStartTimeNs + 50));
-    events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
-                                        bucketStartTimeNs + 110));
+    vector<int> attributionUids4 = {111, 222, 222};
+    vector<string> attributionTags4 = {"App1", "GMSCoreModule1", "GMSCoreModule2"};
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 50, attributionUids4,
+                                          attributionTags4, "ReadEmail"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 110, attributionUids4, attributionTags4,
+                                        "ReadEmail"));
 
-    events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail",
-                                          bucketStartTimeNs + 300));
-    events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
-                                        bucketStartTimeNs + bucketSizeNs + 700));
-    events.push_back(CreateSyncStartEvent(attributions2, "ReadDoc",
-                                          bucketStartTimeNs + 400));
-    events.push_back(CreateSyncEndEvent(attributions2, "ReadDoc",
-                                        bucketStartTimeNs + bucketSizeNs - 1));
+    vector<int> attributionUids5 = {333, 222, 555};
+    vector<string> attributionTags5 = {"App2", "GMSCoreModule1", "GMSCoreModule2"};
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 300, attributionUids5,
+                                          attributionTags5, "ReadEmail"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids5,
+                                        attributionTags5, "ReadEmail"));
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 400, attributionUids5,
+                                          attributionTags5, "ReadDoc"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids5,
+                                        attributionTags5, "ReadDoc"));
 
-    events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
-                                          bucketStartTimeNs + 550));
-    events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
-                                        bucketStartTimeNs + 800));
-    events.push_back(CreateSyncStartEvent(attributions3, "ReadDoc",
-                                          bucketStartTimeNs + bucketSizeNs - 1));
-    events.push_back(CreateSyncEndEvent(attributions3, "ReadDoc",
-                                        bucketStartTimeNs + bucketSizeNs + 700));
+    vector<int> attributionUids6 = {444, 222, 555};
+    vector<string> attributionTags6 = {"App3", "GMSCoreModule1", "GMSCoreModule2"};
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + 550, attributionUids6,
+                                          attributionTags6, "ReadDoc"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + 800, attributionUids6, attributionTags6,
+                                        "ReadDoc"));
+    events.push_back(CreateSyncStartEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids6,
+                                          attributionTags6, "ReadDoc"));
+    events.push_back(CreateSyncEndEvent(bucketStartTimeNs + bucketSizeNs + 700, attributionUids6,
+                                        attributionTags6, "ReadDoc"));
     sortLogEventsByTimestamp(&events);
 
     while (state.KeepRunning()) {
diff --git a/cmds/statsd/benchmark/filter_value_benchmark.cpp b/cmds/statsd/benchmark/filter_value_benchmark.cpp
index cfe477d..28bf21a 100644
--- a/cmds/statsd/benchmark/filter_value_benchmark.cpp
+++ b/cmds/statsd/benchmark/filter_value_benchmark.cpp
@@ -19,6 +19,7 @@
 #include "HashableDimensionKey.h"
 #include "logd/LogEvent.h"
 #include "stats_log_util.h"
+#include "stats_event.h"
 
 namespace android {
 namespace os {
@@ -26,17 +27,31 @@
 
 using std::vector;
 
-static void createLogEventAndMatcher(LogEvent* event, FieldMatcher *field_matcher) {
-    AttributionNodeInternal node;
-    node.set_uid(100);
-    node.set_tag("LOCATION");
+static void createLogEventAndMatcher(LogEvent* event, FieldMatcher* field_matcher) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, 1);
+    AStatsEvent_overwriteTimestamp(statsEvent, 100000);
 
-    std::vector<AttributionNodeInternal> nodes = {node, node};
-    event->write(nodes);
-    event->write(3.2f);
-    event->write("LOCATION");
-    event->write((int64_t)990);
-    event->init();
+    std::vector<int> attributionUids = {100, 100};
+    std::vector<string> attributionTags = {"LOCATION", "LOCATION"};
+
+    vector<const char*> cTags(attributionTags.size());
+    for (int i = 0; i < cTags.size(); i++) {
+        cTags[i] = attributionTags[i].c_str();
+    }
+
+    AStatsEvent_writeAttributionChain(statsEvent,
+                                      reinterpret_cast<const uint32_t*>(attributionUids.data()),
+                                      cTags.data(), attributionUids.size());
+    AStatsEvent_writeFloat(statsEvent, 3.2f);
+    AStatsEvent_writeString(statsEvent, "LOCATION");
+    AStatsEvent_writeInt64(statsEvent, 990);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    event->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
 
     field_matcher->set_field(1);
     auto child = field_matcher->add_child();
@@ -46,7 +61,7 @@
 }
 
 static void BM_FilterValue(benchmark::State& state) {
-    LogEvent event(1, 100000);
+    LogEvent event(/*uid=*/0, /*pid=*/0);
     FieldMatcher field_matcher;
     createLogEventAndMatcher(&event, &field_matcher);
 
diff --git a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
index 2a4403e..c7d01cc 100644
--- a/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
+++ b/cmds/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
@@ -19,6 +19,7 @@
 #include "HashableDimensionKey.h"
 #include "logd/LogEvent.h"
 #include "stats_log_util.h"
+#include "stats_event.h"
 
 namespace android {
 namespace os {
@@ -27,16 +28,30 @@
 using std::vector;
 
 static void createLogEventAndLink(LogEvent* event, Metric2Condition *link) {
-    AttributionNodeInternal node;
-    node.set_uid(100);
-    node.set_tag("LOCATION");
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, 1);
+    AStatsEvent_overwriteTimestamp(statsEvent, 100000);
 
-    std::vector<AttributionNodeInternal> nodes = {node, node};
-    event->write(nodes);
-    event->write(3.2f);
-    event->write("LOCATION");
-    event->write((int64_t)990);
-    event->init();
+    std::vector<int> attributionUids = {100, 100};
+    std::vector<string> attributionTags = {"LOCATION", "LOCATION"};
+
+    vector<const char*> cTags(attributionTags.size());
+    for (int i = 0; i < cTags.size(); i++) {
+        cTags[i] = attributionTags[i].c_str();
+    }
+
+    AStatsEvent_writeAttributionChain(statsEvent,
+                                      reinterpret_cast<const uint32_t*>(attributionUids.data()),
+                                      cTags.data(), attributionUids.size());
+    AStatsEvent_writeFloat(statsEvent, 3.2f);
+    AStatsEvent_writeString(statsEvent, "LOCATION");
+    AStatsEvent_writeInt64(statsEvent, 990);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    event->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
 
     link->conditionId = 1;
 
@@ -54,7 +69,7 @@
 
 static void BM_GetDimensionInCondition(benchmark::State& state) {
     Metric2Condition link;
-    LogEvent event(1, 100000);
+    LogEvent event(/*uid=*/0, /*pid=*/0);
     createLogEventAndLink(&event, &link);
 
     while (state.KeepRunning()) {
diff --git a/cmds/statsd/benchmark/log_event_benchmark.cpp b/cmds/statsd/benchmark/log_event_benchmark.cpp
index 8b68743..057e00b 100644
--- a/cmds/statsd/benchmark/log_event_benchmark.cpp
+++ b/cmds/statsd/benchmark/log_event_benchmark.cpp
@@ -39,7 +39,8 @@
     uint8_t msg[LOGGER_ENTRY_MAX_PAYLOAD];
     size_t size = createAndParseStatsEvent(msg);
     while (state.KeepRunning()) {
-        benchmark::DoNotOptimize(LogEvent(msg, size, /*uid=*/ 1000, /*pid=*/ 1001));
+        LogEvent event(/*uid=*/ 1000, /*pid=*/ 1001);
+        benchmark::DoNotOptimize(event.parseBuffer(msg, size));
     }
 }
 BENCHMARK(BM_LogEventCreation);
diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp
index cca6d52..4bce89f 100644
--- a/cmds/statsd/benchmark/metric_util.cpp
+++ b/cmds/statsd/benchmark/metric_util.cpp
@@ -14,6 +14,8 @@
 
 #include "metric_util.h"
 
+#include "stats_event.h"
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -246,117 +248,112 @@
 }
 
 std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
-    const android::view::DisplayStateEnum state, uint64_t timestampNs) {
-    auto event = std::make_unique<LogEvent>(android::util::SCREEN_STATE_CHANGED, timestampNs);
-    event->write(state);
-    event->init();
-    return event;
-}
+        uint64_t timestampNs, const android::view::DisplayStateEnum state) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
 
-std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
-    int level, uint64_t timestampNs) {
-    auto event = std::make_unique<LogEvent>(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs);
-    (event->write(level));
-    event->init();
-    return event;
+    AStatsEvent_writeInt32(statsEvent, state);
+    AStatsEvent_build(statsEvent);
 
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+
+    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+    return logEvent;
 }
 
 std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& jobName,
-        const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
-    auto event = std::make_unique<LogEvent>(android::util::SCHEDULED_JOB_STATE_CHANGED, timestampNs);
-    event->write(attributions);
-    event->write(jobName);
-    event->write(state);
-    event->init();
-    return event;
+        const vector<int>& attributionUids, const vector<string>& attributionTags,
+        const string& jobName, const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    vector<const char*> cTags(attributionTags.size());
+    for (int i = 0; i < cTags.size(); i++) {
+        cTags[i] = attributionTags[i].c_str();
+    }
+
+    AStatsEvent_writeAttributionChain(statsEvent,
+                                      reinterpret_cast<const uint32_t*>(attributionUids.data()),
+                                      cTags.data(), attributionUids.size());
+    AStatsEvent_writeString(statsEvent, jobName.c_str());
+    AStatsEvent_writeInt32(statsEvent, state);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+
+    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+    return logEvent;
 }
 
-std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(
-    const std::vector<AttributionNodeInternal>& attributions,
-    const string& name, uint64_t timestampNs) {
-    return CreateScheduledJobStateChangedEvent(
-            attributions, name, ScheduledJobStateChanged::STARTED, timestampNs);
+std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
+                                                       const vector<int>& attributionUids,
+                                                       const vector<string>& attributionTags,
+                                                       const string& jobName) {
+    return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
+                                               ScheduledJobStateChanged::STARTED, timestampNs);
 }
 
 // Create log event when scheduled job finishes.
-std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(
-    const std::vector<AttributionNodeInternal>& attributions,
-    const string& name, uint64_t timestampNs) {
-    return CreateScheduledJobStateChangedEvent(
-            attributions, name, ScheduledJobStateChanged::FINISHED, timestampNs);
+std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
+                                                        const vector<int>& attributionUids,
+                                                        const vector<string>& attributionTags,
+                                                        const string& jobName) {
+    return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
+                                               ScheduledJobStateChanged::FINISHED, timestampNs);
 }
 
-std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
-        const WakelockStateChanged::State state, uint64_t timestampNs) {
-    auto event = std::make_unique<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, timestampNs);
-    event->write(attributions);
-    event->write(android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK);
-    event->write(wakelockName);
-    event->write(state);
-    event->init();
-    return event;
+std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(uint64_t timestampNs,
+                                                      const vector<int>& attributionUids,
+                                                      const vector<string>& attributionTags,
+                                                      const string& name,
+                                                      const SyncStateChanged::State state) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    vector<const char*> cTags(attributionTags.size());
+    for (int i = 0; i < cTags.size(); i++) {
+        cTags[i] = attributionTags[i].c_str();
+    }
+
+    AStatsEvent_writeAttributionChain(statsEvent,
+                                      reinterpret_cast<const uint32_t*>(attributionUids.data()),
+                                      cTags.data(), attributionUids.size());
+    AStatsEvent_writeString(statsEvent, name.c_str());
+    AStatsEvent_writeInt32(statsEvent, state);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+
+    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+    return logEvent;
 }
 
-std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
-        uint64_t timestampNs) {
-    return CreateWakelockStateChangedEvent(
-        attributions, wakelockName, WakelockStateChanged::ACQUIRE, timestampNs);
+std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs,
+                                               const vector<int>& attributionUids,
+                                               const vector<string>& attributionTags,
+                                               const string& name) {
+    return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
+                                       SyncStateChanged::ON);
 }
 
-std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
-        uint64_t timestampNs) {
-    return CreateWakelockStateChangedEvent(
-        attributions, wakelockName, WakelockStateChanged::RELEASE, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
-    const int uid, const ActivityForegroundStateChanged::State state, uint64_t timestampNs) {
-    auto event = std::make_unique<LogEvent>(
-        android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs);
-    event->write(uid);
-    event->write("pkg_name");
-    event->write("class_name");
-    event->write(state);
-    event->init();
-    return event;
-}
-
-std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) {
-    return CreateActivityForegroundStateChangedEvent(
-        uid, ActivityForegroundStateChanged::BACKGROUND, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) {
-    return CreateActivityForegroundStateChangedEvent(
-        uid, ActivityForegroundStateChanged::FOREGROUND, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& name,
-        const SyncStateChanged::State state, uint64_t timestampNs) {
-    auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs);
-    event->write(attributions);
-    event->write(name);
-    event->write(state);
-    event->init();
-    return event;
-}
-
-std::unique_ptr<LogEvent> CreateSyncStartEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& name,
-        uint64_t timestampNs) {
-    return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::ON, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateSyncEndEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& name,
-        uint64_t timestampNs) {
-    return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs);
+std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs,
+                                             const vector<int>& attributionUids,
+                                             const vector<string>& attributionTags,
+                                             const string& name) {
+    return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
+                                       SyncStateChanged::OFF);
 }
 
 sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
diff --git a/cmds/statsd/benchmark/metric_util.h b/cmds/statsd/benchmark/metric_util.h
index 9b28d60..6199fa9 100644
--- a/cmds/statsd/benchmark/metric_util.h
+++ b/cmds/statsd/benchmark/metric_util.h
@@ -94,55 +94,31 @@
 
 // Create log event for screen state changed.
 std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
-    const android::view::DisplayStateEnum state, uint64_t timestampNs);
-
-// Create log event for screen brightness state changed.
-std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
-   int level, uint64_t timestampNs);
+        uint64_t timestampNs, const android::view::DisplayStateEnum state);
 
 // Create log event when scheduled job starts.
-std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(
-    const std::vector<AttributionNodeInternal>& attributions,
-    const string& name, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
+                                                       const vector<int>& attributionUids,
+                                                       const vector<string>& attributionTags,
+                                                       const string& jobName);
 
 // Create log event when scheduled job finishes.
-std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(
-    const std::vector<AttributionNodeInternal>& attributions,
-    const string& name, uint64_t timestampNs);
-
-// Create log event for app moving to background.
-std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs);
-
-// Create log event for app moving to foreground.
-std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
+                                                        const vector<int>& attributionUids,
+                                                        const vector<string>& attributionTags,
+                                                        const string& jobName);
 
 // Create log event when the app sync starts.
-std::unique_ptr<LogEvent> CreateSyncStartEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& name,
-        uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs,
+                                               const vector<int>& attributionUids,
+                                               const vector<string>& attributionTags,
+                                               const string& name);
 
 // Create log event when the app sync ends.
-std::unique_ptr<LogEvent> CreateSyncEndEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& name,
-        uint64_t timestampNs);
-
-// Create log event when the app sync ends.
-std::unique_ptr<LogEvent> CreateAppCrashEvent(
-    const int uid, uint64_t timestampNs);
-
-// Create log event for acquiring wakelock.
-std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
-        uint64_t timestampNs);
-
-// Create log event for releasing wakelock.
-std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
-        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
-        uint64_t timestampNs);
-
-// Create log event for releasing wakelock.
-std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(
-    int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs,
+                                             const vector<int>& attributionUids,
+                                             const vector<string>& attributionTags,
+                                             const string& name);
 
 // Helper function to create an AttributionNodeInternal proto.
 AttributionNodeInternal CreateAttribution(const int& uid, const string& tag);
@@ -158,4 +134,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index 40a24dc..afee79d 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -117,4 +117,6 @@
     optional bool allow_from_any_uid = 50003 [default = false];
 
     repeated string module = 50004;
+
+    optional bool truncate_timestamp = 50005 [default = false];
 }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5f2f546..979f950 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -78,7 +78,8 @@
     // Pushed atoms start at 2.
     oneof pushed {
         // For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
-        BleScanStateChanged ble_scan_state_changed = 2 [(module) = "bluetooth"];
+        BleScanStateChanged ble_scan_state_changed = 2
+                [(module) = "bluetooth", (module) = "statsdtest"];
         ProcessStateChanged process_state_changed = 3 [(module) = "framework"];
         BleScanResultReceived ble_scan_result_received = 4 [(module) = "bluetooth"];
         SensorStateChanged sensor_state_changed =
@@ -93,7 +94,8 @@
                 10 [(module) = "framework", (module) = "statsdtest"];
         LongPartialWakelockStateChanged long_partial_wakelock_state_changed =
                 11 [(module) = "framework"];
-        MobileRadioPowerStateChanged mobile_radio_power_state_changed = 12 [(module) = "framework"];
+        MobileRadioPowerStateChanged mobile_radio_power_state_changed =
+                12 [(module) = "framework", (truncate_timestamp) = true];
         WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13 [(module) = "framework"];
         ActivityManagerSleepStateChanged activity_manager_sleep_state_changed =
                 14 [(module) = "framework"];
@@ -106,7 +108,8 @@
                 20 [(module) = "framework", (module) = "statsdtest"];
         DeviceIdleModeStateChanged device_idle_mode_state_changed = 21 [(module) = "framework"];
         DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22 [(module) = "framework"];
-        AudioStateChanged audio_state_changed = 23 [(module) = "framework"];
+        AudioStateChanged audio_state_changed =
+                23 [(module) = "framework", (truncate_timestamp) = true];
         MediaCodecStateChanged media_codec_state_changed = 24 [(module) = "framework"];
         CameraStateChanged camera_state_changed = 25 [(module) = "framework"];
         FlashlightStateChanged flashlight_state_changed = 26 [(module) = "framework"];
@@ -127,7 +130,8 @@
         WifiLockStateChanged wifi_lock_state_changed = 37 [(module) = "wifi"];
         WifiSignalStrengthChanged wifi_signal_strength_changed = 38 [(module) = "wifi"];
         WifiScanStateChanged wifi_scan_state_changed = 39 [(module) = "wifi"];
-        PhoneSignalStrengthChanged phone_signal_strength_changed = 40 [(module) = "framework"];
+        PhoneSignalStrengthChanged phone_signal_strength_changed =
+                40 [(module) = "framework", (truncate_timestamp) = true];
         SettingChanged setting_changed = 41 [(module) = "framework"];
         ActivityForegroundStateChanged activity_foreground_state_changed =
                 42 [(module) = "framework", (module) = "statsdtest"];
@@ -153,7 +157,8 @@
                 59 [(module) = "framework", (module) = "statsdtest"];
         ForegroundServiceStateChanged foreground_service_state_changed
                 = 60 [(module) = "framework"];
-        CallStateChanged call_state_changed = 61 [(module) = "telecom"];
+        CallStateChanged call_state_changed =
+                61 [(module) = "telecom", (truncate_timestamp) = true];
         KeyguardStateChanged keyguard_state_changed = 62 [(module) = "sysui"];
         KeyguardBouncerStateChanged keyguard_bouncer_state_changed = 63 [(module) = "sysui"];
         KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64 [(module) = "sysui"];
@@ -419,8 +424,10 @@
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
-        MobileBytesTransfer mobile_bytes_transfer = 10002 [(module) = "framework"];
-        MobileBytesTransferByFgBg mobile_bytes_transfer_by_fg_bg = 10003 [(module) = "framework"];
+        MobileBytesTransfer mobile_bytes_transfer =
+                10002 [(module) = "framework", (truncate_timestamp) = true];
+        MobileBytesTransferByFgBg mobile_bytes_transfer_by_fg_bg =
+                10003 [(module) = "framework", (truncate_timestamp) = true];
         BluetoothBytesTransfer bluetooth_bytes_transfer = 10006 [(module) = "framework"];
         KernelWakelock kernel_wakelock = 10004 [(module) = "framework"];
         SubsystemSleepState subsystem_sleep_state = 10005 [(module) = "statsdtest"];
@@ -434,7 +441,7 @@
         ProcessMemoryState process_memory_state = 10013 [(module) = "framework"];
         SystemElapsedRealtime system_elapsed_realtime = 10014 [(module) = "framework"];
         SystemUptime system_uptime = 10015 [(module) = "framework", (module) = "statsdtest"];
-        CpuActiveTime cpu_active_time = 10016 [(module) = "framework"];
+        CpuActiveTime cpu_active_time = 10016 [(module) = "framework", (module) = "statsdtest"];
         CpuClusterTime cpu_cluster_time = 10017 [(module) = "framework", (module) = "statsdtest"];
         DiskSpace disk_space = 10018 [deprecated=true, (module) = "statsdtest"];
         RemainingBatteryCapacity remaining_battery_capacity = 10019 [(module) = "framework"];
@@ -503,6 +510,7 @@
         VoiceCallRatUsage voice_call_rat_usage = 10077 [(module) = "telephony"];
         SimSlotState sim_slot_state = 10078 [(module) = "telephony"];
         SupportedRadioAccessFamily supported_radio_access_family = 10079 [(module) = "telephony"];
+        SettingSnapshot setting_snapshot = 10080 [(module) = "framework"];
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -2745,21 +2753,32 @@
 
 message BackGesture {
     enum BackType {
-          DEFAULT_BACK_TYPE = 0;
-          COMPLETED = 1;
-          COMPLETED_REJECTED = 2; // successful because coming from rejected area
-          INCOMPLETE_EXCLUDED = 3; // would have been successful but in the exclusion area
-          INCOMPLETE = 4;
+        DEFAULT_BACK_TYPE = 0;
+        COMPLETED = 1;
+        COMPLETED_REJECTED = 2; // successful because coming from rejected area
+        INCOMPLETE_EXCLUDED = 3; // would have been successful but in the exclusion area
+        INCOMPLETE = 4;  // Unsuccessful, for reasons other than below.
+        INCOMPLETE_FAR_FROM_EDGE = 5;  // Unsuccessful, far from the edge.
+        INCOMPLETE_MULTI_TOUCH = 6;  // Unsuccessful, multi touch.
+        INCOMPLETE_LONG_PRESS = 7;  // Unsuccessful, long press.
+        INCOMPLETE_VERTICAL_MOVE = 8;  // Unsuccessful, move vertically.
     }
     optional BackType type = 1;
 
-    optional int32 y_coordinate = 2; // y coordinate for ACTION_DOWN event
+    optional int32 y_coordinate = 2 [deprecated = true]; // y coordinate for ACTION_DOWN event
+    optional int32 start_x = 4;  // X coordinate for ACTION_DOWN event.
+    optional int32 start_y = 5;  // Y coordinate for ACTION_DOWN event.
+    optional int32 end_x = 6;   // X coordinate for ACTION_MOVE event.
+    optional int32 end_y = 7;  // Y coordinate for ACTION_MOVE event.
+    optional int32 left_boundary = 8;  // left edge width + left inset
+    optional int32 right_boundary = 9;  // screen width - (right edge width + right inset)
+
     enum WindowHorizontalLocation {
         DEFAULT_LOCATION = 0;
         LEFT = 1;
         RIGHT = 2;
     }
-    optional WindowHorizontalLocation x_location = 3;
+    optional WindowHorizontalLocation x_location = 3 [deprecated = true];
 }
 
 message ExclusionRectStateChanged {
@@ -8992,3 +9011,37 @@
     // Which of the ranked targets got picked, default starting position 0.
     optional int32 position_picked = 4;
 }
+
+/**
+ * Logs settings provider values.
+ *
+ * Use DeviceConfig.getProperties to get a list Setting key, query the data from content provider,
+ * then write the value to proto.
+ *
+ */
+message SettingSnapshot {
+
+    // Setting key
+    optional string name = 1;
+
+    enum SettingsValueType {
+        NOTASSIGNED = 0;
+        ASSIGNED_BOOL_TYPE = 1;
+        ASSIGNED_INT_TYPE = 2;
+        ASSIGNED_FLOAT_TYPE = 3;
+        ASSIGNED_STRING_TYPE = 4;
+    };
+    // Setting value type
+    optional SettingsValueType type = 2;
+
+    optional bool bool_value = 3;
+
+    optional int32 int_value = 4;
+
+    optional float float_value = 5;
+
+    optional string str_value = 6;
+
+    // Android user index. 0 for primary user, 10, 11 for secondary or profile user
+    optional int32 user_id = 7;
+}
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index b515d0a..3b3d0b6 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -504,12 +504,12 @@
     typeInfo = readNextValue<uint8_t>();
     if (getTypeId(typeInfo) != INT64_TYPE) mValid = false;
     mElapsedTimestampNs = readNextValue<int64_t>();
-    parseAnnotations(getNumAnnotations(typeInfo)); // atom-level annotations
     numElements--;
 
     typeInfo = readNextValue<uint8_t>();
     if (getTypeId(typeInfo) != INT32_TYPE) mValid = false;
     mTagId = readNextValue<int32_t>();
+    parseAnnotations(getNumAnnotations(typeInfo)); // atom-level annotations
     numElements--;
 
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 6f54ea7..fca48f9 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -80,7 +80,7 @@
             mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers,
             mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap,
             mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap,
-            mMetricIndexesWithActivation, mNoReportMetricIds);
+            mAlertTrackerMap, mMetricIndexesWithActivation, mNoReportMetricIds);
 
     mHashStringsInReport = config.hash_strings_in_metric_report();
     mVersionStringsInReport = config.version_strings_in_metric_report();
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 6d20822..7500ec9 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -230,6 +230,10 @@
     // Maps deactivation triggering event to MetricProducers.
     std::unordered_map<int, std::vector<int>> mDeactivationAtomTrackerToMetricMap;
 
+    // Maps AlertIds to the index of the corresponding AnomalyTracker stored in mAllAnomalyTrackers.
+    // The map is used in LoadMetadata to more efficiently lookup AnomalyTrackers from an AlertId.
+    std::unordered_map<int64_t, int> mAlertTrackerMap;
+
     std::vector<int> mMetricIndexesWithActivation;
 
     void initLogSourceWhiteList();
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 40a313a..e5fe87a 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -830,10 +830,10 @@
 
 bool initAlerts(const StatsdConfig& config,
                 const unordered_map<int64_t, int>& metricProducerMap,
+                unordered_map<int64_t, int>& alertTrackerMap,
                 const sp<AlarmMonitor>& anomalyAlarmMonitor,
                 vector<sp<MetricProducer>>& allMetricProducers,
                 vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
-    unordered_map<int64_t, int> anomalyTrackerMap;
     for (int i = 0; i < config.alert_size(); i++) {
         const Alert& alert = config.alert(i);
         const auto& itr = metricProducerMap.find(alert.metric_id());
@@ -858,7 +858,7 @@
             // The ALOGW for this invalid alert was already displayed in addAnomalyTracker().
             return false;
         }
-        anomalyTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
+        alertTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
         allAnomalyTrackers.push_back(anomalyTracker);
     }
     for (int i = 0; i < config.subscription_size(); ++i) {
@@ -872,8 +872,8 @@
                 (long long)subscription.id());
             return false;
         }
-        const auto& itr = anomalyTrackerMap.find(subscription.rule_id());
-        if (itr == anomalyTrackerMap.end()) {
+        const auto& itr = alertTrackerMap.find(subscription.rule_id());
+        if (itr == alertTrackerMap.end()) {
             ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
                 (long long)subscription.id(), (long long)subscription.rule_id());
             return false;
@@ -944,6 +944,7 @@
                       unordered_map<int, std::vector<int>>& trackerToConditionMap,
                       unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
                       unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+                      unordered_map<int64_t, int>& alertTrackerMap,
                       vector<int>& metricsWithActivation,
                       std::set<int64_t>& noReportMetricIds) {
     unordered_map<int64_t, int> logTrackerMap;
@@ -976,8 +977,8 @@
         ALOGE("initMetricProducers failed");
         return false;
     }
-    if (!initAlerts(config, metricProducerMap, anomalyAlarmMonitor, allMetricProducers,
-                    allAnomalyTrackers)) {
+    if (!initAlerts(config, metricProducerMap, alertTrackerMap, anomalyAlarmMonitor,
+                    allMetricProducers, allAnomalyTrackers)) {
         ALOGE("initAlerts failed");
         return false;
     }
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 5ebb232..a8ccc62 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -128,6 +128,7 @@
                       std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
                       unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
                       unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+                      std::unordered_map<int64_t, int>& alertTrackerMap,
                       vector<int>& metricsWithActivation,
                       std::set<int64_t>& noReportMetricIds);
 
diff --git a/cmds/statsd/src/statsd_metadata.proto b/cmds/statsd/src/statsd_metadata.proto
new file mode 100644
index 0000000..e00fe336
--- /dev/null
+++ b/cmds/statsd/src/statsd_metadata.proto
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+syntax = "proto2";
+
+package android.os.statsd.metadata;
+
+message ConfigKey {
+  optional int64 config_id = 1;
+  optional int32 uid = 2;
+}
+
+message Field {
+  optional int32 tag = 1;
+  optional int32 field = 2;
+}
+
+message FieldValue {
+  optional Field field = 1;
+  oneof value {
+    int32 value_int = 2;
+    int64 value_long = 3;
+    float value_float = 4;
+    double value_double = 5;
+    string value_str = 6;
+    bytes value_storage = 7;
+  }
+}
+
+message MetricDimensionKey {
+  repeated FieldValue dimension_key_in_what = 1;
+  repeated FieldValue state_values_key = 2;
+}
+
+message AlertMetadata {
+  optional int64 alert_id = 1;
+  // The earliest time the alert can be fired again in wall clock time.
+  optional int32 last_refractory_ends_sec = 2;
+  optional MetricDimensionKey dimension_key = 3;
+}
+
+// All metadata for a config in statsd
+message StatsMetadata {
+  optional ConfigKey config_key = 1;
+  repeated AlertMetadata alert_metadata = 2;
+}
+
+message StatsMetadataList {
+  repeated StatsMetadata stats_metadata = 1;
+}
\ No newline at end of file
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index a5ff067..0bf24f1 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -14,13 +14,16 @@
  * limitations under the License.
  */
 #include <gtest/gtest.h>
+
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "matchers/matcher_util.h"
 #include "src/logd/LogEvent.h"
+#include "stats_event.h"
 #include "stats_log_util.h"
 #include "stats_util.h"
 #include "subscriber/SubscriberReporter.h"
+#include "tests/statsd_test_util.h"
 
 #ifdef __ANDROID__
 
@@ -30,6 +33,58 @@
 namespace os {
 namespace statsd {
 
+namespace {
+void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+                  const vector<int>& attributionUids, const vector<string>& attributionTags,
+                  const string& name) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    vector<const char*> cTags(attributionTags.size());
+    for (int i = 0; i < cTags.size(); i++) {
+        cTags[i] = attributionTags[i].c_str();
+    }
+
+    AStatsEvent_writeAttributionChain(statsEvent,
+                                      reinterpret_cast<const uint32_t*>(attributionUids.data()),
+                                      cTags.data(), attributionUids.size());
+    AStatsEvent_writeString(statsEvent, name.c_str());
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
+}
+
+void makeLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+                  const vector<int>& attributionUids, const vector<string>& attributionTags,
+                  const int32_t value) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    vector<const char*> cTags(attributionTags.size());
+    for (int i = 0; i < cTags.size(); i++) {
+        cTags[i] = attributionTags[i].c_str();
+    }
+
+    AStatsEvent_writeAttributionChain(statsEvent,
+                                      reinterpret_cast<const uint32_t*>(attributionUids.data()),
+                                      cTags.data(), attributionUids.size());
+    AStatsEvent_writeInt32(statsEvent, value);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
+}
+}  // anonymous namespace
+
 TEST(AtomMatcherTest, TestFieldTranslation) {
     FieldMatcher matcher1;
     matcher1.set_field(10);
@@ -72,66 +127,50 @@
     EXPECT_EQ((int32_t)0xff7f7f7f, matcher12.mMask);
 }
 
-// TODO(b/149590301): Update this test to use new socket schema.
-//TEST(AtomMatcherTest, TestFilter_ALL) {
-//    FieldMatcher matcher1;
-//    matcher1.set_field(10);
-//    FieldMatcher* child = matcher1.add_child();
-//    child->set_field(1);
-//    child->set_position(Position::ALL);
-//
-//    child->add_child()->set_field(1);
-//    child->add_child()->set_field(2);
-//
-//    child = matcher1.add_child();
-//    child->set_field(2);
-//
-//    vector<Matcher> matchers;
-//    translateFieldMatcher(matcher1, &matchers);
-//
-//    AttributionNodeInternal attribution_node1;
-//    attribution_node1.set_uid(1111);
-//    attribution_node1.set_tag("location1");
-//
-//    AttributionNodeInternal attribution_node2;
-//    attribution_node2.set_uid(2222);
-//    attribution_node2.set_tag("location2");
-//
-//    AttributionNodeInternal attribution_node3;
-//    attribution_node3.set_uid(3333);
-//    attribution_node3.set_tag("location3");
-//    std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
-//                                                              attribution_node3};
-//
-//    // Set up the event
-//    LogEvent event(10, 12345);
-//    event.write(attribution_nodes);
-//    event.write("some value");
-//    // Convert to a LogEvent
-//    event.init();
-//    HashableDimensionKey output;
-//
-//    filterValues(matchers, event.getValues(), &output);
-//
-//    EXPECT_EQ((size_t)7, output.getValues().size());
-//    EXPECT_EQ((int32_t)0x02010101, output.getValues()[0].mField.getField());
-//    EXPECT_EQ((int32_t)1111, output.getValues()[0].mValue.int_value);
-//    EXPECT_EQ((int32_t)0x02010102, output.getValues()[1].mField.getField());
-//    EXPECT_EQ("location1", output.getValues()[1].mValue.str_value);
-//
-//    EXPECT_EQ((int32_t)0x02010201, output.getValues()[2].mField.getField());
-//    EXPECT_EQ((int32_t)2222, output.getValues()[2].mValue.int_value);
-//    EXPECT_EQ((int32_t)0x02010202, output.getValues()[3].mField.getField());
-//    EXPECT_EQ("location2", output.getValues()[3].mValue.str_value);
-//
-//    EXPECT_EQ((int32_t)0x02010301, output.getValues()[4].mField.getField());
-//    EXPECT_EQ((int32_t)3333, output.getValues()[4].mValue.int_value);
-//    EXPECT_EQ((int32_t)0x02010302, output.getValues()[5].mField.getField());
-//    EXPECT_EQ("location3", output.getValues()[5].mValue.str_value);
-//
-//    EXPECT_EQ((int32_t)0x00020000, output.getValues()[6].mField.getField());
-//    EXPECT_EQ("some value", output.getValues()[6].mValue.str_value);
-//}
+TEST(AtomMatcherTest, TestFilter_ALL) {
+    FieldMatcher matcher1;
+    matcher1.set_field(10);
+    FieldMatcher* child = matcher1.add_child();
+    child->set_field(1);
+    child->set_position(Position::ALL);
+
+    child->add_child()->set_field(1);
+    child->add_child()->set_field(2);
+
+    child = matcher1.add_child();
+    child->set_field(2);
+
+    vector<Matcher> matchers;
+    translateFieldMatcher(matcher1, &matchers);
+
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event, 10 /*atomId*/, 1012345, attributionUids, attributionTags, "some value");
+    HashableDimensionKey output;
+
+    filterValues(matchers, event.getValues(), &output);
+
+    EXPECT_EQ((size_t)7, output.getValues().size());
+    EXPECT_EQ((int32_t)0x02010101, output.getValues()[0].mField.getField());
+    EXPECT_EQ((int32_t)1111, output.getValues()[0].mValue.int_value);
+    EXPECT_EQ((int32_t)0x02010102, output.getValues()[1].mField.getField());
+    EXPECT_EQ("location1", output.getValues()[1].mValue.str_value);
+
+    EXPECT_EQ((int32_t)0x02010201, output.getValues()[2].mField.getField());
+    EXPECT_EQ((int32_t)2222, output.getValues()[2].mValue.int_value);
+    EXPECT_EQ((int32_t)0x02010202, output.getValues()[3].mField.getField());
+    EXPECT_EQ("location2", output.getValues()[3].mValue.str_value);
+
+    EXPECT_EQ((int32_t)0x02010301, output.getValues()[4].mField.getField());
+    EXPECT_EQ((int32_t)3333, output.getValues()[4].mValue.int_value);
+    EXPECT_EQ((int32_t)0x02010302, output.getValues()[5].mField.getField());
+    EXPECT_EQ("location3", output.getValues()[5].mValue.str_value);
+
+    EXPECT_EQ((int32_t)0x00020000, output.getValues()[6].mField.getField());
+    EXPECT_EQ("some value", output.getValues()[6].mValue.str_value);
+}
 
 TEST(AtomMatcherTest, TestSubDimension) {
     HashableDimensionKey dim;
@@ -174,61 +213,45 @@
     EXPECT_TRUE(dim.contains(subDim4));
 }
 
-// TODO(b/149590301): Update this test to use new socket schema.
-//TEST(AtomMatcherTest, TestMetric2ConditionLink) {
-//    AttributionNodeInternal attribution_node1;
-//    attribution_node1.set_uid(1111);
-//    attribution_node1.set_tag("location1");
-//
-//    AttributionNodeInternal attribution_node2;
-//    attribution_node2.set_uid(2222);
-//    attribution_node2.set_tag("location2");
-//
-//    AttributionNodeInternal attribution_node3;
-//    attribution_node3.set_uid(3333);
-//    attribution_node3.set_tag("location3");
-//    std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
-//                                                              attribution_node3};
-//
-//    // Set up the event
-//    LogEvent event(10, 12345);
-//    event.write(attribution_nodes);
-//    event.write("some value");
-//    // Convert to a LogEvent
-//    event.init();
-//
-//    FieldMatcher whatMatcher;
-//    whatMatcher.set_field(10);
-//    FieldMatcher* child11 = whatMatcher.add_child();
-//    child11->set_field(1);
-//    child11->set_position(Position::ANY);
-//    child11 = child11->add_child();
-//    child11->set_field(1);
-//
-//    FieldMatcher conditionMatcher;
-//    conditionMatcher.set_field(27);
-//    FieldMatcher* child2 = conditionMatcher.add_child();
-//    child2->set_field(2);
-//    child2->set_position(Position::LAST);
-//
-//    child2 = child2->add_child();
-//    child2->set_field(2);
-//
-//    Metric2Condition link;
-//
-//    translateFieldMatcher(whatMatcher, &link.metricFields);
-//    translateFieldMatcher(conditionMatcher, &link.conditionFields);
-//
-//    EXPECT_EQ((size_t)1, link.metricFields.size());
-//    EXPECT_EQ((int32_t)0x02010001, link.metricFields[0].mMatcher.getField());
-//    EXPECT_EQ((int32_t)0xff7f007f, link.metricFields[0].mMask);
-//    EXPECT_EQ((int32_t)10, link.metricFields[0].mMatcher.getTag());
-//
-//    EXPECT_EQ((size_t)1, link.conditionFields.size());
-//    EXPECT_EQ((int32_t)0x02028002, link.conditionFields[0].mMatcher.getField());
-//    EXPECT_EQ((int32_t)0xff7f807f, link.conditionFields[0].mMask);
-//    EXPECT_EQ((int32_t)27, link.conditionFields[0].mMatcher.getTag());
-//}
+TEST(AtomMatcherTest, TestMetric2ConditionLink) {
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event, 10 /*atomId*/, 12345, attributionUids, attributionTags, "some value");
+
+    FieldMatcher whatMatcher;
+    whatMatcher.set_field(10);
+    FieldMatcher* child11 = whatMatcher.add_child();
+    child11->set_field(1);
+    child11->set_position(Position::ANY);
+    child11 = child11->add_child();
+    child11->set_field(1);
+
+    FieldMatcher conditionMatcher;
+    conditionMatcher.set_field(27);
+    FieldMatcher* child2 = conditionMatcher.add_child();
+    child2->set_field(2);
+    child2->set_position(Position::LAST);
+
+    child2 = child2->add_child();
+    child2->set_field(2);
+
+    Metric2Condition link;
+
+    translateFieldMatcher(whatMatcher, &link.metricFields);
+    translateFieldMatcher(conditionMatcher, &link.conditionFields);
+
+    EXPECT_EQ((size_t)1, link.metricFields.size());
+    EXPECT_EQ((int32_t)0x02010001, link.metricFields[0].mMatcher.getField());
+    EXPECT_EQ((int32_t)0xff7f007f, link.metricFields[0].mMask);
+    EXPECT_EQ((int32_t)10, link.metricFields[0].mMatcher.getTag());
+
+    EXPECT_EQ((size_t)1, link.conditionFields.size());
+    EXPECT_EQ((int32_t)0x02028002, link.conditionFields[0].mMatcher.getField());
+    EXPECT_EQ((int32_t)0xff7f807f, link.conditionFields[0].mMask);
+    EXPECT_EQ((int32_t)27, link.conditionFields[0].mMatcher.getTag());
+}
 
 TEST(AtomMatcherTest, TestWriteDimensionPath) {
     for (auto position : {Position::ANY, Position::ALL, Position::FIRST, Position::LAST}) {
@@ -439,50 +462,38 @@
     EXPECT_EQ(99999, dim4.value_long());
 }
 
-// TODO(b/149590301): Update this test to use new socket schema.
-//TEST(AtomMatcherTest, TestWriteAtomToProto) {
-//    AttributionNodeInternal attribution_node1;
-//    attribution_node1.set_uid(1111);
-//    attribution_node1.set_tag("location1");
-//
-//    AttributionNodeInternal attribution_node2;
-//    attribution_node2.set_uid(2222);
-//    attribution_node2.set_tag("location2");
-//
-//    std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2};
-//
-//    // Set up the event
-//    LogEvent event(4, 12345);
-//    event.write(attribution_nodes);
-//    event.write((int32_t)999);
-//    // Convert to a LogEvent
-//    event.init();
-//
-//    android::util::ProtoOutputStream protoOutput;
-//    writeFieldValueTreeToStream(event.GetTagId(), event.getValues(), &protoOutput);
-//
-//    vector<uint8_t> outData;
-//    outData.resize(protoOutput.size());
-//    size_t pos = 0;
-//    sp<ProtoReader> reader = protoOutput.data();
-//    while (reader->readBuffer() != NULL) {
-//        size_t toRead = reader->currentToRead();
-//        std::memcpy(&(outData[pos]), reader->readBuffer(), toRead);
-//        pos += toRead;
-//        reader->move(toRead);
-//    }
-//
-//    Atom result;
-//    EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
-//    EXPECT_EQ(Atom::PushedCase::kBleScanResultReceived, result.pushed_case());
-//    const auto& atom = result.ble_scan_result_received();
-//    EXPECT_EQ(2, atom.attribution_node_size());
-//    EXPECT_EQ(1111, atom.attribution_node(0).uid());
-//    EXPECT_EQ("location1", atom.attribution_node(0).tag());
-//    EXPECT_EQ(2222, atom.attribution_node(1).uid());
-//    EXPECT_EQ("location2", atom.attribution_node(1).tag());
-//    EXPECT_EQ(999, atom.num_results());
-//}
+TEST(AtomMatcherTest, TestWriteAtomToProto) {
+    std::vector<int> attributionUids = {1111, 2222};
+    std::vector<string> attributionTags = {"location1", "location2"};
+
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event, 4 /*atomId*/, 12345, attributionUids, attributionTags, 999);
+
+    android::util::ProtoOutputStream protoOutput;
+    writeFieldValueTreeToStream(event.GetTagId(), event.getValues(), &protoOutput);
+
+    vector<uint8_t> outData;
+    outData.resize(protoOutput.size());
+    size_t pos = 0;
+    sp<ProtoReader> reader = protoOutput.data();
+    while (reader->readBuffer() != NULL) {
+        size_t toRead = reader->currentToRead();
+        std::memcpy(&(outData[pos]), reader->readBuffer(), toRead);
+        pos += toRead;
+        reader->move(toRead);
+    }
+
+    Atom result;
+    EXPECT_EQ(true, result.ParseFromArray(&outData[0], outData.size()));
+    EXPECT_EQ(Atom::PushedCase::kBleScanResultReceived, result.pushed_case());
+    const auto& atom = result.ble_scan_result_received();
+    EXPECT_EQ(2, atom.attribution_node_size());
+    EXPECT_EQ(1111, atom.attribution_node(0).uid());
+    EXPECT_EQ("location1", atom.attribution_node(0).tag());
+    EXPECT_EQ(2222, atom.attribution_node(1).uid());
+    EXPECT_EQ("location2", atom.attribution_node(1).tag());
+    EXPECT_EQ(999, atom.num_results());
+}
 
 /*
  * Test two Matchers is not a subset of one Matcher.
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 2de6377..6f4c8e4 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -12,18 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "matchers/matcher_util.h"
-#include "stats_log_util.h"
-#include "stats_util.h"
-
 #include <gtest/gtest.h>
 #include <log/log_event_list.h>
 #include <log/log_read.h>
 #include <log/logprint.h>
-
 #include <stdio.h>
 
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "matchers/matcher_util.h"
+#include "stats_event.h"
+#include "stats_log_util.h"
+#include "stats_util.h"
+#include "statsd_test_util.h"
+
 using namespace android::os::statsd;
 using std::unordered_map;
 using std::vector;
@@ -39,646 +40,691 @@
 
 
 #ifdef __ANDROID__
-// TODO(b/149590301): Update these tests to use new socket schema.
-//TEST(AtomMatcherTest, TestSimpleMatcher) {
-//    UidMap uidMap;
-//
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//
-//    LogEvent event(TAG_ID, 0);
-//    EXPECT_TRUE(event.write(11));
-//    event.init();
-//
-//    // Test
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // Wrong tag id.
-//    simpleMatcher->set_atom_id(TAG_ID + 1);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//}
-//
-//TEST(AtomMatcherTest, TestAttributionMatcher) {
-//    UidMap uidMap;
-//    AttributionNodeInternal attribution_node1;
-//    attribution_node1.set_uid(1111);
-//    attribution_node1.set_tag("location1");
-//
-//    AttributionNodeInternal attribution_node2;
-//    attribution_node2.set_uid(2222);
-//    attribution_node2.set_tag("location2");
-//
-//    AttributionNodeInternal attribution_node3;
-//    attribution_node3.set_uid(3333);
-//    attribution_node3.set_tag("location3");
-//    std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
-//                                                              attribution_node3};
-//
-//    // Set up the event
-//    LogEvent event(TAG_ID, 0);
-//    event.write(attribution_nodes);
-//    event.write("some value");
-//    // Convert to a LogEvent
-//    event.init();
-//
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//
-//    // Match first node.
-//    auto attributionMatcher = simpleMatcher->add_field_value_matcher();
-//    attributionMatcher->set_field(FIELD_ID_1);
-//    attributionMatcher->set_position(Position::FIRST);
-//    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
-//        ATTRIBUTION_TAG_FIELD_ID);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("tag");
-//
-//    auto fieldMatcher = simpleMatcher->add_field_value_matcher();
-//    fieldMatcher->set_field(FIELD_ID_2);
-//    fieldMatcher->set_eq_string("some value");
-//
-//    // Tag not matched.
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("location3");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("location1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // Match last node.
-//    attributionMatcher->set_position(Position::LAST);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("location3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // Match any node.
-//    attributionMatcher->set_position(Position::ANY);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("location1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("location2");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("location3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("location4");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // Attribution match but primitive field not match.
-//    attributionMatcher->set_position(Position::ANY);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("location2");
-//    fieldMatcher->set_eq_string("wrong value");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    fieldMatcher->set_eq_string("some value");
-//
-//    // Uid match.
-//    attributionMatcher->set_position(Position::ANY);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_field(
-//        ATTRIBUTION_UID_FIELD_ID);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("pkg0");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    uidMap.updateMap(
-//            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
-//            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
-//             android::String16("v1"), android::String16("v2")},
-//            {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
-//             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
-//            {android::String16(""), android::String16(""), android::String16(""),
-//             android::String16(""), android::String16("")});
-//
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg2");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg0");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    attributionMatcher->set_position(Position::FIRST);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg0");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg2");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    attributionMatcher->set_position(Position::LAST);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg0");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg2");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // Uid + tag.
-//    attributionMatcher->set_position(Position::ANY);
-//    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
-//        ATTRIBUTION_TAG_FIELD_ID);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg0");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location2");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg2");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    attributionMatcher->set_position(Position::FIRST);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg0");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location2");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg2");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location3");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location3");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    attributionMatcher->set_position(Position::LAST);
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg0");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg1");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location2");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg2");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
-//        ->set_eq_string("pkg3");
-//    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
-//        ->set_eq_string("location1");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//}
-//
-//TEST(AtomMatcherTest, TestUidFieldMatcher) {
-//    UidMap uidMap;
-//    uidMap.updateMap(
-//        1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
-//        {android::String16("v1"), android::String16("v1"), android::String16("v2"),
-//         android::String16("v1"), android::String16("v2")},
-//        {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
-//         android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
-//        {android::String16(""), android::String16(""), android::String16(""),
-//         android::String16(""), android::String16("")});
-//
-//    // Set up matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//    simpleMatcher->add_field_value_matcher()->set_field(1);
-//    simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("pkg0");
-//
-//    // Set up the event
-//    LogEvent event(TAG_ID, 0);
-//    event.write(1111);
-//    event.init();
-//
-//    LogEvent event2(TAG_ID_2, 0);
-//    event2.write(1111);
-//    event2.write("some value");
-//    event2.init();
-//
-//    // Tag not in kAtomsWithUidField
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // Tag found in kAtomsWithUidField and has matching uid
-//    simpleMatcher->set_atom_id(TAG_ID_2);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
-//
-//    // Tag found in kAtomsWithUidField but has non-matching uid
-//    simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("Pkg2");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
-//}
-//
-//TEST(AtomMatcherTest, TestNeqAnyStringMatcher) {
-//    UidMap uidMap;
-//    uidMap.updateMap(
-//            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
-//            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
-//             android::String16("v1"), android::String16("v2")},
-//            {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
-//             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
-//            {android::String16(""), android::String16(""), android::String16(""),
-//             android::String16(""), android::String16("")});
-//
-//    AttributionNodeInternal attribution_node1;
-//    attribution_node1.set_uid(1111);
-//    attribution_node1.set_tag("location1");
-//
-//    AttributionNodeInternal attribution_node2;
-//    attribution_node2.set_uid(2222);
-//    attribution_node2.set_tag("location2");
-//
-//    AttributionNodeInternal attribution_node3;
-//    attribution_node3.set_uid(3333);
-//    attribution_node3.set_tag("location3");
-//
-//    AttributionNodeInternal attribution_node4;
-//    attribution_node4.set_uid(1066);
-//    attribution_node4.set_tag("location3");
-//    std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
-//                                                              attribution_node3, attribution_node4};
-//
-//    // Set up the event
-//    LogEvent event(TAG_ID, 0);
-//    event.write(attribution_nodes);
-//    event.write("some value");
-//    // Convert to a LogEvent
-//    event.init();
-//
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//
-//    // Match first node.
-//    auto attributionMatcher = simpleMatcher->add_field_value_matcher();
-//    attributionMatcher->set_field(FIELD_ID_1);
-//    attributionMatcher->set_position(Position::FIRST);
-//    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
-//            ATTRIBUTION_UID_FIELD_ID);
-//    auto neqStringList = attributionMatcher->mutable_matches_tuple()
-//                                 ->mutable_field_value_matcher(0)
-//                                 ->mutable_neq_any_string();
-//    neqStringList->add_str_value("pkg2");
-//    neqStringList->add_str_value("pkg3");
-//
-//    auto fieldMatcher = simpleMatcher->add_field_value_matcher();
-//    fieldMatcher->set_field(FIELD_ID_2);
-//    fieldMatcher->set_eq_string("some value");
-//
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    neqStringList->Clear();
-//    neqStringList->add_str_value("pkg1");
-//    neqStringList->add_str_value("pkg3");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    attributionMatcher->set_position(Position::ANY);
-//    neqStringList->Clear();
-//    neqStringList->add_str_value("maps.com");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    neqStringList->Clear();
-//    neqStringList->add_str_value("PkG3");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    attributionMatcher->set_position(Position::LAST);
-//    neqStringList->Clear();
-//    neqStringList->add_str_value("AID_STATSD");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//}
-//
-//TEST(AtomMatcherTest, TestEqAnyStringMatcher) {
-//    UidMap uidMap;
-//    uidMap.updateMap(
-//            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
-//            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
-//             android::String16("v1"), android::String16("v2")},
-//            {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
-//             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
-//            {android::String16(""), android::String16(""), android::String16(""),
-//             android::String16(""), android::String16("")});
-//
-//    AttributionNodeInternal attribution_node1;
-//    attribution_node1.set_uid(1067);
-//    attribution_node1.set_tag("location1");
-//
-//    AttributionNodeInternal attribution_node2;
-//    attribution_node2.set_uid(2222);
-//    attribution_node2.set_tag("location2");
-//
-//    AttributionNodeInternal attribution_node3;
-//    attribution_node3.set_uid(3333);
-//    attribution_node3.set_tag("location3");
-//
-//    AttributionNodeInternal attribution_node4;
-//    attribution_node4.set_uid(1066);
-//    attribution_node4.set_tag("location3");
-//    std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
-//                                                              attribution_node3, attribution_node4};
-//
-//    // Set up the event
-//    LogEvent event(TAG_ID, 0);
-//    event.write(attribution_nodes);
-//    event.write("some value");
-//    // Convert to a LogEvent
-//    event.init();
-//
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//
-//    // Match first node.
-//    auto attributionMatcher = simpleMatcher->add_field_value_matcher();
-//    attributionMatcher->set_field(FIELD_ID_1);
-//    attributionMatcher->set_position(Position::FIRST);
-//    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
-//            ATTRIBUTION_UID_FIELD_ID);
-//    auto eqStringList = attributionMatcher->mutable_matches_tuple()
-//                                ->mutable_field_value_matcher(0)
-//                                ->mutable_eq_any_string();
-//    eqStringList->add_str_value("AID_ROOT");
-//    eqStringList->add_str_value("AID_INCIDENTD");
-//
-//    auto fieldMatcher = simpleMatcher->add_field_value_matcher();
-//    fieldMatcher->set_field(FIELD_ID_2);
-//    fieldMatcher->set_eq_string("some value");
-//
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    attributionMatcher->set_position(Position::ANY);
-//    eqStringList->Clear();
-//    eqStringList->add_str_value("AID_STATSD");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    eqStringList->Clear();
-//    eqStringList->add_str_value("pkg1");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    auto normalStringField = fieldMatcher->mutable_eq_any_string();
-//    normalStringField->add_str_value("some value123");
-//    normalStringField->add_str_value("some value");
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    normalStringField->Clear();
-//    normalStringField->add_str_value("AID_STATSD");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    eqStringList->Clear();
-//    eqStringList->add_str_value("maps.com");
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//}
-//
-//TEST(AtomMatcherTest, TestBoolMatcher) {
-//    UidMap uidMap;
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//    auto keyValue1 = simpleMatcher->add_field_value_matcher();
-//    keyValue1->set_field(FIELD_ID_1);
-//    auto keyValue2 = simpleMatcher->add_field_value_matcher();
-//    keyValue2->set_field(FIELD_ID_2);
-//
-//    // Set up the event
-//    LogEvent event(TAG_ID, 0);
-//    EXPECT_TRUE(event.write(true));
-//    EXPECT_TRUE(event.write(false));
-//    // Convert to a LogEvent
-//    event.init();
-//
-//    // Test
-//    keyValue1->set_eq_bool(true);
-//    keyValue2->set_eq_bool(false);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    keyValue1->set_eq_bool(false);
-//    keyValue2->set_eq_bool(false);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    keyValue1->set_eq_bool(false);
-//    keyValue2->set_eq_bool(true);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    keyValue1->set_eq_bool(true);
-//    keyValue2->set_eq_bool(true);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//}
-//
-//TEST(AtomMatcherTest, TestStringMatcher) {
-//    UidMap uidMap;
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//    auto keyValue = simpleMatcher->add_field_value_matcher();
-//    keyValue->set_field(FIELD_ID_1);
-//    keyValue->set_eq_string("some value");
-//
-//    // Set up the event
-//    LogEvent event(TAG_ID, 0);
-//    event.write("some value");
-//    // Convert to a LogEvent
-//    event.init();
-//
-//    // Test
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//}
-//
-//TEST(AtomMatcherTest, TestMultiFieldsMatcher) {
-//    UidMap uidMap;
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//    auto keyValue1 = simpleMatcher->add_field_value_matcher();
-//    keyValue1->set_field(FIELD_ID_1);
-//    auto keyValue2 = simpleMatcher->add_field_value_matcher();
-//    keyValue2->set_field(FIELD_ID_2);
-//
-//    // Set up the event
-//    LogEvent event(TAG_ID, 0);
-//    event.write(2);
-//    event.write(3);
-//
-//    // Convert to a LogEvent
-//    event.init();
-//
-//    // Test
-//    keyValue1->set_eq_int(2);
-//    keyValue2->set_eq_int(3);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    keyValue1->set_eq_int(2);
-//    keyValue2->set_eq_int(4);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    keyValue1->set_eq_int(4);
-//    keyValue2->set_eq_int(3);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//}
-//
-//TEST(AtomMatcherTest, TestIntComparisonMatcher) {
-//    UidMap uidMap;
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//
-//    simpleMatcher->set_atom_id(TAG_ID);
-//    auto keyValue = simpleMatcher->add_field_value_matcher();
-//    keyValue->set_field(FIELD_ID_1);
-//
-//    // Set up the event
-//    LogEvent event(TAG_ID, 0);
-//    event.write(11);
-//    event.init();
-//
-//    // Test
-//
-//    // eq_int
-//    keyValue->set_eq_int(10);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_eq_int(11);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_eq_int(12);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // lt_int
-//    keyValue->set_lt_int(10);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_lt_int(11);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_lt_int(12);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // lte_int
-//    keyValue->set_lte_int(10);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_lte_int(11);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_lte_int(12);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // gt_int
-//    keyValue->set_gt_int(10);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_gt_int(11);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_gt_int(12);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//
-//    // gte_int
-//    keyValue->set_gte_int(10);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_gte_int(11);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
-//    keyValue->set_gte_int(12);
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
-//}
-//
-//TEST(AtomMatcherTest, TestFloatComparisonMatcher) {
-//    UidMap uidMap;
-//    // Set up the matcher
-//    AtomMatcher matcher;
-//    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-//    simpleMatcher->set_atom_id(TAG_ID);
-//
-//    auto keyValue = simpleMatcher->add_field_value_matcher();
-//    keyValue->set_field(FIELD_ID_1);
-//
-//    LogEvent event1(TAG_ID, 0);
-//    keyValue->set_lt_float(10.0);
-//    event1.write(10.1f);
-//    event1.init();
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
-//
-//    LogEvent event2(TAG_ID, 0);
-//    event2.write(9.9f);
-//    event2.init();
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
-//
-//    LogEvent event3(TAG_ID, 0);
-//    event3.write(10.1f);
-//    event3.init();
-//    keyValue->set_gt_float(10.0);
-//    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3));
-//
-//    LogEvent event4(TAG_ID, 0);
-//    event4.write(9.9f);
-//    event4.init();
-//    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4));
-//}
+
+namespace {
+
+void makeIntLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+                     const int32_t value) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    AStatsEvent_writeInt32(statsEvent, value);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
+}
+
+void makeFloatLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+                       const float floatValue) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    AStatsEvent_writeFloat(statsEvent, floatValue);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
+}
+
+void makeStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+                        const string& name) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    AStatsEvent_writeString(statsEvent, name.c_str());
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
+}
+
+void makeIntStringLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+                           const int32_t value, const string& name) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    AStatsEvent_writeInt32(statsEvent, value);
+    AStatsEvent_writeString(statsEvent, name.c_str());
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
+}
+
+void makeAttributionLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+                             const vector<int>& attributionUids,
+                             const vector<string>& attributionTags, const string& name) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    vector<const char*> cTags(attributionTags.size());
+    for (int i = 0; i < cTags.size(); i++) {
+        cTags[i] = attributionTags[i].c_str();
+    }
+
+    AStatsEvent_writeAttributionChain(statsEvent,
+                                      reinterpret_cast<const uint32_t*>(attributionUids.data()),
+                                      cTags.data(), attributionUids.size());
+    AStatsEvent_writeString(statsEvent, name.c_str());
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
+}
+
+void makeBoolLogEvent(LogEvent* logEvent, const int32_t atomId, const int64_t timestamp,
+                      const bool bool1, const bool bool2) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    AStatsEvent_writeBool(statsEvent, bool1);
+    AStatsEvent_writeBool(statsEvent, bool2);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
+}
+
+}  // anonymous namespace
+
+TEST(AtomMatcherTest, TestSimpleMatcher) {
+    UidMap uidMap;
+
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeIntLogEvent(&event, TAG_ID, 0, 11);
+
+    // Test
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // Wrong tag id.
+    simpleMatcher->set_atom_id(TAG_ID + 1);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestAttributionMatcher) {
+    UidMap uidMap;
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+
+    // Set up the log event.
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value");
+
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+
+    // Match first node.
+    auto attributionMatcher = simpleMatcher->add_field_value_matcher();
+    attributionMatcher->set_field(FIELD_ID_1);
+    attributionMatcher->set_position(Position::FIRST);
+    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
+            ATTRIBUTION_TAG_FIELD_ID);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "tag");
+
+    auto fieldMatcher = simpleMatcher->add_field_value_matcher();
+    fieldMatcher->set_field(FIELD_ID_2);
+    fieldMatcher->set_eq_string("some value");
+
+    // Tag not matched.
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "location3");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // Match last node.
+    attributionMatcher->set_position(Position::LAST);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // Match any node.
+    attributionMatcher->set_position(Position::ANY);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "location2");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "location4");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // Attribution match but primitive field not match.
+    attributionMatcher->set_position(Position::ANY);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "location2");
+    fieldMatcher->set_eq_string("wrong value");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    fieldMatcher->set_eq_string("some value");
+
+    // Uid match.
+    attributionMatcher->set_position(Position::ANY);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_field(
+            ATTRIBUTION_UID_FIELD_ID);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg0");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    uidMap.updateMap(
+            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
+             android::String16("v1"), android::String16("v2")},
+            {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
+             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
+            {android::String16(""), android::String16(""), android::String16(""),
+             android::String16(""), android::String16("")});
+
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg2");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg0");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::FIRST);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg0");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg2");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::LAST);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg0");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg2");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // Uid + tag.
+    attributionMatcher->set_position(Position::ANY);
+    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
+            ATTRIBUTION_TAG_FIELD_ID);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg0");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location2");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg2");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::FIRST);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg0");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location2");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg2");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location3");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location3");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::LAST);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg0");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location2");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg2");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
+            "pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
+            "location1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestUidFieldMatcher) {
+    UidMap uidMap;
+    uidMap.updateMap(
+            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
+             android::String16("v1"), android::String16("v2")},
+            {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
+             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
+            {android::String16(""), android::String16(""), android::String16(""),
+             android::String16(""), android::String16("")});
+
+    // Set up matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+    simpleMatcher->add_field_value_matcher()->set_field(1);
+    simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("pkg0");
+
+    // Set up the event
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeIntLogEvent(&event1, TAG_ID, 0, 1111);
+
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeIntStringLogEvent(&event2, TAG_ID_2, 0, 1111, "some value");
+
+    // Tag not in kAtomsWithUidField
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+
+    // Tag found in kAtomsWithUidField and has matching uid
+    simpleMatcher->set_atom_id(TAG_ID_2);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+
+    // Tag found in kAtomsWithUidField but has non-matching uid
+    simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("Pkg2");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
+}
+
+TEST(AtomMatcherTest, TestNeqAnyStringMatcher) {
+    UidMap uidMap;
+    uidMap.updateMap(
+            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
+             android::String16("v1"), android::String16("v2")},
+            {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
+             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
+            {android::String16(""), android::String16(""), android::String16(""),
+             android::String16(""), android::String16("")});
+
+    std::vector<int> attributionUids = {1111, 2222, 3333, 1066};
+    std::vector<string> attributionTags = {"location1", "location2", "location3", "location3"};
+
+    // Set up the event
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value");
+
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+
+    // Match first node.
+    auto attributionMatcher = simpleMatcher->add_field_value_matcher();
+    attributionMatcher->set_field(FIELD_ID_1);
+    attributionMatcher->set_position(Position::FIRST);
+    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
+            ATTRIBUTION_UID_FIELD_ID);
+    auto neqStringList = attributionMatcher->mutable_matches_tuple()
+                                 ->mutable_field_value_matcher(0)
+                                 ->mutable_neq_any_string();
+    neqStringList->add_str_value("pkg2");
+    neqStringList->add_str_value("pkg3");
+
+    auto fieldMatcher = simpleMatcher->add_field_value_matcher();
+    fieldMatcher->set_field(FIELD_ID_2);
+    fieldMatcher->set_eq_string("some value");
+
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    neqStringList->Clear();
+    neqStringList->add_str_value("pkg1");
+    neqStringList->add_str_value("pkg3");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::ANY);
+    neqStringList->Clear();
+    neqStringList->add_str_value("maps.com");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    neqStringList->Clear();
+    neqStringList->add_str_value("PkG3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::LAST);
+    neqStringList->Clear();
+    neqStringList->add_str_value("AID_STATSD");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestEqAnyStringMatcher) {
+    UidMap uidMap;
+    uidMap.updateMap(
+            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+            {android::String16("v1"), android::String16("v1"), android::String16("v2"),
+             android::String16("v1"), android::String16("v2")},
+            {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
+             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */,
+            {android::String16(""), android::String16(""), android::String16(""),
+             android::String16(""), android::String16("")});
+
+    std::vector<int> attributionUids = {1067, 2222, 3333, 1066};
+    std::vector<string> attributionTags = {"location1", "location2", "location3", "location3"};
+
+    // Set up the event
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value");
+
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+
+    // Match first node.
+    auto attributionMatcher = simpleMatcher->add_field_value_matcher();
+    attributionMatcher->set_field(FIELD_ID_1);
+    attributionMatcher->set_position(Position::FIRST);
+    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
+            ATTRIBUTION_UID_FIELD_ID);
+    auto eqStringList = attributionMatcher->mutable_matches_tuple()
+                                ->mutable_field_value_matcher(0)
+                                ->mutable_eq_any_string();
+    eqStringList->add_str_value("AID_ROOT");
+    eqStringList->add_str_value("AID_INCIDENTD");
+
+    auto fieldMatcher = simpleMatcher->add_field_value_matcher();
+    fieldMatcher->set_field(FIELD_ID_2);
+    fieldMatcher->set_eq_string("some value");
+
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::ANY);
+    eqStringList->Clear();
+    eqStringList->add_str_value("AID_STATSD");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    eqStringList->Clear();
+    eqStringList->add_str_value("pkg1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    auto normalStringField = fieldMatcher->mutable_eq_any_string();
+    normalStringField->add_str_value("some value123");
+    normalStringField->add_str_value("some value");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    normalStringField->Clear();
+    normalStringField->add_str_value("AID_STATSD");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    eqStringList->Clear();
+    eqStringList->add_str_value("maps.com");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestBoolMatcher) {
+    UidMap uidMap;
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+    auto keyValue1 = simpleMatcher->add_field_value_matcher();
+    keyValue1->set_field(FIELD_ID_1);
+    auto keyValue2 = simpleMatcher->add_field_value_matcher();
+    keyValue2->set_field(FIELD_ID_2);
+
+    // Set up the event
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeBoolLogEvent(&event, TAG_ID, 0, true, false);
+
+    // Test
+    keyValue1->set_eq_bool(true);
+    keyValue2->set_eq_bool(false);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    keyValue1->set_eq_bool(false);
+    keyValue2->set_eq_bool(false);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    keyValue1->set_eq_bool(false);
+    keyValue2->set_eq_bool(true);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    keyValue1->set_eq_bool(true);
+    keyValue2->set_eq_bool(true);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestStringMatcher) {
+    UidMap uidMap;
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+    auto keyValue = simpleMatcher->add_field_value_matcher();
+    keyValue->set_field(FIELD_ID_1);
+    keyValue->set_eq_string("some value");
+
+    // Set up the event
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeStringLogEvent(&event, TAG_ID, 0, "some value");
+
+    // Test
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestMultiFieldsMatcher) {
+    UidMap uidMap;
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+    auto keyValue1 = simpleMatcher->add_field_value_matcher();
+    keyValue1->set_field(FIELD_ID_1);
+    auto keyValue2 = simpleMatcher->add_field_value_matcher();
+    keyValue2->set_field(FIELD_ID_2);
+
+    // Set up the event
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    CreateTwoValueLogEvent(&event, TAG_ID, 0, 2, 3);
+
+    // Test
+    keyValue1->set_eq_int(2);
+    keyValue2->set_eq_int(3);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    keyValue1->set_eq_int(2);
+    keyValue2->set_eq_int(4);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    keyValue1->set_eq_int(4);
+    keyValue2->set_eq_int(3);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestIntComparisonMatcher) {
+    UidMap uidMap;
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+
+    simpleMatcher->set_atom_id(TAG_ID);
+    auto keyValue = simpleMatcher->add_field_value_matcher();
+    keyValue->set_field(FIELD_ID_1);
+
+    // Set up the event
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeIntLogEvent(&event, TAG_ID, 0, 11);
+
+    // Test
+
+    // eq_int
+    keyValue->set_eq_int(10);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_eq_int(11);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_eq_int(12);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // lt_int
+    keyValue->set_lt_int(10);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_lt_int(11);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_lt_int(12);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // lte_int
+    keyValue->set_lte_int(10);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_lte_int(11);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_lte_int(12);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // gt_int
+    keyValue->set_gt_int(10);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_gt_int(11);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_gt_int(12);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // gte_int
+    keyValue->set_gte_int(10);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_gte_int(11);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    keyValue->set_gte_int(12);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestFloatComparisonMatcher) {
+    UidMap uidMap;
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+
+    auto keyValue = simpleMatcher->add_field_value_matcher();
+    keyValue->set_field(FIELD_ID_1);
+
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeFloatLogEvent(&event1, TAG_ID, 0, 10.1f);
+    keyValue->set_lt_float(10.0);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeFloatLogEvent(&event2, TAG_ID, 0, 9.9f);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+
+    LogEvent event3(/*uid=*/0, /*pid=*/0);
+    makeFloatLogEvent(&event3, TAG_ID, 0, 10.1f);
+    keyValue->set_gt_float(10.0);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3));
+
+    LogEvent event4(/*uid=*/0, /*pid=*/0);
+    makeFloatLogEvent(&event4, TAG_ID, 0, 9.9f);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4));
+}
 
 // Helper for the composite matchers.
 void addSimpleMatcher(SimpleAtomMatcher* simpleMatcher, int tag, int key, int val) {
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 71adc57..356e40b 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -40,6 +40,7 @@
 #ifdef __ANDROID__
 
 const ConfigKey kConfigKey(0, 12345);
+const long kAlertId = 3;
 
 const long timeBaseSec = 1000;
 
@@ -85,7 +86,7 @@
     config.add_no_report_metric(3);
 
     auto alert = config.add_alert();
-    alert->set_id(3);
+    alert->set_id(kAlertId);
     alert->set_metric_id(3);
     alert->set_num_buckets(10);
     alert->set_refractory_period_secs(100);
@@ -218,7 +219,7 @@
     metric->mutable_dimensions_in_what()->add_child()->set_field(1);
 
     auto alert = config.add_alert();
-    alert->set_id(103);
+    alert->set_id(kAlertId);
     alert->set_metric_id(3);
     alert->set_num_buckets(10);
     alert->set_refractory_period_secs(100);
@@ -284,6 +285,7 @@
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
     unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
     vector<int> metricsWithActivation;
     std::set<int64_t> noReportMetricIds;
 
@@ -293,10 +295,14 @@
                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
                                  trackerToMetricMap, trackerToConditionMap,
                                  activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                 metricsWithActivation, noReportMetricIds));
+                                 alertTrackerMap, metricsWithActivation,
+                                 noReportMetricIds));
     EXPECT_EQ(1u, allMetricProducers.size());
     EXPECT_EQ(1u, allAnomalyTrackers.size());
     EXPECT_EQ(1u, noReportMetricIds.size());
+    EXPECT_EQ(1u, alertTrackerMap.size());
+    EXPECT_NE(alertTrackerMap.find(kAlertId), alertTrackerMap.end());
+    EXPECT_EQ(alertTrackerMap.find(kAlertId)->second, 0);
 }
 
 TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
@@ -316,6 +322,7 @@
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
     unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
     vector<int> metricsWithActivation;
     std::set<int64_t> noReportMetricIds;
 
@@ -325,7 +332,8 @@
                                   allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
                                   trackerToMetricMap, trackerToConditionMap,
                                   activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  metricsWithActivation, noReportMetricIds));
+                                  alertTrackerMap, metricsWithActivation,
+                                  noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
@@ -345,6 +353,7 @@
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
     unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
     vector<int> metricsWithActivation;
     std::set<int64_t> noReportMetricIds;
 
@@ -354,7 +363,8 @@
                                   allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
                                   trackerToMetricMap, trackerToConditionMap,
                                   activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  metricsWithActivation, noReportMetricIds));
+                                  alertTrackerMap, metricsWithActivation,
+                                  noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestMissingMatchers) {
@@ -374,6 +384,7 @@
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
     unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
     vector<int> metricsWithActivation;
     std::set<int64_t> noReportMetricIds;
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
@@ -382,7 +393,8 @@
                                   allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
                                   trackerToMetricMap, trackerToConditionMap,
                                   activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  metricsWithActivation, noReportMetricIds));
+                                  alertTrackerMap, metricsWithActivation,
+                                  noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestMissingPredicate) {
@@ -402,6 +414,7 @@
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
     unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
     vector<int> metricsWithActivation;
     std::set<int64_t> noReportMetricIds;
     EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
@@ -410,7 +423,7 @@
                                   allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
                                   trackerToMetricMap, trackerToConditionMap,
                                   activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  metricsWithActivation, noReportMetricIds));
+                                  alertTrackerMap, metricsWithActivation, noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, TestCirclePredicateDependency) {
@@ -430,6 +443,7 @@
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
     unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
     vector<int> metricsWithActivation;
     std::set<int64_t> noReportMetricIds;
 
@@ -439,7 +453,8 @@
                                   allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
                                   trackerToMetricMap, trackerToConditionMap,
                                   activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  metricsWithActivation, noReportMetricIds));
+                                  alertTrackerMap, metricsWithActivation,
+                                  noReportMetricIds));
 }
 
 TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
@@ -459,6 +474,7 @@
     unordered_map<int, std::vector<int>> trackerToConditionMap;
     unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
     unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
     vector<int> metricsWithActivation;
     std::set<int64_t> noReportMetricIds;
 
@@ -468,7 +484,8 @@
                                   allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
                                   trackerToMetricMap, trackerToConditionMap,
                                   activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  metricsWithActivation, noReportMetricIds));
+                                  alertTrackerMap, metricsWithActivation,
+                                  noReportMetricIds));
 }
 
 #else
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index d6498f4..9e7b7c8 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -253,1416 +253,1420 @@
     EXPECT_EQ(2, report.annotation(0).field_int32());
 }
 
-// TODO(b/149590301): Update this test to use new socket schema.
-//TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) {
-//    // Setup a simple config.
-//    StatsdConfig config;
-//    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
-//    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
-//    *config.add_atom_matcher() = wakelockAcquireMatcher;
-//
-//    auto countMetric = config.add_count_metric();
-//    countMetric->set_id(123456);
-//    countMetric->set_what(wakelockAcquireMatcher.id());
-//    countMetric->set_bucket(FIVE_MINUTES);
-//
-//    ConfigKey cfgKey;
-//    sp<StatsLogProcessor> processor = CreateStatsLogProcessor(1, 1, config, cfgKey);
-//
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
-//    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 2);
-//    processor->OnLogEvent(event.get());
-//
-//    vector<uint8_t> bytes;
-//    ConfigMetricsReportList output;
-//
-//    // Dump report WITHOUT erasing data.
-//    processor->onDumpReport(cfgKey, 3, true, false /* Do NOT erase data. */, ADB_DUMP, FAST, &bytes);
-//    output.ParseFromArray(bytes.data(), bytes.size());
-//    EXPECT_EQ(output.reports_size(), 1);
-//    EXPECT_EQ(output.reports(0).metrics_size(), 1);
-//    EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
-//
-//    // Dump report WITH erasing data. There should be data since we didn't previously erase it.
-//    processor->onDumpReport(cfgKey, 4, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes);
-//    output.ParseFromArray(bytes.data(), bytes.size());
-//    EXPECT_EQ(output.reports_size(), 1);
-//    EXPECT_EQ(output.reports(0).metrics_size(), 1);
-//    EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
-//
-//    // Dump report again. There should be no data since we erased it.
-//    processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes);
-//    output.ParseFromArray(bytes.data(), bytes.size());
-//    // We don't care whether statsd has a report, as long as it has no count metrics in it.
-//    bool noData = output.reports_size() == 0
-//            || output.reports(0).metrics_size() == 0
-//            || output.reports(0).metrics(0).count_metrics().data_size() == 0;
-//    EXPECT_TRUE(noData);
-//}
-//
-//TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) {
-//    int uid = 1111;
-//
-//    // Setup a simple config, no activation
-//    StatsdConfig config1;
-//    int64_t cfgId1 = 12341;
-//    config1.set_id(cfgId1);
-//    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
-//    *config1.add_atom_matcher() = wakelockAcquireMatcher;
-//
-//    long metricId1 = 1234561;
-//    long metricId2 = 1234562;
-//    auto countMetric1 = config1.add_count_metric();
-//    countMetric1->set_id(metricId1);
-//    countMetric1->set_what(wakelockAcquireMatcher.id());
-//    countMetric1->set_bucket(FIVE_MINUTES);
-//
-//    auto countMetric2 = config1.add_count_metric();
-//    countMetric2->set_id(metricId2);
-//    countMetric2->set_what(wakelockAcquireMatcher.id());
-//    countMetric2->set_bucket(FIVE_MINUTES);
-//
-//    ConfigKey cfgKey1(uid, cfgId1);
-//
-//    // Add another config, with two metrics, one with activation
-//    StatsdConfig config2;
-//    int64_t cfgId2 = 12342;
-//    config2.set_id(cfgId2);
-//    config2.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//    *config2.add_atom_matcher() = wakelockAcquireMatcher;
-//
-//    long metricId3 = 1234561;
-//    long metricId4 = 1234562;
-//
-//    auto countMetric3 = config2.add_count_metric();
-//    countMetric3->set_id(metricId3);
-//    countMetric3->set_what(wakelockAcquireMatcher.id());
-//    countMetric3->set_bucket(FIVE_MINUTES);
-//
-//    auto countMetric4 = config2.add_count_metric();
-//    countMetric4->set_id(metricId4);
-//    countMetric4->set_what(wakelockAcquireMatcher.id());
-//    countMetric4->set_bucket(FIVE_MINUTES);
-//
-//    auto metric3Activation = config2.add_metric_activation();
-//    metric3Activation->set_metric_id(metricId3);
-//    metric3Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
-//    auto metric3ActivationTrigger = metric3Activation->add_event_activation();
-//    metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
-//    metric3ActivationTrigger->set_ttl_seconds(100);
-//
-//    ConfigKey cfgKey2(uid, cfgId2);
-//
-//    // Add another config, with two metrics, both with activations
-//    StatsdConfig config3;
-//    int64_t cfgId3 = 12343;
-//    config3.set_id(cfgId3);
-//    config3.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//    *config3.add_atom_matcher() = wakelockAcquireMatcher;
-//
-//    long metricId5 = 1234565;
-//    long metricId6 = 1234566;
-//    auto countMetric5 = config3.add_count_metric();
-//    countMetric5->set_id(metricId5);
-//    countMetric5->set_what(wakelockAcquireMatcher.id());
-//    countMetric5->set_bucket(FIVE_MINUTES);
-//
-//    auto countMetric6 = config3.add_count_metric();
-//    countMetric6->set_id(metricId6);
-//    countMetric6->set_what(wakelockAcquireMatcher.id());
-//    countMetric6->set_bucket(FIVE_MINUTES);
-//
-//    auto metric5Activation = config3.add_metric_activation();
-//    metric5Activation->set_metric_id(metricId5);
-//    metric5Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
-//    auto metric5ActivationTrigger = metric5Activation->add_event_activation();
-//    metric5ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
-//    metric5ActivationTrigger->set_ttl_seconds(100);
-//
-//    auto metric6Activation = config3.add_metric_activation();
-//    metric6Activation->set_metric_id(metricId6);
-//    metric6Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
-//    auto metric6ActivationTrigger = metric6Activation->add_event_activation();
-//    metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
-//    metric6ActivationTrigger->set_ttl_seconds(200);
-//
-//    ConfigKey cfgKey3(uid, cfgId3);
-//
-//    sp<UidMap> m = new UidMap();
-//    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-//    sp<AlarmMonitor> anomalyAlarmMonitor;
-//    sp<AlarmMonitor> subscriberAlarmMonitor;
-//    vector<int64_t> activeConfigsBroadcast;
-//
-//    long timeBase1 = 1;
-//    int broadcastCount = 0;
-//    StatsLogProcessor processor(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor,
-//            timeBase1, [](const ConfigKey& key) { return true; },
-//            [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
-//                    const vector<int64_t>& activeConfigs) {
-//                broadcastCount++;
-//                EXPECT_EQ(broadcastUid, uid);
-//                activeConfigsBroadcast.clear();
-//                activeConfigsBroadcast.insert(activeConfigsBroadcast.end(),
-//                        activeConfigs.begin(), activeConfigs.end());
-//                return true;
-//            });
-//
-//    processor.OnConfigUpdated(1, cfgKey1, config1);
-//    processor.OnConfigUpdated(2, cfgKey2, config2);
-//    processor.OnConfigUpdated(3, cfgKey3, config3);
-//
-//    EXPECT_EQ(3, processor.mMetricsManagers.size());
-//
-//    // Expect the first config and both metrics in it to be active.
-//    auto it = processor.mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor.mMetricsManagers.end());
-//    auto& metricsManager1 = it->second;
-//    EXPECT_TRUE(metricsManager1->isActive());
-//
-//    auto metricIt = metricsManager1->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId1) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
-//    auto& metricProducer1 = *metricIt;
-//    EXPECT_TRUE(metricProducer1->isActive());
-//
-//    metricIt = metricsManager1->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId2) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
-//    auto& metricProducer2 = *metricIt;
-//    EXPECT_TRUE(metricProducer2->isActive());
-//
-//    // Expect config 2 to be active. Metric 3 shouldn't be active, metric 4 should be active.
-//    it = processor.mMetricsManagers.find(cfgKey2);
-//    EXPECT_TRUE(it != processor.mMetricsManagers.end());
-//    auto& metricsManager2 = it->second;
-//    EXPECT_TRUE(metricsManager2->isActive());
-//
-//    metricIt = metricsManager2->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId3) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end());
-//    auto& metricProducer3 = *metricIt;
-//    EXPECT_FALSE(metricProducer3->isActive());
-//
-//    metricIt = metricsManager2->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId4) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end());
-//    auto& metricProducer4 = *metricIt;
-//    EXPECT_TRUE(metricProducer4->isActive());
-//
-//    // Expect the third config and both metrics in it to be inactive.
-//    it = processor.mMetricsManagers.find(cfgKey3);
-//    EXPECT_TRUE(it != processor.mMetricsManagers.end());
-//    auto& metricsManager3 = it->second;
-//    EXPECT_FALSE(metricsManager3->isActive());
-//
-//    metricIt = metricsManager3->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId5) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end());
-//    auto& metricProducer5 = *metricIt;
-//    EXPECT_FALSE(metricProducer5->isActive());
-//
-//    metricIt = metricsManager3->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager3->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId6) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end());
-//    auto& metricProducer6 = *metricIt;
-//    EXPECT_FALSE(metricProducer6->isActive());
-//
-//    // No broadcast for active configs should have happened yet.
-//    EXPECT_EQ(broadcastCount, 0);
-//
-//    // Activate all 3 metrics that were not active.
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
-//    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
-//    processor.OnLogEvent(event.get());
-//
-//    // Assert that all 3 configs are active.
-//    EXPECT_TRUE(metricsManager1->isActive());
-//    EXPECT_TRUE(metricsManager2->isActive());
-//    EXPECT_TRUE(metricsManager3->isActive());
-//
-//    // A broadcast should have happened, and all 3 configs should be active in the broadcast.
-//    EXPECT_EQ(broadcastCount, 1);
-//    EXPECT_EQ(activeConfigsBroadcast.size(), 3);
-//    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1)
-//            != activeConfigsBroadcast.end());
-//    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2)
-//            != activeConfigsBroadcast.end());
-//    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3)
-//            != activeConfigsBroadcast.end());
-//
-//    // When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns.
-//    int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
-//    processor.SaveActiveConfigsToDisk(shutDownTime);
-//    const int64_t ttl3 = event->GetElapsedTimestampNs() +
-//            metric3ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
-//    const int64_t ttl5 = event->GetElapsedTimestampNs() +
-//            metric5ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
-//    const int64_t ttl6 = event->GetElapsedTimestampNs() +
-//            metric6ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
-//
-//    // Create a second StatsLogProcessor and push the same 3 configs.
-//    long timeBase2 = 1000;
-//    sp<StatsLogProcessor> processor2 =
-//            CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
-//    processor2->OnConfigUpdated(timeBase2, cfgKey2, config2);
-//    processor2->OnConfigUpdated(timeBase2, cfgKey3, config3);
-//
-//    EXPECT_EQ(3, processor2->mMetricsManagers.size());
-//
-//    // First config and both metrics are active.
-//    it = processor2->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
-//    auto& metricsManager1001 = it->second;
-//    EXPECT_TRUE(metricsManager1001->isActive());
-//
-//    metricIt = metricsManager1001->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId1) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
-//    auto& metricProducer1001 = *metricIt;
-//    EXPECT_TRUE(metricProducer1001->isActive());
-//
-//    metricIt = metricsManager1001->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId2) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
-//    auto& metricProducer1002 = *metricIt;
-//    EXPECT_TRUE(metricProducer1002->isActive());
-//
-//    // Second config is active. Metric 3 is inactive, metric 4 is active.
-//    it = processor2->mMetricsManagers.find(cfgKey2);
-//    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
-//    auto& metricsManager1002 = it->second;
-//    EXPECT_TRUE(metricsManager1002->isActive());
-//
-//    metricIt = metricsManager1002->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId3) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end());
-//    auto& metricProducer1003 = *metricIt;
-//    EXPECT_FALSE(metricProducer1003->isActive());
-//
-//    metricIt = metricsManager1002->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId4) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end());
-//    auto& metricProducer1004 = *metricIt;
-//    EXPECT_TRUE(metricProducer1004->isActive());
-//
-//    // Config 3 is inactive. both metrics are inactive.
-//    it = processor2->mMetricsManagers.find(cfgKey3);
-//    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
-//    auto& metricsManager1003 = it->second;
-//    EXPECT_FALSE(metricsManager1003->isActive());
-//    EXPECT_EQ(2, metricsManager1003->mAllMetricProducers.size());
-//
-//    metricIt = metricsManager1003->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId5) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end());
-//    auto& metricProducer1005 = *metricIt;
-//    EXPECT_FALSE(metricProducer1005->isActive());
-//
-//    metricIt = metricsManager1003->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1003->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId6) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end());
-//    auto& metricProducer1006 = *metricIt;
-//    EXPECT_FALSE(metricProducer1006->isActive());
-//
-//    // Assert that all 3 metrics with activation are inactive and that the ttls were properly set.
-//    EXPECT_FALSE(metricProducer1003->isActive());
-//    const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second;
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
-//    EXPECT_EQ(0, activation1003->start_ns);
-//    EXPECT_FALSE(metricProducer1005->isActive());
-//    const auto& activation1005 = metricProducer1005->mEventActivationMap.begin()->second;
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1005->ttl_ns);
-//    EXPECT_EQ(0, activation1005->start_ns);
-//    EXPECT_FALSE(metricProducer1006->isActive());
-//    const auto& activation1006 = metricProducer1006->mEventActivationMap.begin()->second;
-//    EXPECT_EQ(200 * NS_PER_SEC, activation1006->ttl_ns);
-//    EXPECT_EQ(0, activation1006->start_ns);
-//
-//    processor2->LoadActiveConfigsFromDisk();
-//
-//    // After loading activations from disk, assert that all 3 metrics are active.
-//    EXPECT_TRUE(metricProducer1003->isActive());
-//    EXPECT_EQ(timeBase2 + ttl3 - activation1003->ttl_ns, activation1003->start_ns);
-//    EXPECT_TRUE(metricProducer1005->isActive());
-//    EXPECT_EQ(timeBase2 + ttl5 - activation1005->ttl_ns, activation1005->start_ns);
-//    EXPECT_TRUE(metricProducer1006->isActive());
-//    EXPECT_EQ(timeBase2 + ttl6 - activation1006->ttl_ns, activation1003->start_ns);
-//
-//    // Make sure no more broadcasts have happened.
-//    EXPECT_EQ(broadcastCount, 1);
-//}
-//
-//TEST(StatsLogProcessorTest, TestActivationOnBoot) {
-//    int uid = 1111;
-//
-//    StatsdConfig config1;
-//    config1.set_id(12341);
-//    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
-//    *config1.add_atom_matcher() = wakelockAcquireMatcher;
-//
-//    long metricId1 = 1234561;
-//    long metricId2 = 1234562;
-//    auto countMetric1 = config1.add_count_metric();
-//    countMetric1->set_id(metricId1);
-//    countMetric1->set_what(wakelockAcquireMatcher.id());
-//    countMetric1->set_bucket(FIVE_MINUTES);
-//
-//    auto countMetric2 = config1.add_count_metric();
-//    countMetric2->set_id(metricId2);
-//    countMetric2->set_what(wakelockAcquireMatcher.id());
-//    countMetric2->set_bucket(FIVE_MINUTES);
-//
-//    auto metric1Activation = config1.add_metric_activation();
-//    metric1Activation->set_metric_id(metricId1);
-//    metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
-//    auto metric1ActivationTrigger = metric1Activation->add_event_activation();
-//    metric1ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
-//    metric1ActivationTrigger->set_ttl_seconds(100);
-//
-//    ConfigKey cfgKey1(uid, 12341);
-//    long timeBase1 = 1;
-//    sp<StatsLogProcessor> processor =
-//            CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
-//
-//    EXPECT_EQ(1, processor->mMetricsManagers.size());
-//    auto it = processor->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor->mMetricsManagers.end());
-//    auto& metricsManager1 = it->second;
-//    EXPECT_TRUE(metricsManager1->isActive());
-//
-//    auto metricIt = metricsManager1->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId1) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
-//    auto& metricProducer1 = *metricIt;
-//    EXPECT_FALSE(metricProducer1->isActive());
-//
-//    metricIt = metricsManager1->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId2) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
-//    auto& metricProducer2 = *metricIt;
-//    EXPECT_TRUE(metricProducer2->isActive());
-//
-//    const auto& activation1 = metricProducer1->mEventActivationMap.begin()->second;
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
-//    EXPECT_EQ(0, activation1->start_ns);
-//    EXPECT_EQ(kNotActive, activation1->state);
-//
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
-//    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
-//    processor->OnLogEvent(event.get());
-//
-//    EXPECT_FALSE(metricProducer1->isActive());
-//    EXPECT_EQ(0, activation1->start_ns);
-//    EXPECT_EQ(kActiveOnBoot, activation1->state);
-//
-//    int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
-//    processor->SaveActiveConfigsToDisk(shutDownTime);
-//    EXPECT_FALSE(metricProducer1->isActive());
-//    const int64_t ttl1 = metric1ActivationTrigger->ttl_seconds() * NS_PER_SEC;
-//
-//    long timeBase2 = 1000;
-//    sp<StatsLogProcessor> processor2 =
-//            CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
-//
-//    EXPECT_EQ(1, processor2->mMetricsManagers.size());
-//    it = processor2->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
-//    auto& metricsManager1001 = it->second;
-//    EXPECT_TRUE(metricsManager1001->isActive());
-//
-//    metricIt = metricsManager1001->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId1) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
-//    auto& metricProducer1001 = *metricIt;
-//    EXPECT_FALSE(metricProducer1001->isActive());
-//
-//    metricIt = metricsManager1001->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId2) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
-//    auto& metricProducer1002 = *metricIt;
-//    EXPECT_TRUE(metricProducer1002->isActive());
-//
-//    const auto& activation1001 = metricProducer1001->mEventActivationMap.begin()->second;
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
-//    EXPECT_EQ(0, activation1001->start_ns);
-//    EXPECT_EQ(kNotActive, activation1001->state);
-//
-//    processor2->LoadActiveConfigsFromDisk();
-//
-//    EXPECT_TRUE(metricProducer1001->isActive());
-//    EXPECT_EQ(timeBase2 + ttl1 - activation1001->ttl_ns, activation1001->start_ns);
-//    EXPECT_EQ(kActive, activation1001->state);
-//}
-//
-//TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) {
-//    int uid = 1111;
-//
-//    // Create config with 2 metrics:
-//    // Metric 1: Activate on boot with 2 activations
-//    // Metric 2: Always active
-//    StatsdConfig config1;
-//    config1.set_id(12341);
-//    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
-//    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
-//    *config1.add_atom_matcher() = wakelockAcquireMatcher;
-//    *config1.add_atom_matcher() = screenOnMatcher;
-//
-//    long metricId1 = 1234561;
-//    long metricId2 = 1234562;
-//
-//    auto countMetric1 = config1.add_count_metric();
-//    countMetric1->set_id(metricId1);
-//    countMetric1->set_what(wakelockAcquireMatcher.id());
-//    countMetric1->set_bucket(FIVE_MINUTES);
-//
-//    auto countMetric2 = config1.add_count_metric();
-//    countMetric2->set_id(metricId2);
-//    countMetric2->set_what(wakelockAcquireMatcher.id());
-//    countMetric2->set_bucket(FIVE_MINUTES);
-//
-//    auto metric1Activation = config1.add_metric_activation();
-//    metric1Activation->set_metric_id(metricId1);
-//    metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
-//    auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
-//    metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
-//    metric1ActivationTrigger1->set_ttl_seconds(100);
-//    auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
-//    metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
-//    metric1ActivationTrigger2->set_ttl_seconds(200);
-//
-//    ConfigKey cfgKey1(uid, 12341);
-//    long timeBase1 = 1;
-//    sp<StatsLogProcessor> processor =
-//            CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
-//
-//    // Metric 1 is not active.
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_EQ(1, processor->mMetricsManagers.size());
-//    auto it = processor->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor->mMetricsManagers.end());
-//    auto& metricsManager1 = it->second;
-//    EXPECT_TRUE(metricsManager1->isActive());
-//
-//    auto metricIt = metricsManager1->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId1) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
-//    auto& metricProducer1 = *metricIt;
-//    EXPECT_FALSE(metricProducer1->isActive());
-//
-//    metricIt = metricsManager1->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId2) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
-//    auto& metricProducer2 = *metricIt;
-//    EXPECT_TRUE(metricProducer2->isActive());
-//
-//    int i = 0;
-//    for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
-//        if (metricsManager1->mAllAtomMatchers[i]->getId() ==
-//                metric1ActivationTrigger1->atom_matcher_id()) {
-//            break;
-//        }
-//    }
-//    const auto& activation1 = metricProducer1->mEventActivationMap.at(i);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
-//    EXPECT_EQ(0, activation1->start_ns);
-//    EXPECT_EQ(kNotActive, activation1->state);
-//
-//    i = 0;
-//    for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
-//        if (metricsManager1->mAllAtomMatchers[i]->getId() ==
-//                metric1ActivationTrigger2->atom_matcher_id()) {
-//            break;
-//        }
-//    }
-//    const auto& activation2 = metricProducer1->mEventActivationMap.at(i);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
-//    EXPECT_EQ(0, activation2->start_ns);
-//    EXPECT_EQ(kNotActive, activation2->state);
-//    // }}}------------------------------------------------------------------------------
-//
-//    // Trigger Activation 1 for Metric 1
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
-//    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
-//    processor->OnLogEvent(event.get());
-//
-//    // Metric 1 is not active; Activation 1 set to kActiveOnBoot
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_FALSE(metricProducer1->isActive());
-//    EXPECT_EQ(0, activation1->start_ns);
-//    EXPECT_EQ(kActiveOnBoot, activation1->state);
-//    EXPECT_EQ(0, activation2->start_ns);
-//    EXPECT_EQ(kNotActive, activation2->state);
-//
-//    EXPECT_TRUE(metricProducer2->isActive());
-//    // }}}-----------------------------------------------------------------------------
-//
-//    // Simulate shutdown by saving state to disk
-//    int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
-//    processor->SaveActiveConfigsToDisk(shutDownTime);
-//    EXPECT_FALSE(metricProducer1->isActive());
-//    int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
-//
-//    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
-//    // same config.
-//    long timeBase2 = 1000;
-//    sp<StatsLogProcessor> processor2 =
-//            CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
-//
-//    // Metric 1 is not active.
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_EQ(1, processor2->mMetricsManagers.size());
-//    it = processor2->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
-//    auto& metricsManager1001 = it->second;
-//    EXPECT_TRUE(metricsManager1001->isActive());
-//
-//    metricIt = metricsManager1001->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId1) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
-//    auto& metricProducer1001 = *metricIt;
-//    EXPECT_FALSE(metricProducer1001->isActive());
-//
-//    metricIt = metricsManager1001->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId2) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
-//    auto& metricProducer1002 = *metricIt;
-//    EXPECT_TRUE(metricProducer1002->isActive());
-//
-//    i = 0;
-//    for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
-//        if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
-//                metric1ActivationTrigger1->atom_matcher_id()) {
-//            break;
-//        }
-//    }
-//    const auto& activation1001_1 = metricProducer1001->mEventActivationMap.at(i);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1001_1->ttl_ns);
-//    EXPECT_EQ(0, activation1001_1->start_ns);
-//    EXPECT_EQ(kNotActive, activation1001_1->state);
-//
-//    i = 0;
-//    for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
-//        if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
-//                metric1ActivationTrigger2->atom_matcher_id()) {
-//            break;
-//        }
-//    }
-//
-//    const auto& activation1001_2 = metricProducer1001->mEventActivationMap.at(i);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation1001_2->ttl_ns);
-//    EXPECT_EQ(0, activation1001_2->start_ns);
-//    EXPECT_EQ(kNotActive, activation1001_2->state);
-//    // }}}-----------------------------------------------------------------------------------
-//
-//    // Load saved state from disk.
-//    processor2->LoadActiveConfigsFromDisk();
-//
-//    // Metric 1 active; Activation 1 is active, Activation 2 is not active
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_TRUE(metricProducer1001->isActive());
-//    EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
-//    EXPECT_EQ(kActive, activation1001_1->state);
-//    EXPECT_EQ(0, activation1001_2->start_ns);
-//    EXPECT_EQ(kNotActive, activation1001_2->state);
-//
-//    EXPECT_TRUE(metricProducer1002->isActive());
-//    // }}}--------------------------------------------------------------------------------
-//
-//    // Trigger Activation 2 for Metric 1.
-//    auto screenOnEvent = CreateScreenStateChangedEvent(
-//            android::view::DISPLAY_STATE_ON,
-//            timeBase2 + 200
-//    );
-//    processor2->OnLogEvent(screenOnEvent.get());
-//
-//    // Metric 1 active; Activation 1 is active, Activation 2 is set to kActiveOnBoot
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_TRUE(metricProducer1001->isActive());
-//    EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
-//    EXPECT_EQ(kActive, activation1001_1->state);
-//    EXPECT_EQ(0, activation1001_2->start_ns);
-//    EXPECT_EQ(kActiveOnBoot, activation1001_2->state);
-//
-//    EXPECT_TRUE(metricProducer1002->isActive());
-//    // }}}---------------------------------------------------------------------------
-//
-//    // Simulate shutdown by saving state to disk
-//    shutDownTime = timeBase2 + 50 * NS_PER_SEC;
-//    processor2->SaveActiveConfigsToDisk(shutDownTime);
-//    EXPECT_TRUE(metricProducer1001->isActive());
-//    EXPECT_TRUE(metricProducer1002->isActive());
-//    ttl1 = timeBase2 + metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC - shutDownTime;
-//    int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC;
-//
-//    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
-//    // same config.
-//    long timeBase3 = timeBase2 + 120 * NS_PER_SEC;
-//    sp<StatsLogProcessor> processor3 =
-//            CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1);
-//
-//    // Metric 1 is not active.
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_EQ(1, processor3->mMetricsManagers.size());
-//    it = processor3->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor3->mMetricsManagers.end());
-//    auto& metricsManagerTimeBase3 = it->second;
-//    EXPECT_TRUE(metricsManagerTimeBase3->isActive());
-//
-//    metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId1) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
-//    auto& metricProducerTimeBase3_1 = *metricIt;
-//    EXPECT_FALSE(metricProducerTimeBase3_1->isActive());
-//
-//    metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId2) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
-//    auto& metricProducerTimeBase3_2 = *metricIt;
-//    EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
-//
-//    i = 0;
-//    for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
-//        if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
-//                metric1ActivationTrigger1->atom_matcher_id()) {
-//            break;
-//        }
-//    }
-//    const auto& activationTimeBase3_1 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
-//    EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase3_1->ttl_ns);
-//    EXPECT_EQ(0, activationTimeBase3_1->start_ns);
-//    EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
-//
-//    i = 0;
-//    for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
-//        if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
-//                metric1ActivationTrigger2->atom_matcher_id()) {
-//            break;
-//        }
-//    }
-//
-//    const auto& activationTimeBase3_2 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
-//    EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase3_2->ttl_ns);
-//    EXPECT_EQ(0, activationTimeBase3_2->start_ns);
-//    EXPECT_EQ(kNotActive, activationTimeBase3_2->state);
-//
-//    EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
-//    // }}}----------------------------------------------------------------------------------
-//
-//    // Load saved state from disk.
-//    processor3->LoadActiveConfigsFromDisk();
-//
-//    // Metric 1 active: Activation 1 is active, Activation 2 is active
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
-//    EXPECT_EQ(timeBase3 + ttl1 - activationTimeBase3_1->ttl_ns, activationTimeBase3_1->start_ns);
-//    EXPECT_EQ(kActive, activationTimeBase3_1->state);
-//    EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns);
-//    EXPECT_EQ(kActive, activationTimeBase3_2->state);
-//
-//    EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
-//    // }}}-------------------------------------------------------------------------------
-//
-//    // Trigger Activation 2 for Metric 1 again.
-//    screenOnEvent = CreateScreenStateChangedEvent(
-//            android::view::DISPLAY_STATE_ON,
-//            timeBase3 + 100 * NS_PER_SEC
-//    );
-//    processor3->OnLogEvent(screenOnEvent.get());
-//
-//    // Metric 1 active; Activation 1 is not active, Activation 2 is set to active
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
-//    EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
-//    EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns);
-//    EXPECT_EQ(kActive, activationTimeBase3_2->state);
-//
-//    EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
-//    // }}}---------------------------------------------------------------------------
-//
-//    // Simulate shutdown by saving state to disk.
-//    shutDownTime = timeBase3 + 500 * NS_PER_SEC;
-//    processor3->SaveActiveConfigsToDisk(shutDownTime);
-//    EXPECT_TRUE(metricProducer1001->isActive());
-//    EXPECT_TRUE(metricProducer1002->isActive());
-//    ttl1 = timeBase3 + ttl1 - shutDownTime;
-//    ttl2 = timeBase3 + metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - shutDownTime;
-//
-//    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
-//    // same config.
-//    long timeBase4 = timeBase3 + 600 * NS_PER_SEC;
-//    sp<StatsLogProcessor> processor4 =
-//            CreateStatsLogProcessor(timeBase4, timeBase4, config1, cfgKey1);
-//
-//    // Metric 1 is not active.
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_EQ(1, processor4->mMetricsManagers.size());
-//    it = processor4->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor4->mMetricsManagers.end());
-//    auto& metricsManagerTimeBase4 = it->second;
-//    EXPECT_TRUE(metricsManagerTimeBase4->isActive());
-//
-//    metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId1) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end());
-//    auto& metricProducerTimeBase4_1 = *metricIt;
-//    EXPECT_FALSE(metricProducerTimeBase4_1->isActive());
-//
-//    metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin();
-//    for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) {
-//        if ((*metricIt)->getMetricId() == metricId2) {
-//            break;
-//        }
-//    }
-//    EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end());
-//    auto& metricProducerTimeBase4_2 = *metricIt;
-//    EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
-//
-//    i = 0;
-//    for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) {
-//        if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() ==
-//                metric1ActivationTrigger1->atom_matcher_id()) {
-//            break;
-//        }
-//    }
-//    const auto& activationTimeBase4_1 = metricProducerTimeBase4_1->mEventActivationMap.at(i);
-//    EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase4_1->ttl_ns);
-//    EXPECT_EQ(0, activationTimeBase4_1->start_ns);
-//    EXPECT_EQ(kNotActive, activationTimeBase4_1->state);
-//
-//    i = 0;
-//    for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) {
-//        if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() ==
-//                metric1ActivationTrigger2->atom_matcher_id()) {
-//            break;
-//        }
-//    }
-//
-//    const auto& activationTimeBase4_2 = metricProducerTimeBase4_1->mEventActivationMap.at(i);
-//    EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase4_2->ttl_ns);
-//    EXPECT_EQ(0, activationTimeBase4_2->start_ns);
-//    EXPECT_EQ(kNotActive, activationTimeBase4_2->state);
-//
-//    EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
-//    // }}}----------------------------------------------------------------------------------
-//
-//    // Load saved state from disk.
-//    processor4->LoadActiveConfigsFromDisk();
-//
-//    // Metric 1 active: Activation 1 is not active, Activation 2 is not active
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_FALSE(metricProducerTimeBase4_1->isActive());
-//    EXPECT_EQ(kNotActive, activationTimeBase4_1->state);
-//    EXPECT_EQ(kNotActive, activationTimeBase4_2->state);
-//
-//    EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
-//    // }}}-------------------------------------------------------------------------------
-//}
-//
-//TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes) {
-//    int uid = 1111;
-//
-//    // Create config with 2 metrics:
-//    // Metric 1: Activate on boot with 2 activations
-//    // Metric 2: Always active
-//    StatsdConfig config1;
-//    config1.set_id(12341);
-//    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
-//    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
-//    *config1.add_atom_matcher() = wakelockAcquireMatcher;
-//    *config1.add_atom_matcher() = screenOnMatcher;
-//
-//    long metricId1 = 1234561;
-//    long metricId2 = 1234562;
-//
-//    auto countMetric1 = config1.add_count_metric();
-//    countMetric1->set_id(metricId1);
-//    countMetric1->set_what(wakelockAcquireMatcher.id());
-//    countMetric1->set_bucket(FIVE_MINUTES);
-//
-//    auto countMetric2 = config1.add_count_metric();
-//    countMetric2->set_id(metricId2);
-//    countMetric2->set_what(wakelockAcquireMatcher.id());
-//    countMetric2->set_bucket(FIVE_MINUTES);
-//
-//    auto metric1Activation = config1.add_metric_activation();
-//    metric1Activation->set_metric_id(metricId1);
-//    metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
-//    auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
-//    metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
-//    metric1ActivationTrigger1->set_ttl_seconds(100);
-//    auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
-//    metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
-//    metric1ActivationTrigger2->set_ttl_seconds(200);
-//    metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
-//
-//    ConfigKey cfgKey1(uid, 12341);
-//    long timeBase1 = 1;
-//    sp<StatsLogProcessor> processor1 =
-//            CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
-//
-//    // Metric 1 is not active.
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_EQ(1, processor1->mMetricsManagers.size());
-//    auto it = processor1->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor1->mMetricsManagers.end());
-//    auto& metricsManager1 = it->second;
-//    EXPECT_TRUE(metricsManager1->isActive());
-//
-//    EXPECT_EQ(metricsManager1->mAllMetricProducers.size(), 2);
-//    // We assume that the index of a MetricProducer within the mAllMetricProducers
-//    // array follows the order in which metrics are added to the config.
-//    auto& metricProducer1_1 = metricsManager1->mAllMetricProducers[0];
-//    EXPECT_EQ(metricProducer1_1->getMetricId(), metricId1);
-//    EXPECT_FALSE(metricProducer1_1->isActive());  // inactive due to associated MetricActivation
-//
-//    auto& metricProducer1_2 = metricsManager1->mAllMetricProducers[1];
-//    EXPECT_EQ(metricProducer1_2->getMetricId(), metricId2);
-//    EXPECT_TRUE(metricProducer1_2->isActive());
-//
-//    EXPECT_EQ(metricProducer1_1->mEventActivationMap.size(), 2);
-//    // The key in mEventActivationMap is the index of the associated atom matcher. We assume
-//    // that matchers are indexed in the order that they are added to the config.
-//    const auto& activation1_1_1 = metricProducer1_1->mEventActivationMap.at(0);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1_1_1->ttl_ns);
-//    EXPECT_EQ(0, activation1_1_1->start_ns);
-//    EXPECT_EQ(kNotActive, activation1_1_1->state);
-//    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1_1_1->activationType);
-//
-//    const auto& activation1_1_2 = metricProducer1_1->mEventActivationMap.at(1);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation1_1_2->ttl_ns);
-//    EXPECT_EQ(0, activation1_1_2->start_ns);
-//    EXPECT_EQ(kNotActive, activation1_1_2->state);
-//    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1_1_2->activationType);
-//    // }}}------------------------------------------------------------------------------
-//
-//    // Trigger Activation 1 for Metric 1
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
-//    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 100 + timeBase1);
-//    processor1->OnLogEvent(event.get());
-//
-//    // Metric 1 is not active; Activation 1 set to kActiveOnBoot
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_FALSE(metricProducer1_1->isActive());
-//    EXPECT_EQ(0, activation1_1_1->start_ns);
-//    EXPECT_EQ(kActiveOnBoot, activation1_1_1->state);
-//    EXPECT_EQ(0, activation1_1_2->start_ns);
-//    EXPECT_EQ(kNotActive, activation1_1_2->state);
-//
-//    EXPECT_TRUE(metricProducer1_2->isActive());
-//    // }}}-----------------------------------------------------------------------------
-//
-//    // Simulate shutdown by saving state to disk
-//    int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
-//    processor1->SaveActiveConfigsToDisk(shutDownTime);
-//    EXPECT_FALSE(metricProducer1_1->isActive());
-//
-//    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
-//    // same config.
-//    long timeBase2 = 1000;
-//    sp<StatsLogProcessor> processor2 =
-//            CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
-//
-//    // Metric 1 is not active.
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_EQ(1, processor2->mMetricsManagers.size());
-//    it = processor2->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
-//    auto& metricsManager2 = it->second;
-//    EXPECT_TRUE(metricsManager2->isActive());
-//
-//    EXPECT_EQ(metricsManager2->mAllMetricProducers.size(), 2);
-//    // We assume that the index of a MetricProducer within the mAllMetricProducers
-//    // array follows the order in which metrics are added to the config.
-//    auto& metricProducer2_1 = metricsManager2->mAllMetricProducers[0];
-//    EXPECT_EQ(metricProducer2_1->getMetricId(), metricId1);
-//    EXPECT_FALSE(metricProducer2_1->isActive());
-//
-//    auto& metricProducer2_2 = metricsManager2->mAllMetricProducers[1];
-//    EXPECT_EQ(metricProducer2_2->getMetricId(), metricId2);
-//    EXPECT_TRUE(metricProducer2_2->isActive());
-//
-//    EXPECT_EQ(metricProducer2_1->mEventActivationMap.size(), 2);
-//    // The key in mEventActivationMap is the index of the associated atom matcher. We assume
-//    // that matchers are indexed in the order that they are added to the config.
-//    const auto& activation2_1_1 = metricProducer2_1->mEventActivationMap.at(0);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation2_1_1->ttl_ns);
-//    EXPECT_EQ(0, activation2_1_1->start_ns);
-//    EXPECT_EQ(kNotActive, activation2_1_1->state);
-//    EXPECT_EQ(ACTIVATE_ON_BOOT, activation2_1_1->activationType);
-//
-//    const auto& activation2_1_2 = metricProducer2_1->mEventActivationMap.at(1);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation2_1_2->ttl_ns);
-//    EXPECT_EQ(0, activation2_1_2->start_ns);
-//    EXPECT_EQ(kNotActive, activation2_1_2->state);
-//    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2_1_2->activationType);
-//    // }}}-----------------------------------------------------------------------------------
-//
-//    // Load saved state from disk.
-//    processor2->LoadActiveConfigsFromDisk();
-//
-//    // Metric 1 active; Activation 1 is active, Activation 2 is not active
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_TRUE(metricProducer2_1->isActive());
-//    int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
-//    EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns);
-//    EXPECT_EQ(kActive, activation2_1_1->state);
-//    EXPECT_EQ(0, activation2_1_2->start_ns);
-//    EXPECT_EQ(kNotActive, activation2_1_2->state);
-//
-//    EXPECT_TRUE(metricProducer2_2->isActive());
-//    // }}}--------------------------------------------------------------------------------
-//
-//    // Trigger Activation 2 for Metric 1.
-//    auto screenOnEvent = CreateScreenStateChangedEvent(
-//            android::view::DISPLAY_STATE_ON,
-//            timeBase2 + 200
-//    );
-//    processor2->OnLogEvent(screenOnEvent.get());
-//
-//    // Metric 1 active; Activation 1 is active, Activation 2 is active
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_TRUE(metricProducer2_1->isActive());
-//    EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns);
-//    EXPECT_EQ(kActive, activation2_1_1->state);
-//    EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation2_1_2->start_ns);
-//    EXPECT_EQ(kActive, activation2_1_2->state);
-//
-//    EXPECT_TRUE(metricProducer2_2->isActive());
-//    // }}}---------------------------------------------------------------------------
-//
-//    // Simulate shutdown by saving state to disk
-//    shutDownTime = timeBase2 + 50 * NS_PER_SEC;
-//    processor2->SaveActiveConfigsToDisk(shutDownTime);
-//    EXPECT_TRUE(metricProducer2_1->isActive());
-//    EXPECT_TRUE(metricProducer2_2->isActive());
-//    ttl1 -= shutDownTime - timeBase2;
-//    int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC
-//            - (shutDownTime - screenOnEvent->GetElapsedTimestampNs());
-//
-//    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
-//    // same config.
-//    long timeBase3 = timeBase2 + 120 * NS_PER_SEC;
-//    sp<StatsLogProcessor> processor3 =
-//            CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1);
-//
-//    // Metric 1 is not active.
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_EQ(1, processor3->mMetricsManagers.size());
-//    it = processor3->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor3->mMetricsManagers.end());
-//    auto& metricsManager3 = it->second;
-//    EXPECT_TRUE(metricsManager3->isActive());
-//
-//    EXPECT_EQ(metricsManager3->mAllMetricProducers.size(), 2);
-//    // We assume that the index of a MetricProducer within the mAllMetricProducers
-//    // array follows the order in which metrics are added to the config.
-//    auto& metricProducer3_1 = metricsManager3->mAllMetricProducers[0];
-//    EXPECT_EQ(metricProducer3_1->getMetricId(), metricId1);
-//    EXPECT_FALSE(metricProducer3_1->isActive());
-//
-//    auto& metricProducer3_2 = metricsManager3->mAllMetricProducers[1];
-//    EXPECT_EQ(metricProducer3_2->getMetricId(), metricId2);
-//    EXPECT_TRUE(metricProducer3_2->isActive());
-//
-//    EXPECT_EQ(metricProducer3_1->mEventActivationMap.size(), 2);
-//    // The key in mEventActivationMap is the index of the associated atom matcher. We assume
-//    // that matchers are indexed in the order that they are added to the config.
-//    const auto& activation3_1_1 = metricProducer3_1->mEventActivationMap.at(0);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation3_1_1->ttl_ns);
-//    EXPECT_EQ(0, activation3_1_1->start_ns);
-//    EXPECT_EQ(kNotActive, activation3_1_1->state);
-//    EXPECT_EQ(ACTIVATE_ON_BOOT, activation3_1_1->activationType);
-//
-//    const auto& activation3_1_2 = metricProducer3_1->mEventActivationMap.at(1);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation3_1_2->ttl_ns);
-//    EXPECT_EQ(0, activation3_1_2->start_ns);
-//    EXPECT_EQ(kNotActive, activation3_1_2->state);
-//    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation3_1_2->activationType);
-//    // }}}----------------------------------------------------------------------------------
-//
-//    // Load saved state from disk.
-//    processor3->LoadActiveConfigsFromDisk();
-//
-//    // Metric 1 active: Activation 1 is active, Activation 2 is active
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_TRUE(metricProducer3_1->isActive());
-//    EXPECT_EQ(timeBase3 + ttl1 - activation3_1_1->ttl_ns, activation3_1_1->start_ns);
-//    EXPECT_EQ(kActive, activation3_1_1->state);
-//    EXPECT_EQ(timeBase3 + ttl2 - activation3_1_2->ttl_ns, activation3_1_2->start_ns);
-//    EXPECT_EQ(kActive, activation3_1_2->state);
-//
-//    EXPECT_TRUE(metricProducer3_2->isActive());
-//    // }}}-------------------------------------------------------------------------------
-//
-//
-//    // Trigger Activation 2 for Metric 1 again.
-//    screenOnEvent = CreateScreenStateChangedEvent(
-//            android::view::DISPLAY_STATE_ON,
-//            timeBase3 + 100 * NS_PER_SEC
-//    );
-//    processor3->OnLogEvent(screenOnEvent.get());
-//
-//    // Metric 1 active; Activation 1 is inactive (above screenOnEvent causes ttl1 to expire),
-//    //                  Activation 2 is set to active
-//    // Metric 2 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_TRUE(metricProducer3_1->isActive());
-//    EXPECT_EQ(kNotActive, activation3_1_1->state);
-//    EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation3_1_2->start_ns);
-//    EXPECT_EQ(kActive, activation3_1_2->state);
-//
-//    EXPECT_TRUE(metricProducer3_2->isActive());
-//    // }}}---------------------------------------------------------------------------
-//}
-//
-//TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) {
-//    int uid = 9876;
-//    long configId = 12341;
-//
-//    // Create config with 3 metrics:
-//    // Metric 1: Activate on 2 activations, 1 on boot, 1 immediate.
-//    // Metric 2: Activate on 2 activations, 1 on boot, 1 immediate.
-//    // Metric 3: Always active
-//    StatsdConfig config1;
-//    config1.set_id(configId);
-//    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
-//    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
-//    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
-//    auto jobStartMatcher = CreateStartScheduledJobAtomMatcher();
-//    auto jobFinishMatcher = CreateFinishScheduledJobAtomMatcher();
-//    *config1.add_atom_matcher() = wakelockAcquireMatcher;
-//    *config1.add_atom_matcher() = screenOnMatcher;
-//    *config1.add_atom_matcher() = jobStartMatcher;
-//    *config1.add_atom_matcher() = jobFinishMatcher;
-//
-//    long metricId1 = 1234561;
-//    long metricId2 = 1234562;
-//    long metricId3 = 1234563;
-//
-//    auto countMetric1 = config1.add_count_metric();
-//    countMetric1->set_id(metricId1);
-//    countMetric1->set_what(wakelockAcquireMatcher.id());
-//    countMetric1->set_bucket(FIVE_MINUTES);
-//
-//    auto countMetric2 = config1.add_count_metric();
-//    countMetric2->set_id(metricId2);
-//    countMetric2->set_what(wakelockAcquireMatcher.id());
-//    countMetric2->set_bucket(FIVE_MINUTES);
-//
-//    auto countMetric3 = config1.add_count_metric();
-//    countMetric3->set_id(metricId3);
-//    countMetric3->set_what(wakelockAcquireMatcher.id());
-//    countMetric3->set_bucket(FIVE_MINUTES);
-//
-//    // Metric 1 activates on boot for wakelock acquire, immediately for screen on.
-//    auto metric1Activation = config1.add_metric_activation();
-//    metric1Activation->set_metric_id(metricId1);
-//    auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
-//    metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
-//    metric1ActivationTrigger1->set_ttl_seconds(100);
-//    metric1ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT);
-//    auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
-//    metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
-//    metric1ActivationTrigger2->set_ttl_seconds(200);
-//    metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
-//
-//    // Metric 2 activates on boot for scheduled job start, immediately for scheduled job finish.
-//    auto metric2Activation = config1.add_metric_activation();
-//    metric2Activation->set_metric_id(metricId2);
-//    auto metric2ActivationTrigger1 = metric2Activation->add_event_activation();
-//    metric2ActivationTrigger1->set_atom_matcher_id(jobStartMatcher.id());
-//    metric2ActivationTrigger1->set_ttl_seconds(100);
-//    metric2ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT);
-//    auto metric2ActivationTrigger2 = metric2Activation->add_event_activation();
-//    metric2ActivationTrigger2->set_atom_matcher_id(jobFinishMatcher.id());
-//    metric2ActivationTrigger2->set_ttl_seconds(200);
-//    metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
-//
-//    // Send the config.
-//    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
-//    string serialized = config1.SerializeAsString();
-//    service->addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
-//
-//    // Make sure the config is stored on disk. Otherwise, we will not reset on system server death.
-//    StatsdConfig tmpConfig;
-//    ConfigKey cfgKey1(uid, configId);
-//    EXPECT_TRUE(StorageManager::readConfigFromDisk(cfgKey1, &tmpConfig));
-//
-//    // Metric 1 is not active.
-//    // Metric 2 is not active.
-//    // Metric 3 is active.
-//    // {{{---------------------------------------------------------------------------
-//    sp<StatsLogProcessor> processor = service->mProcessor;
-//    EXPECT_EQ(1, processor->mMetricsManagers.size());
-//    auto it = processor->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor->mMetricsManagers.end());
-//    auto& metricsManager1 = it->second;
-//    EXPECT_TRUE(metricsManager1->isActive());
-//    EXPECT_EQ(3, metricsManager1->mAllMetricProducers.size());
-//
-//    auto& metricProducer1 = metricsManager1->mAllMetricProducers[0];
-//    EXPECT_EQ(metricId1, metricProducer1->getMetricId());
-//    EXPECT_FALSE(metricProducer1->isActive());
-//
-//    auto& metricProducer2 = metricsManager1->mAllMetricProducers[1];
-//    EXPECT_EQ(metricId2, metricProducer2->getMetricId());
-//    EXPECT_FALSE(metricProducer2->isActive());
-//
-//    auto& metricProducer3 = metricsManager1->mAllMetricProducers[2];
-//    EXPECT_EQ(metricId3, metricProducer3->getMetricId());
-//    EXPECT_TRUE(metricProducer3->isActive());
-//
-//    // Check event activations.
-//    EXPECT_EQ(metricsManager1->mAllAtomMatchers.size(), 4);
-//    EXPECT_EQ(metricsManager1->mAllAtomMatchers[0]->getId(),
-//              metric1ActivationTrigger1->atom_matcher_id());
-//    const auto& activation1 = metricProducer1->mEventActivationMap.at(0);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
-//    EXPECT_EQ(0, activation1->start_ns);
-//    EXPECT_EQ(kNotActive, activation1->state);
-//    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType);
-//
-//    EXPECT_EQ(metricsManager1->mAllAtomMatchers[1]->getId(),
-//              metric1ActivationTrigger2->atom_matcher_id());
-//    const auto& activation2 = metricProducer1->mEventActivationMap.at(1);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
-//    EXPECT_EQ(0, activation2->start_ns);
-//    EXPECT_EQ(kNotActive, activation2->state);
-//    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType);
-//
-//    EXPECT_EQ(metricsManager1->mAllAtomMatchers[2]->getId(),
-//              metric2ActivationTrigger1->atom_matcher_id());
-//    const auto& activation3 = metricProducer2->mEventActivationMap.at(2);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns);
-//    EXPECT_EQ(0, activation3->start_ns);
-//    EXPECT_EQ(kNotActive, activation3->state);
-//    EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType);
-//
-//    EXPECT_EQ(metricsManager1->mAllAtomMatchers[3]->getId(),
-//              metric2ActivationTrigger2->atom_matcher_id());
-//    const auto& activation4 = metricProducer2->mEventActivationMap.at(3);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns);
-//    EXPECT_EQ(0, activation4->start_ns);
-//    EXPECT_EQ(kNotActive, activation4->state);
-//    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation4->activationType);
-//    // }}}------------------------------------------------------------------------------
-//
-//    // Trigger Activation 1 for Metric 1. Should activate on boot.
-//    // Trigger Activation 4 for Metric 2. Should activate immediately.
-//    long configAddedTimeNs = metricsManager1->mLastReportTimeNs;
-//    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
-//    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 1 + configAddedTimeNs);
-//    processor->OnLogEvent(event.get());
-//
-//    event = CreateFinishScheduledJobEvent(attributions1, "finish1", 2 + configAddedTimeNs);
-//    processor->OnLogEvent(event.get());
-//
-//    // Metric 1 is not active; Activation 1 set to kActiveOnBoot
-//    // Metric 2 is active. Activation 4 set to kActive
-//    // Metric 3 is active.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_FALSE(metricProducer1->isActive());
-//    EXPECT_EQ(0, activation1->start_ns);
-//    EXPECT_EQ(kActiveOnBoot, activation1->state);
-//    EXPECT_EQ(0, activation2->start_ns);
-//    EXPECT_EQ(kNotActive, activation2->state);
-//
-//    EXPECT_TRUE(metricProducer2->isActive());
-//    EXPECT_EQ(0, activation3->start_ns);
-//    EXPECT_EQ(kNotActive, activation3->state);
-//    EXPECT_EQ(2 + configAddedTimeNs, activation4->start_ns);
-//    EXPECT_EQ(kActive, activation4->state);
-//
-//    EXPECT_TRUE(metricProducer3->isActive());
-//    // }}}-----------------------------------------------------------------------------
-//
-//    // Can't fake time with StatsService.
-//    // Lets get a time close to the system server death time and make sure it's sane.
-//    int64_t approximateSystemServerDeath = getElapsedRealtimeNs();
-//    EXPECT_TRUE(approximateSystemServerDeath > 2 + configAddedTimeNs);
-//    EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs);
-//
-//    // System server dies.
-//    service->statsCompanionServiceDiedImpl();
-//
-//    // We should have a new metrics manager. Lets get it and ensure activation status is restored.
-//    // {{{---------------------------------------------------------------------------
-//    EXPECT_EQ(1, processor->mMetricsManagers.size());
-//    it = processor->mMetricsManagers.find(cfgKey1);
-//    EXPECT_TRUE(it != processor->mMetricsManagers.end());
-//    auto& metricsManager2 = it->second;
-//    EXPECT_TRUE(metricsManager2->isActive());
-//    EXPECT_EQ(3, metricsManager2->mAllMetricProducers.size());
-//
-//    auto& metricProducer1001 = metricsManager2->mAllMetricProducers[0];
-//    EXPECT_EQ(metricId1, metricProducer1001->getMetricId());
-//    EXPECT_FALSE(metricProducer1001->isActive());
-//
-//    auto& metricProducer1002 = metricsManager2->mAllMetricProducers[1];
-//    EXPECT_EQ(metricId2, metricProducer1002->getMetricId());
-//    EXPECT_TRUE(metricProducer1002->isActive());
-//
-//    auto& metricProducer1003 = metricsManager2->mAllMetricProducers[2];
-//    EXPECT_EQ(metricId3, metricProducer1003->getMetricId());
-//    EXPECT_TRUE(metricProducer1003->isActive());
-//
-//    // Check event activations.
-//    // Activation 1 is kActiveOnBoot.
-//    // Activation 2 and 3 are not active.
-//    // Activation 4 is active.
-//    EXPECT_EQ(metricsManager2->mAllAtomMatchers.size(), 4);
-//    EXPECT_EQ(metricsManager2->mAllAtomMatchers[0]->getId(),
-//              metric1ActivationTrigger1->atom_matcher_id());
-//    const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
-//    EXPECT_EQ(0, activation1001->start_ns);
-//    EXPECT_EQ(kActiveOnBoot, activation1001->state);
-//    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType);
-//
-//    EXPECT_EQ(metricsManager2->mAllAtomMatchers[1]->getId(),
-//              metric1ActivationTrigger2->atom_matcher_id());
-//    const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns);
-//    EXPECT_EQ(0, activation1002->start_ns);
-//    EXPECT_EQ(kNotActive, activation1002->state);
-//    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType);
-//
-//    EXPECT_EQ(metricsManager2->mAllAtomMatchers[2]->getId(),
-//              metric2ActivationTrigger1->atom_matcher_id());
-//    const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2);
-//    EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
-//    EXPECT_EQ(0, activation1003->start_ns);
-//    EXPECT_EQ(kNotActive, activation1003->state);
-//    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType);
-//
-//    EXPECT_EQ(metricsManager2->mAllAtomMatchers[3]->getId(),
-//              metric2ActivationTrigger2->atom_matcher_id());
-//    const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3);
-//    EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns);
-//    EXPECT_EQ(2 + configAddedTimeNs, activation1004->start_ns);
-//    EXPECT_EQ(kActive, activation1004->state);
-//    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1004->activationType);
-//    // }}}------------------------------------------------------------------------------
-//
-//    // Clear the data stored on disk as a result of the system server death.
-//    vector<uint8_t> buffer;
-//    processor->onDumpReport(cfgKey1, configAddedTimeNs + NS_PER_SEC, false, true,
-//                                ADB_DUMP, FAST, &buffer);
-//}
+TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) {
+    // Setup a simple config.
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+    *config.add_atom_matcher() = wakelockAcquireMatcher;
+
+    auto countMetric = config.add_count_metric();
+    countMetric->set_id(123456);
+    countMetric->set_what(wakelockAcquireMatcher.id());
+    countMetric->set_bucket(FIVE_MINUTES);
+
+    ConfigKey cfgKey;
+    sp<StatsLogProcessor> processor = CreateStatsLogProcessor(1, 1, config, cfgKey);
+
+    std::vector<int> attributionUids = {111};
+    std::vector<string> attributionTags = {"App1"};
+    std::unique_ptr<LogEvent> event =
+            CreateAcquireWakelockEvent(2 /*timestamp*/, attributionUids, attributionTags, "wl1");
+    processor->OnLogEvent(event.get());
+
+    vector<uint8_t> bytes;
+    ConfigMetricsReportList output;
+
+    // Dump report WITHOUT erasing data.
+    processor->onDumpReport(cfgKey, 3, true, false /* Do NOT erase data. */, ADB_DUMP, FAST,
+                            &bytes);
+    output.ParseFromArray(bytes.data(), bytes.size());
+    EXPECT_EQ(output.reports_size(), 1);
+    EXPECT_EQ(output.reports(0).metrics_size(), 1);
+    EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
+
+    // Dump report WITH erasing data. There should be data since we didn't previously erase it.
+    processor->onDumpReport(cfgKey, 4, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes);
+    output.ParseFromArray(bytes.data(), bytes.size());
+    EXPECT_EQ(output.reports_size(), 1);
+    EXPECT_EQ(output.reports(0).metrics_size(), 1);
+    EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);
+
+    // Dump report again. There should be no data since we erased it.
+    processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, FAST, &bytes);
+    output.ParseFromArray(bytes.data(), bytes.size());
+    // We don't care whether statsd has a report, as long as it has no count metrics in it.
+    bool noData = output.reports_size() == 0 || output.reports(0).metrics_size() == 0 ||
+                  output.reports(0).metrics(0).count_metrics().data_size() == 0;
+    EXPECT_TRUE(noData);
+}
+
+TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead) {
+    int uid = 1111;
+
+    // Setup a simple config, no activation
+    StatsdConfig config1;
+    int64_t cfgId1 = 12341;
+    config1.set_id(cfgId1);
+    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+    *config1.add_atom_matcher() = wakelockAcquireMatcher;
+
+    long metricId1 = 1234561;
+    long metricId2 = 1234562;
+    auto countMetric1 = config1.add_count_metric();
+    countMetric1->set_id(metricId1);
+    countMetric1->set_what(wakelockAcquireMatcher.id());
+    countMetric1->set_bucket(FIVE_MINUTES);
+
+    auto countMetric2 = config1.add_count_metric();
+    countMetric2->set_id(metricId2);
+    countMetric2->set_what(wakelockAcquireMatcher.id());
+    countMetric2->set_bucket(FIVE_MINUTES);
+
+    ConfigKey cfgKey1(uid, cfgId1);
+
+    // Add another config, with two metrics, one with activation
+    StatsdConfig config2;
+    int64_t cfgId2 = 12342;
+    config2.set_id(cfgId2);
+    config2.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    *config2.add_atom_matcher() = wakelockAcquireMatcher;
+
+    long metricId3 = 1234561;
+    long metricId4 = 1234562;
+
+    auto countMetric3 = config2.add_count_metric();
+    countMetric3->set_id(metricId3);
+    countMetric3->set_what(wakelockAcquireMatcher.id());
+    countMetric3->set_bucket(FIVE_MINUTES);
+
+    auto countMetric4 = config2.add_count_metric();
+    countMetric4->set_id(metricId4);
+    countMetric4->set_what(wakelockAcquireMatcher.id());
+    countMetric4->set_bucket(FIVE_MINUTES);
+
+    auto metric3Activation = config2.add_metric_activation();
+    metric3Activation->set_metric_id(metricId3);
+    metric3Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
+    auto metric3ActivationTrigger = metric3Activation->add_event_activation();
+    metric3ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
+    metric3ActivationTrigger->set_ttl_seconds(100);
+
+    ConfigKey cfgKey2(uid, cfgId2);
+
+    // Add another config, with two metrics, both with activations
+    StatsdConfig config3;
+    int64_t cfgId3 = 12343;
+    config3.set_id(cfgId3);
+    config3.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    *config3.add_atom_matcher() = wakelockAcquireMatcher;
+
+    long metricId5 = 1234565;
+    long metricId6 = 1234566;
+    auto countMetric5 = config3.add_count_metric();
+    countMetric5->set_id(metricId5);
+    countMetric5->set_what(wakelockAcquireMatcher.id());
+    countMetric5->set_bucket(FIVE_MINUTES);
+
+    auto countMetric6 = config3.add_count_metric();
+    countMetric6->set_id(metricId6);
+    countMetric6->set_what(wakelockAcquireMatcher.id());
+    countMetric6->set_bucket(FIVE_MINUTES);
+
+    auto metric5Activation = config3.add_metric_activation();
+    metric5Activation->set_metric_id(metricId5);
+    metric5Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
+    auto metric5ActivationTrigger = metric5Activation->add_event_activation();
+    metric5ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
+    metric5ActivationTrigger->set_ttl_seconds(100);
+
+    auto metric6Activation = config3.add_metric_activation();
+    metric6Activation->set_metric_id(metricId6);
+    metric6Activation->set_activation_type(ACTIVATE_IMMEDIATELY);
+    auto metric6ActivationTrigger = metric6Activation->add_event_activation();
+    metric6ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
+    metric6ActivationTrigger->set_ttl_seconds(200);
+
+    ConfigKey cfgKey3(uid, cfgId3);
+
+    sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> subscriberAlarmMonitor;
+    vector<int64_t> activeConfigsBroadcast;
+
+    long timeBase1 = 1;
+    int broadcastCount = 0;
+    StatsLogProcessor processor(
+            m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, timeBase1,
+            [](const ConfigKey& key) { return true; },
+            [&uid, &broadcastCount, &activeConfigsBroadcast](const int& broadcastUid,
+                                                             const vector<int64_t>& activeConfigs) {
+                broadcastCount++;
+                EXPECT_EQ(broadcastUid, uid);
+                activeConfigsBroadcast.clear();
+                activeConfigsBroadcast.insert(activeConfigsBroadcast.end(), activeConfigs.begin(),
+                                              activeConfigs.end());
+                return true;
+            });
+
+    processor.OnConfigUpdated(1, cfgKey1, config1);
+    processor.OnConfigUpdated(2, cfgKey2, config2);
+    processor.OnConfigUpdated(3, cfgKey3, config3);
+
+    EXPECT_EQ(3, processor.mMetricsManagers.size());
+
+    // Expect the first config and both metrics in it to be active.
+    auto it = processor.mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor.mMetricsManagers.end());
+    auto& metricsManager1 = it->second;
+    EXPECT_TRUE(metricsManager1->isActive());
+
+    auto metricIt = metricsManager1->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId1) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+    auto& metricProducer1 = *metricIt;
+    EXPECT_TRUE(metricProducer1->isActive());
+
+    metricIt = metricsManager1->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId2) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+    auto& metricProducer2 = *metricIt;
+    EXPECT_TRUE(metricProducer2->isActive());
+
+    // Expect config 2 to be active. Metric 3 shouldn't be active, metric 4 should be active.
+    it = processor.mMetricsManagers.find(cfgKey2);
+    EXPECT_TRUE(it != processor.mMetricsManagers.end());
+    auto& metricsManager2 = it->second;
+    EXPECT_TRUE(metricsManager2->isActive());
+
+    metricIt = metricsManager2->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId3) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end());
+    auto& metricProducer3 = *metricIt;
+    EXPECT_FALSE(metricProducer3->isActive());
+
+    metricIt = metricsManager2->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId4) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager2->mAllMetricProducers.end());
+    auto& metricProducer4 = *metricIt;
+    EXPECT_TRUE(metricProducer4->isActive());
+
+    // Expect the third config and both metrics in it to be inactive.
+    it = processor.mMetricsManagers.find(cfgKey3);
+    EXPECT_TRUE(it != processor.mMetricsManagers.end());
+    auto& metricsManager3 = it->second;
+    EXPECT_FALSE(metricsManager3->isActive());
+
+    metricIt = metricsManager3->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager2->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId5) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end());
+    auto& metricProducer5 = *metricIt;
+    EXPECT_FALSE(metricProducer5->isActive());
+
+    metricIt = metricsManager3->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager3->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId6) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager3->mAllMetricProducers.end());
+    auto& metricProducer6 = *metricIt;
+    EXPECT_FALSE(metricProducer6->isActive());
+
+    // No broadcast for active configs should have happened yet.
+    EXPECT_EQ(broadcastCount, 0);
+
+    // Activate all 3 metrics that were not active.
+    std::vector<int> attributionUids = {111};
+    std::vector<string> attributionTags = {"App1"};
+    std::unique_ptr<LogEvent> event =
+            CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1");
+    processor.OnLogEvent(event.get());
+
+    // Assert that all 3 configs are active.
+    EXPECT_TRUE(metricsManager1->isActive());
+    EXPECT_TRUE(metricsManager2->isActive());
+    EXPECT_TRUE(metricsManager3->isActive());
+
+    // A broadcast should have happened, and all 3 configs should be active in the broadcast.
+    EXPECT_EQ(broadcastCount, 1);
+    EXPECT_EQ(activeConfigsBroadcast.size(), 3);
+    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId1) !=
+                activeConfigsBroadcast.end());
+    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId2) !=
+                activeConfigsBroadcast.end());
+    EXPECT_TRUE(std::find(activeConfigsBroadcast.begin(), activeConfigsBroadcast.end(), cfgId3) !=
+                activeConfigsBroadcast.end());
+
+    // When we shut down, metrics 3 & 5 have 100ns remaining, metric 6 has 100s + 100ns.
+    int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
+    processor.SaveActiveConfigsToDisk(shutDownTime);
+    const int64_t ttl3 = event->GetElapsedTimestampNs() +
+                         metric3ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
+    const int64_t ttl5 = event->GetElapsedTimestampNs() +
+                         metric5ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
+    const int64_t ttl6 = event->GetElapsedTimestampNs() +
+                         metric6ActivationTrigger->ttl_seconds() * NS_PER_SEC - shutDownTime;
+
+    // Create a second StatsLogProcessor and push the same 3 configs.
+    long timeBase2 = 1000;
+    sp<StatsLogProcessor> processor2 =
+            CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
+    processor2->OnConfigUpdated(timeBase2, cfgKey2, config2);
+    processor2->OnConfigUpdated(timeBase2, cfgKey3, config3);
+
+    EXPECT_EQ(3, processor2->mMetricsManagers.size());
+
+    // First config and both metrics are active.
+    it = processor2->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+    auto& metricsManager1001 = it->second;
+    EXPECT_TRUE(metricsManager1001->isActive());
+
+    metricIt = metricsManager1001->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId1) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+    auto& metricProducer1001 = *metricIt;
+    EXPECT_TRUE(metricProducer1001->isActive());
+
+    metricIt = metricsManager1001->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId2) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+    auto& metricProducer1002 = *metricIt;
+    EXPECT_TRUE(metricProducer1002->isActive());
+
+    // Second config is active. Metric 3 is inactive, metric 4 is active.
+    it = processor2->mMetricsManagers.find(cfgKey2);
+    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+    auto& metricsManager1002 = it->second;
+    EXPECT_TRUE(metricsManager1002->isActive());
+
+    metricIt = metricsManager1002->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId3) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end());
+    auto& metricProducer1003 = *metricIt;
+    EXPECT_FALSE(metricProducer1003->isActive());
+
+    metricIt = metricsManager1002->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId4) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1002->mAllMetricProducers.end());
+    auto& metricProducer1004 = *metricIt;
+    EXPECT_TRUE(metricProducer1004->isActive());
+
+    // Config 3 is inactive. both metrics are inactive.
+    it = processor2->mMetricsManagers.find(cfgKey3);
+    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+    auto& metricsManager1003 = it->second;
+    EXPECT_FALSE(metricsManager1003->isActive());
+    EXPECT_EQ(2, metricsManager1003->mAllMetricProducers.size());
+
+    metricIt = metricsManager1003->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1002->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId5) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end());
+    auto& metricProducer1005 = *metricIt;
+    EXPECT_FALSE(metricProducer1005->isActive());
+
+    metricIt = metricsManager1003->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1003->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId6) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1003->mAllMetricProducers.end());
+    auto& metricProducer1006 = *metricIt;
+    EXPECT_FALSE(metricProducer1006->isActive());
+
+    // Assert that all 3 metrics with activation are inactive and that the ttls were properly set.
+    EXPECT_FALSE(metricProducer1003->isActive());
+    const auto& activation1003 = metricProducer1003->mEventActivationMap.begin()->second;
+    EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
+    EXPECT_EQ(0, activation1003->start_ns);
+    EXPECT_FALSE(metricProducer1005->isActive());
+    const auto& activation1005 = metricProducer1005->mEventActivationMap.begin()->second;
+    EXPECT_EQ(100 * NS_PER_SEC, activation1005->ttl_ns);
+    EXPECT_EQ(0, activation1005->start_ns);
+    EXPECT_FALSE(metricProducer1006->isActive());
+    const auto& activation1006 = metricProducer1006->mEventActivationMap.begin()->second;
+    EXPECT_EQ(200 * NS_PER_SEC, activation1006->ttl_ns);
+    EXPECT_EQ(0, activation1006->start_ns);
+
+    processor2->LoadActiveConfigsFromDisk();
+
+    // After loading activations from disk, assert that all 3 metrics are active.
+    EXPECT_TRUE(metricProducer1003->isActive());
+    EXPECT_EQ(timeBase2 + ttl3 - activation1003->ttl_ns, activation1003->start_ns);
+    EXPECT_TRUE(metricProducer1005->isActive());
+    EXPECT_EQ(timeBase2 + ttl5 - activation1005->ttl_ns, activation1005->start_ns);
+    EXPECT_TRUE(metricProducer1006->isActive());
+    EXPECT_EQ(timeBase2 + ttl6 - activation1006->ttl_ns, activation1003->start_ns);
+
+    // Make sure no more broadcasts have happened.
+    EXPECT_EQ(broadcastCount, 1);
+}
+
+TEST(StatsLogProcessorTest, TestActivationOnBoot) {
+    int uid = 1111;
+
+    StatsdConfig config1;
+    config1.set_id(12341);
+    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+    *config1.add_atom_matcher() = wakelockAcquireMatcher;
+
+    long metricId1 = 1234561;
+    long metricId2 = 1234562;
+    auto countMetric1 = config1.add_count_metric();
+    countMetric1->set_id(metricId1);
+    countMetric1->set_what(wakelockAcquireMatcher.id());
+    countMetric1->set_bucket(FIVE_MINUTES);
+
+    auto countMetric2 = config1.add_count_metric();
+    countMetric2->set_id(metricId2);
+    countMetric2->set_what(wakelockAcquireMatcher.id());
+    countMetric2->set_bucket(FIVE_MINUTES);
+
+    auto metric1Activation = config1.add_metric_activation();
+    metric1Activation->set_metric_id(metricId1);
+    metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
+    auto metric1ActivationTrigger = metric1Activation->add_event_activation();
+    metric1ActivationTrigger->set_atom_matcher_id(wakelockAcquireMatcher.id());
+    metric1ActivationTrigger->set_ttl_seconds(100);
+
+    ConfigKey cfgKey1(uid, 12341);
+    long timeBase1 = 1;
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
+
+    EXPECT_EQ(1, processor->mMetricsManagers.size());
+    auto it = processor->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor->mMetricsManagers.end());
+    auto& metricsManager1 = it->second;
+    EXPECT_TRUE(metricsManager1->isActive());
+
+    auto metricIt = metricsManager1->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId1) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+    auto& metricProducer1 = *metricIt;
+    EXPECT_FALSE(metricProducer1->isActive());
+
+    metricIt = metricsManager1->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId2) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+    auto& metricProducer2 = *metricIt;
+    EXPECT_TRUE(metricProducer2->isActive());
+
+    const auto& activation1 = metricProducer1->mEventActivationMap.begin()->second;
+    EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
+    EXPECT_EQ(0, activation1->start_ns);
+    EXPECT_EQ(kNotActive, activation1->state);
+
+    std::vector<int> attributionUids = {111};
+    std::vector<string> attributionTags = {"App1"};
+    std::unique_ptr<LogEvent> event =
+            CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1");
+    processor->OnLogEvent(event.get());
+
+    EXPECT_FALSE(metricProducer1->isActive());
+    EXPECT_EQ(0, activation1->start_ns);
+    EXPECT_EQ(kActiveOnBoot, activation1->state);
+
+    int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
+    processor->SaveActiveConfigsToDisk(shutDownTime);
+    EXPECT_FALSE(metricProducer1->isActive());
+    const int64_t ttl1 = metric1ActivationTrigger->ttl_seconds() * NS_PER_SEC;
+
+    long timeBase2 = 1000;
+    sp<StatsLogProcessor> processor2 =
+            CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
+
+    EXPECT_EQ(1, processor2->mMetricsManagers.size());
+    it = processor2->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+    auto& metricsManager1001 = it->second;
+    EXPECT_TRUE(metricsManager1001->isActive());
+
+    metricIt = metricsManager1001->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId1) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+    auto& metricProducer1001 = *metricIt;
+    EXPECT_FALSE(metricProducer1001->isActive());
+
+    metricIt = metricsManager1001->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId2) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+    auto& metricProducer1002 = *metricIt;
+    EXPECT_TRUE(metricProducer1002->isActive());
+
+    const auto& activation1001 = metricProducer1001->mEventActivationMap.begin()->second;
+    EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
+    EXPECT_EQ(0, activation1001->start_ns);
+    EXPECT_EQ(kNotActive, activation1001->state);
+
+    processor2->LoadActiveConfigsFromDisk();
+
+    EXPECT_TRUE(metricProducer1001->isActive());
+    EXPECT_EQ(timeBase2 + ttl1 - activation1001->ttl_ns, activation1001->start_ns);
+    EXPECT_EQ(kActive, activation1001->state);
+}
+
+TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivations) {
+    int uid = 1111;
+
+    // Create config with 2 metrics:
+    // Metric 1: Activate on boot with 2 activations
+    // Metric 2: Always active
+    StatsdConfig config1;
+    config1.set_id(12341);
+    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config1.add_atom_matcher() = wakelockAcquireMatcher;
+    *config1.add_atom_matcher() = screenOnMatcher;
+
+    long metricId1 = 1234561;
+    long metricId2 = 1234562;
+
+    auto countMetric1 = config1.add_count_metric();
+    countMetric1->set_id(metricId1);
+    countMetric1->set_what(wakelockAcquireMatcher.id());
+    countMetric1->set_bucket(FIVE_MINUTES);
+
+    auto countMetric2 = config1.add_count_metric();
+    countMetric2->set_id(metricId2);
+    countMetric2->set_what(wakelockAcquireMatcher.id());
+    countMetric2->set_bucket(FIVE_MINUTES);
+
+    auto metric1Activation = config1.add_metric_activation();
+    metric1Activation->set_metric_id(metricId1);
+    metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
+    auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
+    metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
+    metric1ActivationTrigger1->set_ttl_seconds(100);
+    auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
+    metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
+    metric1ActivationTrigger2->set_ttl_seconds(200);
+
+    ConfigKey cfgKey1(uid, 12341);
+    long timeBase1 = 1;
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
+
+    // Metric 1 is not active.
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor->mMetricsManagers.size());
+    auto it = processor->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor->mMetricsManagers.end());
+    auto& metricsManager1 = it->second;
+    EXPECT_TRUE(metricsManager1->isActive());
+
+    auto metricIt = metricsManager1->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId1) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+    auto& metricProducer1 = *metricIt;
+    EXPECT_FALSE(metricProducer1->isActive());
+
+    metricIt = metricsManager1->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId2) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1->mAllMetricProducers.end());
+    auto& metricProducer2 = *metricIt;
+    EXPECT_TRUE(metricProducer2->isActive());
+
+    int i = 0;
+    for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
+        if (metricsManager1->mAllAtomMatchers[i]->getId() ==
+            metric1ActivationTrigger1->atom_matcher_id()) {
+            break;
+        }
+    }
+    const auto& activation1 = metricProducer1->mEventActivationMap.at(i);
+    EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
+    EXPECT_EQ(0, activation1->start_ns);
+    EXPECT_EQ(kNotActive, activation1->state);
+
+    i = 0;
+    for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
+        if (metricsManager1->mAllAtomMatchers[i]->getId() ==
+            metric1ActivationTrigger2->atom_matcher_id()) {
+            break;
+        }
+    }
+    const auto& activation2 = metricProducer1->mEventActivationMap.at(i);
+    EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
+    EXPECT_EQ(0, activation2->start_ns);
+    EXPECT_EQ(kNotActive, activation2->state);
+    // }}}------------------------------------------------------------------------------
+
+    // Trigger Activation 1 for Metric 1
+    std::vector<int> attributionUids = {111};
+    std::vector<string> attributionTags = {"App1"};
+    std::unique_ptr<LogEvent> event =
+            CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1");
+    processor->OnLogEvent(event.get());
+
+    // Metric 1 is not active; Activation 1 set to kActiveOnBoot
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_FALSE(metricProducer1->isActive());
+    EXPECT_EQ(0, activation1->start_ns);
+    EXPECT_EQ(kActiveOnBoot, activation1->state);
+    EXPECT_EQ(0, activation2->start_ns);
+    EXPECT_EQ(kNotActive, activation2->state);
+
+    EXPECT_TRUE(metricProducer2->isActive());
+    // }}}-----------------------------------------------------------------------------
+
+    // Simulate shutdown by saving state to disk
+    int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
+    processor->SaveActiveConfigsToDisk(shutDownTime);
+    EXPECT_FALSE(metricProducer1->isActive());
+    int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
+
+    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
+    // same config.
+    long timeBase2 = 1000;
+    sp<StatsLogProcessor> processor2 =
+            CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
+
+    // Metric 1 is not active.
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor2->mMetricsManagers.size());
+    it = processor2->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+    auto& metricsManager1001 = it->second;
+    EXPECT_TRUE(metricsManager1001->isActive());
+
+    metricIt = metricsManager1001->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId1) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+    auto& metricProducer1001 = *metricIt;
+    EXPECT_FALSE(metricProducer1001->isActive());
+
+    metricIt = metricsManager1001->mAllMetricProducers.begin();
+    for (; metricIt != metricsManager1001->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId2) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManager1001->mAllMetricProducers.end());
+    auto& metricProducer1002 = *metricIt;
+    EXPECT_TRUE(metricProducer1002->isActive());
+
+    i = 0;
+    for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
+        if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
+            metric1ActivationTrigger1->atom_matcher_id()) {
+            break;
+        }
+    }
+    const auto& activation1001_1 = metricProducer1001->mEventActivationMap.at(i);
+    EXPECT_EQ(100 * NS_PER_SEC, activation1001_1->ttl_ns);
+    EXPECT_EQ(0, activation1001_1->start_ns);
+    EXPECT_EQ(kNotActive, activation1001_1->state);
+
+    i = 0;
+    for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
+        if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
+            metric1ActivationTrigger2->atom_matcher_id()) {
+            break;
+        }
+    }
+
+    const auto& activation1001_2 = metricProducer1001->mEventActivationMap.at(i);
+    EXPECT_EQ(200 * NS_PER_SEC, activation1001_2->ttl_ns);
+    EXPECT_EQ(0, activation1001_2->start_ns);
+    EXPECT_EQ(kNotActive, activation1001_2->state);
+    // }}}-----------------------------------------------------------------------------------
+
+    // Load saved state from disk.
+    processor2->LoadActiveConfigsFromDisk();
+
+    // Metric 1 active; Activation 1 is active, Activation 2 is not active
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_TRUE(metricProducer1001->isActive());
+    EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
+    EXPECT_EQ(kActive, activation1001_1->state);
+    EXPECT_EQ(0, activation1001_2->start_ns);
+    EXPECT_EQ(kNotActive, activation1001_2->state);
+
+    EXPECT_TRUE(metricProducer1002->isActive());
+    // }}}--------------------------------------------------------------------------------
+
+    // Trigger Activation 2 for Metric 1.
+    auto screenOnEvent =
+            CreateScreenStateChangedEvent(timeBase2 + 200, android::view::DISPLAY_STATE_ON);
+    processor2->OnLogEvent(screenOnEvent.get());
+
+    // Metric 1 active; Activation 1 is active, Activation 2 is set to kActiveOnBoot
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_TRUE(metricProducer1001->isActive());
+    EXPECT_EQ(timeBase2 + ttl1 - activation1001_1->ttl_ns, activation1001_1->start_ns);
+    EXPECT_EQ(kActive, activation1001_1->state);
+    EXPECT_EQ(0, activation1001_2->start_ns);
+    EXPECT_EQ(kActiveOnBoot, activation1001_2->state);
+
+    EXPECT_TRUE(metricProducer1002->isActive());
+    // }}}---------------------------------------------------------------------------
+
+    // Simulate shutdown by saving state to disk
+    shutDownTime = timeBase2 + 50 * NS_PER_SEC;
+    processor2->SaveActiveConfigsToDisk(shutDownTime);
+    EXPECT_TRUE(metricProducer1001->isActive());
+    EXPECT_TRUE(metricProducer1002->isActive());
+    ttl1 = timeBase2 + metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC - shutDownTime;
+    int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC;
+
+    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
+    // same config.
+    long timeBase3 = timeBase2 + 120 * NS_PER_SEC;
+    sp<StatsLogProcessor> processor3 =
+            CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1);
+
+    // Metric 1 is not active.
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor3->mMetricsManagers.size());
+    it = processor3->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor3->mMetricsManagers.end());
+    auto& metricsManagerTimeBase3 = it->second;
+    EXPECT_TRUE(metricsManagerTimeBase3->isActive());
+
+    metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
+    for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId1) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
+    auto& metricProducerTimeBase3_1 = *metricIt;
+    EXPECT_FALSE(metricProducerTimeBase3_1->isActive());
+
+    metricIt = metricsManagerTimeBase3->mAllMetricProducers.begin();
+    for (; metricIt != metricsManagerTimeBase3->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId2) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManagerTimeBase3->mAllMetricProducers.end());
+    auto& metricProducerTimeBase3_2 = *metricIt;
+    EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+
+    i = 0;
+    for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
+        if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
+            metric1ActivationTrigger1->atom_matcher_id()) {
+            break;
+        }
+    }
+    const auto& activationTimeBase3_1 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
+    EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase3_1->ttl_ns);
+    EXPECT_EQ(0, activationTimeBase3_1->start_ns);
+    EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
+
+    i = 0;
+    for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
+        if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
+            metric1ActivationTrigger2->atom_matcher_id()) {
+            break;
+        }
+    }
+
+    const auto& activationTimeBase3_2 = metricProducerTimeBase3_1->mEventActivationMap.at(i);
+    EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase3_2->ttl_ns);
+    EXPECT_EQ(0, activationTimeBase3_2->start_ns);
+    EXPECT_EQ(kNotActive, activationTimeBase3_2->state);
+
+    EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+    // }}}----------------------------------------------------------------------------------
+
+    // Load saved state from disk.
+    processor3->LoadActiveConfigsFromDisk();
+
+    // Metric 1 active: Activation 1 is active, Activation 2 is active
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
+    EXPECT_EQ(timeBase3 + ttl1 - activationTimeBase3_1->ttl_ns, activationTimeBase3_1->start_ns);
+    EXPECT_EQ(kActive, activationTimeBase3_1->state);
+    EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns);
+    EXPECT_EQ(kActive, activationTimeBase3_2->state);
+
+    EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+    // }}}-------------------------------------------------------------------------------
+
+    // Trigger Activation 2 for Metric 1 again.
+    screenOnEvent = CreateScreenStateChangedEvent(timeBase3 + 100 * NS_PER_SEC,
+                                                  android::view::DISPLAY_STATE_ON);
+    processor3->OnLogEvent(screenOnEvent.get());
+
+    // Metric 1 active; Activation 1 is not active, Activation 2 is set to active
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_TRUE(metricProducerTimeBase3_1->isActive());
+    EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
+    EXPECT_EQ(timeBase3 + ttl2 - activationTimeBase3_2->ttl_ns, activationTimeBase3_2->start_ns);
+    EXPECT_EQ(kActive, activationTimeBase3_2->state);
+
+    EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
+    // }}}---------------------------------------------------------------------------
+
+    // Simulate shutdown by saving state to disk.
+    shutDownTime = timeBase3 + 500 * NS_PER_SEC;
+    processor3->SaveActiveConfigsToDisk(shutDownTime);
+    EXPECT_TRUE(metricProducer1001->isActive());
+    EXPECT_TRUE(metricProducer1002->isActive());
+    ttl1 = timeBase3 + ttl1 - shutDownTime;
+    ttl2 = timeBase3 + metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC - shutDownTime;
+
+    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
+    // same config.
+    long timeBase4 = timeBase3 + 600 * NS_PER_SEC;
+    sp<StatsLogProcessor> processor4 =
+            CreateStatsLogProcessor(timeBase4, timeBase4, config1, cfgKey1);
+
+    // Metric 1 is not active.
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor4->mMetricsManagers.size());
+    it = processor4->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor4->mMetricsManagers.end());
+    auto& metricsManagerTimeBase4 = it->second;
+    EXPECT_TRUE(metricsManagerTimeBase4->isActive());
+
+    metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin();
+    for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId1) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end());
+    auto& metricProducerTimeBase4_1 = *metricIt;
+    EXPECT_FALSE(metricProducerTimeBase4_1->isActive());
+
+    metricIt = metricsManagerTimeBase4->mAllMetricProducers.begin();
+    for (; metricIt != metricsManagerTimeBase4->mAllMetricProducers.end(); metricIt++) {
+        if ((*metricIt)->getMetricId() == metricId2) {
+            break;
+        }
+    }
+    EXPECT_TRUE(metricIt != metricsManagerTimeBase4->mAllMetricProducers.end());
+    auto& metricProducerTimeBase4_2 = *metricIt;
+    EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
+
+    i = 0;
+    for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) {
+        if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() ==
+            metric1ActivationTrigger1->atom_matcher_id()) {
+            break;
+        }
+    }
+    const auto& activationTimeBase4_1 = metricProducerTimeBase4_1->mEventActivationMap.at(i);
+    EXPECT_EQ(100 * NS_PER_SEC, activationTimeBase4_1->ttl_ns);
+    EXPECT_EQ(0, activationTimeBase4_1->start_ns);
+    EXPECT_EQ(kNotActive, activationTimeBase4_1->state);
+
+    i = 0;
+    for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) {
+        if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() ==
+            metric1ActivationTrigger2->atom_matcher_id()) {
+            break;
+        }
+    }
+
+    const auto& activationTimeBase4_2 = metricProducerTimeBase4_1->mEventActivationMap.at(i);
+    EXPECT_EQ(200 * NS_PER_SEC, activationTimeBase4_2->ttl_ns);
+    EXPECT_EQ(0, activationTimeBase4_2->start_ns);
+    EXPECT_EQ(kNotActive, activationTimeBase4_2->state);
+
+    EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
+    // }}}----------------------------------------------------------------------------------
+
+    // Load saved state from disk.
+    processor4->LoadActiveConfigsFromDisk();
+
+    // Metric 1 active: Activation 1 is not active, Activation 2 is not active
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_FALSE(metricProducerTimeBase4_1->isActive());
+    EXPECT_EQ(kNotActive, activationTimeBase4_1->state);
+    EXPECT_EQ(kNotActive, activationTimeBase4_2->state);
+
+    EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
+    // }}}-------------------------------------------------------------------------------
+}
+
+TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes) {
+    int uid = 1111;
+
+    // Create config with 2 metrics:
+    // Metric 1: Activate on boot with 2 activations
+    // Metric 2: Always active
+    StatsdConfig config1;
+    config1.set_id(12341);
+    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config1.add_atom_matcher() = wakelockAcquireMatcher;
+    *config1.add_atom_matcher() = screenOnMatcher;
+
+    long metricId1 = 1234561;
+    long metricId2 = 1234562;
+
+    auto countMetric1 = config1.add_count_metric();
+    countMetric1->set_id(metricId1);
+    countMetric1->set_what(wakelockAcquireMatcher.id());
+    countMetric1->set_bucket(FIVE_MINUTES);
+
+    auto countMetric2 = config1.add_count_metric();
+    countMetric2->set_id(metricId2);
+    countMetric2->set_what(wakelockAcquireMatcher.id());
+    countMetric2->set_bucket(FIVE_MINUTES);
+
+    auto metric1Activation = config1.add_metric_activation();
+    metric1Activation->set_metric_id(metricId1);
+    metric1Activation->set_activation_type(ACTIVATE_ON_BOOT);
+    auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
+    metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
+    metric1ActivationTrigger1->set_ttl_seconds(100);
+    auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
+    metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
+    metric1ActivationTrigger2->set_ttl_seconds(200);
+    metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
+
+    ConfigKey cfgKey1(uid, 12341);
+    long timeBase1 = 1;
+    sp<StatsLogProcessor> processor1 =
+            CreateStatsLogProcessor(timeBase1, timeBase1, config1, cfgKey1);
+
+    // Metric 1 is not active.
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor1->mMetricsManagers.size());
+    auto it = processor1->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor1->mMetricsManagers.end());
+    auto& metricsManager1 = it->second;
+    EXPECT_TRUE(metricsManager1->isActive());
+
+    EXPECT_EQ(metricsManager1->mAllMetricProducers.size(), 2);
+    // We assume that the index of a MetricProducer within the mAllMetricProducers
+    // array follows the order in which metrics are added to the config.
+    auto& metricProducer1_1 = metricsManager1->mAllMetricProducers[0];
+    EXPECT_EQ(metricProducer1_1->getMetricId(), metricId1);
+    EXPECT_FALSE(metricProducer1_1->isActive());  // inactive due to associated MetricActivation
+
+    auto& metricProducer1_2 = metricsManager1->mAllMetricProducers[1];
+    EXPECT_EQ(metricProducer1_2->getMetricId(), metricId2);
+    EXPECT_TRUE(metricProducer1_2->isActive());
+
+    EXPECT_EQ(metricProducer1_1->mEventActivationMap.size(), 2);
+    // The key in mEventActivationMap is the index of the associated atom matcher. We assume
+    // that matchers are indexed in the order that they are added to the config.
+    const auto& activation1_1_1 = metricProducer1_1->mEventActivationMap.at(0);
+    EXPECT_EQ(100 * NS_PER_SEC, activation1_1_1->ttl_ns);
+    EXPECT_EQ(0, activation1_1_1->start_ns);
+    EXPECT_EQ(kNotActive, activation1_1_1->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1_1_1->activationType);
+
+    const auto& activation1_1_2 = metricProducer1_1->mEventActivationMap.at(1);
+    EXPECT_EQ(200 * NS_PER_SEC, activation1_1_2->ttl_ns);
+    EXPECT_EQ(0, activation1_1_2->start_ns);
+    EXPECT_EQ(kNotActive, activation1_1_2->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1_1_2->activationType);
+    // }}}------------------------------------------------------------------------------
+
+    // Trigger Activation 1 for Metric 1
+    std::vector<int> attributionUids = {111};
+    std::vector<string> attributionTags = {"App1"};
+    std::unique_ptr<LogEvent> event =
+            CreateAcquireWakelockEvent(timeBase1 + 100, attributionUids, attributionTags, "wl1");
+    processor1->OnLogEvent(event.get());
+
+    // Metric 1 is not active; Activation 1 set to kActiveOnBoot
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_FALSE(metricProducer1_1->isActive());
+    EXPECT_EQ(0, activation1_1_1->start_ns);
+    EXPECT_EQ(kActiveOnBoot, activation1_1_1->state);
+    EXPECT_EQ(0, activation1_1_2->start_ns);
+    EXPECT_EQ(kNotActive, activation1_1_2->state);
+
+    EXPECT_TRUE(metricProducer1_2->isActive());
+    // }}}-----------------------------------------------------------------------------
+
+    // Simulate shutdown by saving state to disk
+    int64_t shutDownTime = timeBase1 + 100 * NS_PER_SEC;
+    processor1->SaveActiveConfigsToDisk(shutDownTime);
+    EXPECT_FALSE(metricProducer1_1->isActive());
+
+    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
+    // same config.
+    long timeBase2 = 1000;
+    sp<StatsLogProcessor> processor2 =
+            CreateStatsLogProcessor(timeBase2, timeBase2, config1, cfgKey1);
+
+    // Metric 1 is not active.
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor2->mMetricsManagers.size());
+    it = processor2->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor2->mMetricsManagers.end());
+    auto& metricsManager2 = it->second;
+    EXPECT_TRUE(metricsManager2->isActive());
+
+    EXPECT_EQ(metricsManager2->mAllMetricProducers.size(), 2);
+    // We assume that the index of a MetricProducer within the mAllMetricProducers
+    // array follows the order in which metrics are added to the config.
+    auto& metricProducer2_1 = metricsManager2->mAllMetricProducers[0];
+    EXPECT_EQ(metricProducer2_1->getMetricId(), metricId1);
+    EXPECT_FALSE(metricProducer2_1->isActive());
+
+    auto& metricProducer2_2 = metricsManager2->mAllMetricProducers[1];
+    EXPECT_EQ(metricProducer2_2->getMetricId(), metricId2);
+    EXPECT_TRUE(metricProducer2_2->isActive());
+
+    EXPECT_EQ(metricProducer2_1->mEventActivationMap.size(), 2);
+    // The key in mEventActivationMap is the index of the associated atom matcher. We assume
+    // that matchers are indexed in the order that they are added to the config.
+    const auto& activation2_1_1 = metricProducer2_1->mEventActivationMap.at(0);
+    EXPECT_EQ(100 * NS_PER_SEC, activation2_1_1->ttl_ns);
+    EXPECT_EQ(0, activation2_1_1->start_ns);
+    EXPECT_EQ(kNotActive, activation2_1_1->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation2_1_1->activationType);
+
+    const auto& activation2_1_2 = metricProducer2_1->mEventActivationMap.at(1);
+    EXPECT_EQ(200 * NS_PER_SEC, activation2_1_2->ttl_ns);
+    EXPECT_EQ(0, activation2_1_2->start_ns);
+    EXPECT_EQ(kNotActive, activation2_1_2->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2_1_2->activationType);
+    // }}}-----------------------------------------------------------------------------------
+
+    // Load saved state from disk.
+    processor2->LoadActiveConfigsFromDisk();
+
+    // Metric 1 active; Activation 1 is active, Activation 2 is not active
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_TRUE(metricProducer2_1->isActive());
+    int64_t ttl1 = metric1ActivationTrigger1->ttl_seconds() * NS_PER_SEC;
+    EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns);
+    EXPECT_EQ(kActive, activation2_1_1->state);
+    EXPECT_EQ(0, activation2_1_2->start_ns);
+    EXPECT_EQ(kNotActive, activation2_1_2->state);
+
+    EXPECT_TRUE(metricProducer2_2->isActive());
+    // }}}--------------------------------------------------------------------------------
+
+    // Trigger Activation 2 for Metric 1.
+    auto screenOnEvent =
+            CreateScreenStateChangedEvent(timeBase2 + 200, android::view::DISPLAY_STATE_ON);
+    processor2->OnLogEvent(screenOnEvent.get());
+
+    // Metric 1 active; Activation 1 is active, Activation 2 is active
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_TRUE(metricProducer2_1->isActive());
+    EXPECT_EQ(timeBase2 + ttl1 - activation2_1_1->ttl_ns, activation2_1_1->start_ns);
+    EXPECT_EQ(kActive, activation2_1_1->state);
+    EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation2_1_2->start_ns);
+    EXPECT_EQ(kActive, activation2_1_2->state);
+
+    EXPECT_TRUE(metricProducer2_2->isActive());
+    // }}}---------------------------------------------------------------------------
+
+    // Simulate shutdown by saving state to disk
+    shutDownTime = timeBase2 + 50 * NS_PER_SEC;
+    processor2->SaveActiveConfigsToDisk(shutDownTime);
+    EXPECT_TRUE(metricProducer2_1->isActive());
+    EXPECT_TRUE(metricProducer2_2->isActive());
+    ttl1 -= shutDownTime - timeBase2;
+    int64_t ttl2 = metric1ActivationTrigger2->ttl_seconds() * NS_PER_SEC -
+                   (shutDownTime - screenOnEvent->GetElapsedTimestampNs());
+
+    // Simulate device restarted state by creating new instance of StatsLogProcessor with the
+    // same config.
+    long timeBase3 = timeBase2 + 120 * NS_PER_SEC;
+    sp<StatsLogProcessor> processor3 =
+            CreateStatsLogProcessor(timeBase3, timeBase3, config1, cfgKey1);
+
+    // Metric 1 is not active.
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor3->mMetricsManagers.size());
+    it = processor3->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor3->mMetricsManagers.end());
+    auto& metricsManager3 = it->second;
+    EXPECT_TRUE(metricsManager3->isActive());
+
+    EXPECT_EQ(metricsManager3->mAllMetricProducers.size(), 2);
+    // We assume that the index of a MetricProducer within the mAllMetricProducers
+    // array follows the order in which metrics are added to the config.
+    auto& metricProducer3_1 = metricsManager3->mAllMetricProducers[0];
+    EXPECT_EQ(metricProducer3_1->getMetricId(), metricId1);
+    EXPECT_FALSE(metricProducer3_1->isActive());
+
+    auto& metricProducer3_2 = metricsManager3->mAllMetricProducers[1];
+    EXPECT_EQ(metricProducer3_2->getMetricId(), metricId2);
+    EXPECT_TRUE(metricProducer3_2->isActive());
+
+    EXPECT_EQ(metricProducer3_1->mEventActivationMap.size(), 2);
+    // The key in mEventActivationMap is the index of the associated atom matcher. We assume
+    // that matchers are indexed in the order that they are added to the config.
+    const auto& activation3_1_1 = metricProducer3_1->mEventActivationMap.at(0);
+    EXPECT_EQ(100 * NS_PER_SEC, activation3_1_1->ttl_ns);
+    EXPECT_EQ(0, activation3_1_1->start_ns);
+    EXPECT_EQ(kNotActive, activation3_1_1->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation3_1_1->activationType);
+
+    const auto& activation3_1_2 = metricProducer3_1->mEventActivationMap.at(1);
+    EXPECT_EQ(200 * NS_PER_SEC, activation3_1_2->ttl_ns);
+    EXPECT_EQ(0, activation3_1_2->start_ns);
+    EXPECT_EQ(kNotActive, activation3_1_2->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation3_1_2->activationType);
+    // }}}----------------------------------------------------------------------------------
+
+    // Load saved state from disk.
+    processor3->LoadActiveConfigsFromDisk();
+
+    // Metric 1 active: Activation 1 is active, Activation 2 is active
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_TRUE(metricProducer3_1->isActive());
+    EXPECT_EQ(timeBase3 + ttl1 - activation3_1_1->ttl_ns, activation3_1_1->start_ns);
+    EXPECT_EQ(kActive, activation3_1_1->state);
+    EXPECT_EQ(timeBase3 + ttl2 - activation3_1_2->ttl_ns, activation3_1_2->start_ns);
+    EXPECT_EQ(kActive, activation3_1_2->state);
+
+    EXPECT_TRUE(metricProducer3_2->isActive());
+    // }}}-------------------------------------------------------------------------------
+
+    // Trigger Activation 2 for Metric 1 again.
+    screenOnEvent = CreateScreenStateChangedEvent(timeBase3 + 100 * NS_PER_SEC,
+                                                  android::view::DISPLAY_STATE_ON);
+    processor3->OnLogEvent(screenOnEvent.get());
+
+    // Metric 1 active; Activation 1 is inactive (above screenOnEvent causes ttl1 to expire),
+    //                  Activation 2 is set to active
+    // Metric 2 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_TRUE(metricProducer3_1->isActive());
+    EXPECT_EQ(kNotActive, activation3_1_1->state);
+    EXPECT_EQ(screenOnEvent->GetElapsedTimestampNs(), activation3_1_2->start_ns);
+    EXPECT_EQ(kActive, activation3_1_2->state);
+
+    EXPECT_TRUE(metricProducer3_2->isActive());
+    // }}}---------------------------------------------------------------------------
+}
+
+TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart) {
+    int uid = 9876;
+    long configId = 12341;
+
+    // Create config with 3 metrics:
+    // Metric 1: Activate on 2 activations, 1 on boot, 1 immediate.
+    // Metric 2: Activate on 2 activations, 1 on boot, 1 immediate.
+    // Metric 3: Always active
+    StatsdConfig config1;
+    config1.set_id(configId);
+    config1.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    auto jobStartMatcher = CreateStartScheduledJobAtomMatcher();
+    auto jobFinishMatcher = CreateFinishScheduledJobAtomMatcher();
+    *config1.add_atom_matcher() = wakelockAcquireMatcher;
+    *config1.add_atom_matcher() = screenOnMatcher;
+    *config1.add_atom_matcher() = jobStartMatcher;
+    *config1.add_atom_matcher() = jobFinishMatcher;
+
+    long metricId1 = 1234561;
+    long metricId2 = 1234562;
+    long metricId3 = 1234563;
+
+    auto countMetric1 = config1.add_count_metric();
+    countMetric1->set_id(metricId1);
+    countMetric1->set_what(wakelockAcquireMatcher.id());
+    countMetric1->set_bucket(FIVE_MINUTES);
+
+    auto countMetric2 = config1.add_count_metric();
+    countMetric2->set_id(metricId2);
+    countMetric2->set_what(wakelockAcquireMatcher.id());
+    countMetric2->set_bucket(FIVE_MINUTES);
+
+    auto countMetric3 = config1.add_count_metric();
+    countMetric3->set_id(metricId3);
+    countMetric3->set_what(wakelockAcquireMatcher.id());
+    countMetric3->set_bucket(FIVE_MINUTES);
+
+    // Metric 1 activates on boot for wakelock acquire, immediately for screen on.
+    auto metric1Activation = config1.add_metric_activation();
+    metric1Activation->set_metric_id(metricId1);
+    auto metric1ActivationTrigger1 = metric1Activation->add_event_activation();
+    metric1ActivationTrigger1->set_atom_matcher_id(wakelockAcquireMatcher.id());
+    metric1ActivationTrigger1->set_ttl_seconds(100);
+    metric1ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT);
+    auto metric1ActivationTrigger2 = metric1Activation->add_event_activation();
+    metric1ActivationTrigger2->set_atom_matcher_id(screenOnMatcher.id());
+    metric1ActivationTrigger2->set_ttl_seconds(200);
+    metric1ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
+
+    // Metric 2 activates on boot for scheduled job start, immediately for scheduled job finish.
+    auto metric2Activation = config1.add_metric_activation();
+    metric2Activation->set_metric_id(metricId2);
+    auto metric2ActivationTrigger1 = metric2Activation->add_event_activation();
+    metric2ActivationTrigger1->set_atom_matcher_id(jobStartMatcher.id());
+    metric2ActivationTrigger1->set_ttl_seconds(100);
+    metric2ActivationTrigger1->set_activation_type(ACTIVATE_ON_BOOT);
+    auto metric2ActivationTrigger2 = metric2Activation->add_event_activation();
+    metric2ActivationTrigger2->set_atom_matcher_id(jobFinishMatcher.id());
+    metric2ActivationTrigger2->set_ttl_seconds(200);
+    metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
+
+    // Send the config.
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+    string serialized = config1.SerializeAsString();
+    service->addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
+
+    // Make sure the config is stored on disk. Otherwise, we will not reset on system server death.
+    StatsdConfig tmpConfig;
+    ConfigKey cfgKey1(uid, configId);
+    EXPECT_TRUE(StorageManager::readConfigFromDisk(cfgKey1, &tmpConfig));
+
+    // Metric 1 is not active.
+    // Metric 2 is not active.
+    // Metric 3 is active.
+    // {{{---------------------------------------------------------------------------
+    sp<StatsLogProcessor> processor = service->mProcessor;
+    EXPECT_EQ(1, processor->mMetricsManagers.size());
+    auto it = processor->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor->mMetricsManagers.end());
+    auto& metricsManager1 = it->second;
+    EXPECT_TRUE(metricsManager1->isActive());
+    EXPECT_EQ(3, metricsManager1->mAllMetricProducers.size());
+
+    auto& metricProducer1 = metricsManager1->mAllMetricProducers[0];
+    EXPECT_EQ(metricId1, metricProducer1->getMetricId());
+    EXPECT_FALSE(metricProducer1->isActive());
+
+    auto& metricProducer2 = metricsManager1->mAllMetricProducers[1];
+    EXPECT_EQ(metricId2, metricProducer2->getMetricId());
+    EXPECT_FALSE(metricProducer2->isActive());
+
+    auto& metricProducer3 = metricsManager1->mAllMetricProducers[2];
+    EXPECT_EQ(metricId3, metricProducer3->getMetricId());
+    EXPECT_TRUE(metricProducer3->isActive());
+
+    // Check event activations.
+    EXPECT_EQ(metricsManager1->mAllAtomMatchers.size(), 4);
+    EXPECT_EQ(metricsManager1->mAllAtomMatchers[0]->getId(),
+              metric1ActivationTrigger1->atom_matcher_id());
+    const auto& activation1 = metricProducer1->mEventActivationMap.at(0);
+    EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
+    EXPECT_EQ(0, activation1->start_ns);
+    EXPECT_EQ(kNotActive, activation1->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType);
+
+    EXPECT_EQ(metricsManager1->mAllAtomMatchers[1]->getId(),
+              metric1ActivationTrigger2->atom_matcher_id());
+    const auto& activation2 = metricProducer1->mEventActivationMap.at(1);
+    EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
+    EXPECT_EQ(0, activation2->start_ns);
+    EXPECT_EQ(kNotActive, activation2->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType);
+
+    EXPECT_EQ(metricsManager1->mAllAtomMatchers[2]->getId(),
+              metric2ActivationTrigger1->atom_matcher_id());
+    const auto& activation3 = metricProducer2->mEventActivationMap.at(2);
+    EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns);
+    EXPECT_EQ(0, activation3->start_ns);
+    EXPECT_EQ(kNotActive, activation3->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType);
+
+    EXPECT_EQ(metricsManager1->mAllAtomMatchers[3]->getId(),
+              metric2ActivationTrigger2->atom_matcher_id());
+    const auto& activation4 = metricProducer2->mEventActivationMap.at(3);
+    EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns);
+    EXPECT_EQ(0, activation4->start_ns);
+    EXPECT_EQ(kNotActive, activation4->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation4->activationType);
+    // }}}------------------------------------------------------------------------------
+
+    // Trigger Activation 1 for Metric 1. Should activate on boot.
+    // Trigger Activation 4 for Metric 2. Should activate immediately.
+    long configAddedTimeNs = metricsManager1->mLastReportTimeNs;
+    std::vector<int> attributionUids = {111};
+    std::vector<string> attributionTags = {"App1"};
+    std::unique_ptr<LogEvent> event1 = CreateAcquireWakelockEvent(
+            1 + configAddedTimeNs, attributionUids, attributionTags, "wl1");
+    processor->OnLogEvent(event1.get());
+
+    std::unique_ptr<LogEvent> event2 = CreateFinishScheduledJobEvent(
+            2 + configAddedTimeNs, attributionUids, attributionTags, "finish1");
+    processor->OnLogEvent(event2.get());
+
+    // Metric 1 is not active; Activation 1 set to kActiveOnBoot
+    // Metric 2 is active. Activation 4 set to kActive
+    // Metric 3 is active.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_FALSE(metricProducer1->isActive());
+    EXPECT_EQ(0, activation1->start_ns);
+    EXPECT_EQ(kActiveOnBoot, activation1->state);
+    EXPECT_EQ(0, activation2->start_ns);
+    EXPECT_EQ(kNotActive, activation2->state);
+
+    EXPECT_TRUE(metricProducer2->isActive());
+    EXPECT_EQ(0, activation3->start_ns);
+    EXPECT_EQ(kNotActive, activation3->state);
+    EXPECT_EQ(2 + configAddedTimeNs, activation4->start_ns);
+    EXPECT_EQ(kActive, activation4->state);
+
+    EXPECT_TRUE(metricProducer3->isActive());
+    // }}}-----------------------------------------------------------------------------
+
+    // Can't fake time with StatsService.
+    // Lets get a time close to the system server death time and make sure it's sane.
+    int64_t approximateSystemServerDeath = getElapsedRealtimeNs();
+    EXPECT_TRUE(approximateSystemServerDeath > 2 + configAddedTimeNs);
+    EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs);
+
+    // System server dies.
+    service->statsCompanionServiceDiedImpl();
+
+    // We should have a new metrics manager. Lets get it and ensure activation status is restored.
+    // {{{---------------------------------------------------------------------------
+    EXPECT_EQ(1, processor->mMetricsManagers.size());
+    it = processor->mMetricsManagers.find(cfgKey1);
+    EXPECT_TRUE(it != processor->mMetricsManagers.end());
+    auto& metricsManager2 = it->second;
+    EXPECT_TRUE(metricsManager2->isActive());
+    EXPECT_EQ(3, metricsManager2->mAllMetricProducers.size());
+
+    auto& metricProducer1001 = metricsManager2->mAllMetricProducers[0];
+    EXPECT_EQ(metricId1, metricProducer1001->getMetricId());
+    EXPECT_FALSE(metricProducer1001->isActive());
+
+    auto& metricProducer1002 = metricsManager2->mAllMetricProducers[1];
+    EXPECT_EQ(metricId2, metricProducer1002->getMetricId());
+    EXPECT_TRUE(metricProducer1002->isActive());
+
+    auto& metricProducer1003 = metricsManager2->mAllMetricProducers[2];
+    EXPECT_EQ(metricId3, metricProducer1003->getMetricId());
+    EXPECT_TRUE(metricProducer1003->isActive());
+
+    // Check event activations.
+    // Activation 1 is kActiveOnBoot.
+    // Activation 2 and 3 are not active.
+    // Activation 4 is active.
+    EXPECT_EQ(metricsManager2->mAllAtomMatchers.size(), 4);
+    EXPECT_EQ(metricsManager2->mAllAtomMatchers[0]->getId(),
+              metric1ActivationTrigger1->atom_matcher_id());
+    const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0);
+    EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
+    EXPECT_EQ(0, activation1001->start_ns);
+    EXPECT_EQ(kActiveOnBoot, activation1001->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType);
+
+    EXPECT_EQ(metricsManager2->mAllAtomMatchers[1]->getId(),
+              metric1ActivationTrigger2->atom_matcher_id());
+    const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1);
+    EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns);
+    EXPECT_EQ(0, activation1002->start_ns);
+    EXPECT_EQ(kNotActive, activation1002->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType);
+
+    EXPECT_EQ(metricsManager2->mAllAtomMatchers[2]->getId(),
+              metric2ActivationTrigger1->atom_matcher_id());
+    const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2);
+    EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
+    EXPECT_EQ(0, activation1003->start_ns);
+    EXPECT_EQ(kNotActive, activation1003->state);
+    EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType);
+
+    EXPECT_EQ(metricsManager2->mAllAtomMatchers[3]->getId(),
+              metric2ActivationTrigger2->atom_matcher_id());
+    const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3);
+    EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns);
+    EXPECT_EQ(2 + configAddedTimeNs, activation1004->start_ns);
+    EXPECT_EQ(kActive, activation1004->state);
+    EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1004->activationType);
+    // }}}------------------------------------------------------------------------------
+
+    // Clear the data stored on disk as a result of the system server death.
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey1, configAddedTimeNs + NS_PER_SEC, false, true, ADB_DUMP, FAST,
+                            &buffer);
+}
 
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index a49c18f..29005a2 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -39,35 +39,28 @@
 const string kApp1 = "app1.sharing.1";
 const string kApp2 = "app2.sharing.1";
 
-// TODO(b/149590301): Update this test to use new socket schema.
-//TEST(UidMapTest, TestIsolatedUID) {
-//    sp<UidMap> m = new UidMap();
-//    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-//    sp<AlarmMonitor> anomalyAlarmMonitor;
-//    sp<AlarmMonitor> subscriberAlarmMonitor;
-//    // Construct the processor with a dummy sendBroadcast function that does nothing.
-//    StatsLogProcessor p(m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
-//                        [](const ConfigKey& key) { return true; },
-//                        [](const int&, const vector<int64_t>&) {return true;});
-//    LogEvent addEvent(util::ISOLATED_UID_CHANGED, 1);
-//    addEvent.write(100);  // parent UID
-//    addEvent.write(101);  // isolated UID
-//    addEvent.write(1);    // Indicates creation.
-//    addEvent.init();
-//
-//    EXPECT_EQ(101, m->getHostUidOrSelf(101));
-//
-//    p.OnLogEvent(&addEvent);
-//    EXPECT_EQ(100, m->getHostUidOrSelf(101));
-//
-//    LogEvent removeEvent(util::ISOLATED_UID_CHANGED, 1);
-//    removeEvent.write(100);  // parent UID
-//    removeEvent.write(101);  // isolated UID
-//    removeEvent.write(0);    // Indicates removal.
-//    removeEvent.init();
-//    p.OnLogEvent(&removeEvent);
-//    EXPECT_EQ(101, m->getHostUidOrSelf(101));
-//}
+TEST(UidMapTest, TestIsolatedUID) {
+    sp<UidMap> m = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> subscriberAlarmMonitor;
+    // Construct the processor with a dummy sendBroadcast function that does nothing.
+    StatsLogProcessor p(
+            m, pullerManager, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+            [](const ConfigKey& key) { return true; },
+            [](const int&, const vector<int64_t>&) { return true; });
+
+    std::unique_ptr<LogEvent> addEvent = CreateIsolatedUidChangedEvent(
+            1 /*timestamp*/, 100 /*hostUid*/, 101 /*isolatedUid*/, 1 /*is_create*/);
+    EXPECT_EQ(101, m->getHostUidOrSelf(101));
+    p.OnLogEvent(addEvent.get());
+    EXPECT_EQ(100, m->getHostUidOrSelf(101));
+
+    std::unique_ptr<LogEvent> removeEvent = CreateIsolatedUidChangedEvent(
+            1 /*timestamp*/, 100 /*hostUid*/, 101 /*isolatedUid*/, 0 /*is_create*/);
+    p.OnLogEvent(removeEvent.get());
+    EXPECT_EQ(101, m->getHostUidOrSelf(101));
+}
 
 TEST(UidMapTest, TestMatching) {
     UidMap m;
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index 0f39efd5..e58bbb7 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -13,14 +13,17 @@
 // limitations under the License.
 
 #include "src/metrics/EventMetricProducer.h"
-#include "metrics_test_helper.h"
-#include "tests/statsd_test_util.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <stdio.h>
+
 #include <vector>
 
+#include "metrics_test_helper.h"
+#include "stats_event.h"
+#include "tests/statsd_test_util.h"
+
 using namespace testing;
 using android::sp;
 using std::set;
@@ -35,6 +38,22 @@
 
 const ConfigKey kConfigKey(0, 12345);
 
+namespace {
+void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, string str) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    AStatsEvent_writeString(statsEvent, str.c_str());
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+}
+}  // anonymous namespace
+
 TEST(EventMetricProducerTest, TestNoCondition) {
     int64_t bucketStartTimeNs = 10000000000;
     int64_t eventStartTimeNs = bucketStartTimeNs + 1;
@@ -43,8 +62,11 @@
     EventMetric metric;
     metric.set_id(1);
 
-    LogEvent event1(1 /*tag id*/, bucketStartTimeNs + 1);
-    LogEvent event2(1 /*tag id*/, bucketStartTimeNs + 2);
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    CreateNoValuesLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1);
+
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 2);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
@@ -54,8 +76,17 @@
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
 
-    // TODO(b/110561136): get the report and check the content after the ProtoOutputStream change
-    // is done eventProducer.onDumpReport();
+    // Check dump report content.
+    ProtoOutputStream output;
+    std::set<string> strSet;
+    eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/,
+                               true /*erase data*/, FAST, &strSet, &output);
+
+    StatsLogReport report = outputStreamToProto(&output);
+    EXPECT_TRUE(report.has_event_metrics());
+    EXPECT_EQ(2, report.event_metrics().data_size());
+    EXPECT_EQ(bucketStartTimeNs + 1, report.event_metrics().data(0).elapsed_timestamp_nanos());
+    EXPECT_EQ(bucketStartTimeNs + 2, report.event_metrics().data(1).elapsed_timestamp_nanos());
 }
 
 TEST(EventMetricProducerTest, TestEventsWithNonSlicedCondition) {
@@ -67,8 +98,11 @@
     metric.set_id(1);
     metric.set_condition(StringToId("SCREEN_ON"));
 
-    LogEvent event1(1, bucketStartTimeNs + 1);
-    LogEvent event2(1, bucketStartTimeNs + 10);
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    CreateNoValuesLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1);
+
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
@@ -81,51 +115,67 @@
 
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
 
-    // TODO: get the report and check the content after the ProtoOutputStream change is done.
-    // eventProducer.onDumpReport();
+    // Check dump report content.
+    ProtoOutputStream output;
+    std::set<string> strSet;
+    eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/,
+                               true /*erase data*/, FAST, &strSet, &output);
+
+    StatsLogReport report = outputStreamToProto(&output);
+    EXPECT_TRUE(report.has_event_metrics());
+    EXPECT_EQ(1, report.event_metrics().data_size());
+    EXPECT_EQ(bucketStartTimeNs + 1, report.event_metrics().data(0).elapsed_timestamp_nanos());
 }
 
-// TODO(b/149590301): Update this test to use new socket schema.
-//TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) {
-//    int64_t bucketStartTimeNs = 10000000000;
-//    int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
-//
-//    int tagId = 1;
-//    int conditionTagId = 2;
-//
-//    EventMetric metric;
-//    metric.set_id(1);
-//    metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
-//    MetricConditionLink* link = metric.add_links();
-//    link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
-//    buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what());
-//    buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
-//
-//    LogEvent event1(tagId, bucketStartTimeNs + 1);
-//    EXPECT_TRUE(event1.write("111"));
-//    event1.init();
-//    ConditionKey key1;
-//    key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "111")};
-//
-//    LogEvent event2(tagId, bucketStartTimeNs + 10);
-//    EXPECT_TRUE(event2.write("222"));
-//    event2.init();
-//    ConditionKey key2;
-//    key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "222")};
-//
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse));
-//
-//    EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
-//
-//    EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
-//
-//    eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
-//    eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
-//
-//    // TODO: get the report and check the content after the ProtoOutputStream change is done.
-//    // eventProducer.onDumpReport();
-//}
+TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) {
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
+
+    int tagId = 1;
+    int conditionTagId = 2;
+
+    EventMetric metric;
+    metric.set_id(1);
+    metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
+    MetricConditionLink* link = metric.add_links();
+    link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
+    buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what());
+    buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
+
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1, "111");
+    ConditionKey key1;
+    key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
+            getMockedDimensionKey(conditionTagId, 2, "111")};
+
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10, "222");
+    ConditionKey key2;
+    key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
+            getMockedDimensionKey(conditionTagId, 2, "222")};
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    // Condition is false for first event.
+    EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse));
+    // Condition is true for second event.
+    EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
+
+    EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
+
+    eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
+    eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
+
+    // Check dump report content.
+    ProtoOutputStream output;
+    std::set<string> strSet;
+    eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/,
+                               true /*erase data*/, FAST, &strSet, &output);
+
+    StatsLogReport report = outputStreamToProto(&output);
+    EXPECT_TRUE(report.has_event_metrics());
+    EXPECT_EQ(1, report.event_metrics().data_size());
+    EXPECT_EQ(bucketStartTimeNs + 10, report.event_metrics().data(0).elapsed_timestamp_nanos());
+}
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 609324e..d372ffd 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -26,6 +26,7 @@
 #include "src/matchers/SimpleLogMatchingTracker.h"
 #include "src/metrics/MetricProducer.h"
 #include "src/stats_log_util.h"
+#include "stats_event.h"
 #include "tests/statsd_test_util.h"
 
 using namespace testing;
@@ -53,6 +54,28 @@
 const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
 const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
 
+namespace {
+shared_ptr<LogEvent> makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t value1, string str1,
+                                  int32_t value2) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    AStatsEvent_writeInt32(statsEvent, value1);
+    AStatsEvent_writeString(statsEvent, str1.c_str());
+    AStatsEvent_writeInt32(statsEvent, value2);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+
+    return logEvent;
+}
+}  // anonymous namespace
+
 /*
  * Tests that the first bucket works correctly
  */
@@ -88,769 +111,685 @@
     EXPECT_EQ(660000000005, gaugeProducer.getCurrentBucketEndTimeNs());
 }
 
-// TODO(b/149590301): Update these tests to use new socket schema.
-//TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) {
-//    GaugeMetric metric;
-//    metric.set_id(metricId);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.mutable_gauge_fields_filter()->set_include_all(false);
-//    metric.set_max_pull_delay_sec(INT_MAX);
-//    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
-//    gaugeFieldMatcher->set_field(tagId);
-//    gaugeFieldMatcher->add_child()->set_field(1);
-//    gaugeFieldMatcher->add_child()->set_field(3);
-//
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//
-//    UidMap uidMap;
-//    SimpleAtomMatcher atomMatcher;
-//    atomMatcher.set_atom_id(tagId);
-//    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
-//        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//
-//    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-//    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-//    EXPECT_CALL(*pullerManager, Pull(tagId, _))
-//            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-//                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-//                event->write(3);
-//                event->write("some value");
-//                event->write(11);
-//                event->init();
-//                data->push_back(event);
-//                return true;
-//            }));
-//
-//    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-//                                      logEventMatcherIndex, eventMatcherWizard,
-//                                      tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
-//                                      pullerManager);
-//
-//    vector<shared_ptr<LogEvent>> allData;
-//    allData.clear();
-//    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-//    event->write(10);
-//    event->write("some value");
-//    event->write(11);
-//    event->init();
-//    allData.push_back(event);
-//
-//    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
-//    EXPECT_EQ(INT, it->mValue.getType());
-//    EXPECT_EQ(10, it->mValue.int_value);
-//    it++;
-//    EXPECT_EQ(11, it->mValue.int_value);
-//    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
-//    EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms
-//        .front().mFields->begin()->mValue.int_value);
-//
-//    allData.clear();
-//    std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(tagId, bucket3StartTimeNs + 10);
-//    event2->write(24);
-//    event2->write("some value");
-//    event2->write(25);
-//    event2->init();
-//    allData.push_back(event2);
-//    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
-//    EXPECT_EQ(INT, it->mValue.getType());
-//    EXPECT_EQ(24, it->mValue.int_value);
-//    it++;
-//    EXPECT_EQ(INT, it->mValue.getType());
-//    EXPECT_EQ(25, it->mValue.int_value);
-//    // One dimension.
-//    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
-//    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
-//    it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
-//    EXPECT_EQ(INT, it->mValue.getType());
-//    EXPECT_EQ(10L, it->mValue.int_value);
-//    it++;
-//    EXPECT_EQ(INT, it->mValue.getType());
-//    EXPECT_EQ(11L, it->mValue.int_value);
-//
-//    gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs);
-//    EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    // One dimension.
-//    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
-//    EXPECT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size());
-//    it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
-//    EXPECT_EQ(INT, it->mValue.getType());
-//    EXPECT_EQ(24L, it->mValue.int_value);
-//    it++;
-//    EXPECT_EQ(INT, it->mValue.getType());
-//    EXPECT_EQ(25L, it->mValue.int_value);
-//}
-//
-//TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) {
-//    sp<AlarmMonitor> alarmMonitor;
-//    GaugeMetric metric;
-//    metric.set_id(metricId);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.mutable_gauge_fields_filter()->set_include_all(true);
-//
-//    Alert alert;
-//    alert.set_id(101);
-//    alert.set_metric_id(metricId);
-//    alert.set_trigger_if_sum_gt(25);
-//    alert.set_num_buckets(100);
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//
-//    UidMap uidMap;
-//    SimpleAtomMatcher atomMatcher;
-//    atomMatcher.set_atom_id(tagId);
-//    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
-//        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//
-//    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-//                                      logEventMatcherIndex, eventMatcherWizard,
-//                                      -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs,
-//                                      bucketStartTimeNs, pullerManager);
-//
-//    sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
-//    EXPECT_TRUE(anomalyTracker != nullptr);
-//
-//    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-//    event1->write(1);
-//    event1->write(10);
-//    event1->init();
-//    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
-//    EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
-//
-//    gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
-//    EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
-//    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
-//    EXPECT_EQ(eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
-//    // Partial buckets are not sent to anomaly tracker.
-//    EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-//
-//    // Create an event in the same partial bucket.
-//    shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 59 * NS_PER_SEC);
-//    event2->write(1);
-//    event2->write(10);
-//    event2->init();
-//    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
-//    EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
-//    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
-//    // Partial buckets are not sent to anomaly tracker.
-//    EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-//
-//    // Next event should trigger creation of new bucket and send previous full bucket to anomaly
-//    // tracker.
-//    shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 65 * NS_PER_SEC);
-//    event3->write(1);
-//    event3->write(10);
-//    event3->init();
-//    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
-//    EXPECT_EQ(1L, gaugeProducer.mCurrentBucketNum);
-//    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    EXPECT_EQ((int64_t)bucketStartTimeNs + bucketSizeNs, gaugeProducer.mCurrentBucketStartTimeNs);
-//    EXPECT_EQ(1, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-//
-//    // Next event should trigger creation of new bucket.
-//    shared_ptr<LogEvent> event4 =
-//            make_shared<LogEvent>(tagId, bucketStartTimeNs + 125 * NS_PER_SEC);
-//    event4->write(1);
-//    event4->write(10);
-//    event4->init();
-//    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
-//    EXPECT_EQ(2L, gaugeProducer.mCurrentBucketNum);
-//    EXPECT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
-//}
-//
-//TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) {
-//    GaugeMetric metric;
-//    metric.set_id(metricId);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.set_max_pull_delay_sec(INT_MAX);
-//    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
-//    gaugeFieldMatcher->set_field(tagId);
-//    gaugeFieldMatcher->add_child()->set_field(2);
-//
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//
-//    UidMap uidMap;
-//    SimpleAtomMatcher atomMatcher;
-//    atomMatcher.set_atom_id(tagId);
-//    sp<EventMatcherWizard> eventMatcherWizard =
-//            new EventMatcherWizard({new SimpleLogMatchingTracker(
-//                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//
-//    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-//    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-//    EXPECT_CALL(*pullerManager, Pull(tagId, _))
-//            .WillOnce(Return(false))
-//            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-//                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventUpgradeTimeNs);
-//                event->write("some value");
-//                event->write(2);
-//                event->init();
-//                data->push_back(event);
-//                return true;
-//            }));
-//
-//    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-//                                      logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
-//                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-//
-//    vector<shared_ptr<LogEvent>> allData;
-//    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
-//    event->write("some value");
-//    event->write(1);
-//    event->init();
-//    allData.push_back(event);
-//    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
-//                         ->second.front()
-//                         .mFields->begin()
-//                         ->mValue.int_value);
-//
-//    gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
-//    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
-//    EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin()
-//                         ->second.front()
-//                         .mFields->begin()
-//                         ->mValue.int_value);
-//
-//    allData.clear();
-//    event = make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 1);
-//    event->write("some value");
-//    event->write(3);
-//    event->init();
-//    allData.push_back(event);
-//    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs);
-//    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin()
-//                         ->second.front()
-//                         .mFields->begin()
-//                         ->mValue.int_value);
-//}
-//
-//TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
-//    GaugeMetric metric;
-//    metric.set_id(metricId);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.set_max_pull_delay_sec(INT_MAX);
-//    metric.set_split_bucket_for_app_upgrade(false);
-//    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
-//    gaugeFieldMatcher->set_field(tagId);
-//    gaugeFieldMatcher->add_child()->set_field(2);
-//
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//
-//    UidMap uidMap;
-//    SimpleAtomMatcher atomMatcher;
-//    atomMatcher.set_atom_id(tagId);
-//    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
-//        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//
-//    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-//    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-//    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
-//
-//    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-//                                      logEventMatcherIndex, eventMatcherWizard,
-//                                      tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
-//                                      pullerManager);
-//
-//    vector<shared_ptr<LogEvent>> allData;
-//    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
-//    event->write("some value");
-//    event->write(1);
-//    event->init();
-//    allData.push_back(event);
-//    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
-//                         ->second.front()
-//                         .mFields->begin()
-//                         ->mValue.int_value);
-//
-//    gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
-//    EXPECT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
-//    EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
-//    EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
-//                         ->second.front()
-//                         .mFields->begin()
-//                         ->mValue.int_value);
-//}
-//
-//TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) {
-//    GaugeMetric metric;
-//    metric.set_id(metricId);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.set_max_pull_delay_sec(INT_MAX);
-//    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
-//    gaugeFieldMatcher->set_field(tagId);
-//    gaugeFieldMatcher->add_child()->set_field(2);
-//    metric.set_condition(StringToId("SCREEN_ON"));
-//
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//
-//    UidMap uidMap;
-//    SimpleAtomMatcher atomMatcher;
-//    atomMatcher.set_atom_id(tagId);
-//    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
-//        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//
-//    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-//    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-//    EXPECT_CALL(*pullerManager, Pull(tagId, _))
-//            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-//                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-//                event->write("some value");
-//                event->write(100);
-//                event->init();
-//                data->push_back(event);
-//                return true;
-//            }));
-//
-//    GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard,
-//                                      logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
-//                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-//
-//    gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin()
-//                           ->second.front()
-//                           .mFields->begin()
-//                           ->mValue.int_value);
-//    EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
-//
-//    vector<shared_ptr<LogEvent>> allData;
-//    allData.clear();
-//    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-//    event->write("some value");
-//    event->write(110);
-//    event->init();
-//    allData.push_back(event);
-//    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-//
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin()
-//                           ->second.front()
-//                           .mFields->begin()
-//                           ->mValue.int_value);
-//    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
-//    EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()
-//                           ->second.back()
-//                           .mGaugeAtoms.front()
-//                           .mFields->begin()
-//                           ->mValue.int_value);
-//
-//    gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10);
-//    gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10);
-//    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
-//    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
-//    EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()
-//                            ->second.back()
-//                            .mGaugeAtoms.front()
-//                            .mFields->begin()
-//                            ->mValue.int_value);
-//}
-//
-//TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) {
-//    const int conditionTag = 65;
-//    GaugeMetric metric;
-//    metric.set_id(1111111);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.mutable_gauge_fields_filter()->set_include_all(true);
-//    metric.set_condition(StringToId("APP_DIED"));
-//    metric.set_max_pull_delay_sec(INT_MAX);
-//    auto dim = metric.mutable_dimensions_in_what();
-//    dim->set_field(tagId);
-//    dim->add_child()->set_field(1);
-//
-//    UidMap uidMap;
-//    SimpleAtomMatcher atomMatcher;
-//    atomMatcher.set_atom_id(tagId);
-//    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
-//        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//    EXPECT_CALL(*wizard, query(_, _, _))
-//            .WillRepeatedly(
-//                    Invoke([](const int conditionIndex, const ConditionKey& conditionParameters,
-//                              const bool isPartialLink) {
-//                        int pos[] = {1, 0, 0};
-//                        Field f(conditionTag, pos, 0);
-//                        HashableDimensionKey key;
-//                        key.mutableValues()->emplace_back(f, Value((int32_t)1000000));
-//
-//                        return ConditionState::kTrue;
-//                    }));
-//
-//    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-//    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-//    EXPECT_CALL(*pullerManager, Pull(tagId, _))
-//            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-//                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-//                event->write(1000);
-//                event->write(100);
-//                event->init();
-//                data->push_back(event);
-//                return true;
-//            }));
-//
-//    GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard,
-//                                      logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
-//                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
-//
-//    gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8);
-//
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    const auto& key = gaugeProducer.mCurrentSlicedBucket->begin()->first;
-//    EXPECT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size());
-//    EXPECT_EQ(1000, key.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
-//
-//    EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
-//
-//    vector<shared_ptr<LogEvent>> allData;
-//    allData.clear();
-//    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-//    event->write(1000);
-//    event->write(110);
-//    event->init();
-//    allData.push_back(event);
-//    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
-//
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
-//}
-//
-//TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) {
-//    sp<AlarmMonitor> alarmMonitor;
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//
-//    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
-//    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-//    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
-//
-//    GaugeMetric metric;
-//    metric.set_id(metricId);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.set_max_pull_delay_sec(INT_MAX);
-//    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
-//    gaugeFieldMatcher->set_field(tagId);
-//    gaugeFieldMatcher->add_child()->set_field(2);
-//
-//    UidMap uidMap;
-//    SimpleAtomMatcher atomMatcher;
-//    atomMatcher.set_atom_id(tagId);
-//    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
-//        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//
-//    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-//                                      logEventMatcherIndex, eventMatcherWizard,
-//                                      tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
-//                                      pullerManager);
-//
-//    Alert alert;
-//    alert.set_id(101);
-//    alert.set_metric_id(metricId);
-//    alert.set_trigger_if_sum_gt(25);
-//    alert.set_num_buckets(2);
-//    const int32_t refPeriodSec = 60;
-//    alert.set_refractory_period_secs(refPeriodSec);
-//    sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
-//
-//    int tagId = 1;
-//    std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
-//    event1->write("some value");
-//    event1->write(13);
-//    event1->init();
-//
-//    gaugeProducer.onDataPulled({event1}, /** succeed */ true, bucketStartTimeNs);
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()
-//                           ->second.front()
-//                           .mFields->begin()
-//                           ->mValue.int_value);
-//    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
-//
-//    std::shared_ptr<LogEvent> event2 =
-//            std::make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 20);
-//    event2->write("some value");
-//    event2->write(15);
-//    event2->init();
-//
-//    gaugeProducer.onDataPulled({event2}, /** succeed */ true, bucketStartTimeNs + bucketSizeNs);
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()
-//                           ->second.front()
-//                           .mFields->begin()
-//                           ->mValue.int_value);
-//    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
-//              std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec);
-//
-//    std::shared_ptr<LogEvent> event3 =
-//            std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10);
-//    event3->write("some value");
-//    event3->write(26);
-//    event3->init();
-//
-//    gaugeProducer.onDataPulled({event3}, /** succeed */ true, bucket2StartTimeNs + 2 * bucketSizeNs);
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin()
-//                           ->second.front()
-//                           .mFields->begin()
-//                           ->mValue.int_value);
-//    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
-//              std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
-//
-//    // The event4 does not have the gauge field. Thus the current bucket value is 0.
-//    std::shared_ptr<LogEvent> event4 =
-//            std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10);
-//    event4->write("some value");
-//    event4->init();
-//    gaugeProducer.onDataPulled({event4}, /** succeed */ true, bucketStartTimeNs + 3 * bucketSizeNs);
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty());
-//}
-//
-//TEST(GaugeMetricProducerTest, TestPullOnTrigger) {
-//    GaugeMetric metric;
-//    metric.set_id(metricId);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
-//    metric.mutable_gauge_fields_filter()->set_include_all(false);
-//    metric.set_max_pull_delay_sec(INT_MAX);
-//    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
-//    gaugeFieldMatcher->set_field(tagId);
-//    gaugeFieldMatcher->add_child()->set_field(1);
-//
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//
-//    UidMap uidMap;
-//    SimpleAtomMatcher atomMatcher;
-//    atomMatcher.set_atom_id(tagId);
-//    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
-//        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//
-//    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//    EXPECT_CALL(*pullerManager, Pull(tagId, _))
-//            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-//                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-//                event->write(4);
-//                event->init();
-//                data->push_back(event);
-//                return true;
-//            }))
-//            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-//                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
-//                event->write(5);
-//                event->init();
-//                data->push_back(event);
-//                return true;
-//            }))
-//            .WillOnce(Return(true));
-//
-//    int triggerId = 5;
-//    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-//                                      logEventMatcherIndex, eventMatcherWizard,
-//                                      tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
-//                                      pullerManager);
-//
-//    vector<shared_ptr<LogEvent>> allData;
-//
-//    EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    LogEvent trigger(triggerId, bucketStartTimeNs + 10);
-//    trigger.init();
-//    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
-//    trigger.setElapsedTimestampNs(bucketStartTimeNs + 20);
-//    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
-//    EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
-//    trigger.setElapsedTimestampNs(bucket2StartTimeNs + 1);
-//    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
-//
-//    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
-//    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.size());
-//    EXPECT_EQ(4, gaugeProducer.mPastBuckets.begin()
-//                         ->second.back()
-//                         .mGaugeAtoms[0]
-//                         .mFields->begin()
-//                         ->mValue.int_value);
-//    EXPECT_EQ(5, gaugeProducer.mPastBuckets.begin()
-//                         ->second.back()
-//                         .mGaugeAtoms[1]
-//                         .mFields->begin()
-//                         ->mValue.int_value);
-//}
-//
-//TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) {
-//    GaugeMetric metric;
-//    metric.set_id(metricId);
-//    metric.set_bucket(ONE_MINUTE);
-//    metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
-//    metric.mutable_gauge_fields_filter()->set_include_all(true);
-//    metric.set_max_pull_delay_sec(INT_MAX);
-//    auto dimensionMatcher = metric.mutable_dimensions_in_what();
-//    // use field 1 as dimension.
-//    dimensionMatcher->set_field(tagId);
-//    dimensionMatcher->add_child()->set_field(1);
-//
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//
-//    UidMap uidMap;
-//    SimpleAtomMatcher atomMatcher;
-//    atomMatcher.set_atom_id(tagId);
-//    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
-//        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//
-//    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//    EXPECT_CALL(*pullerManager, Pull(tagId, _))
-//            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-//                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3);
-//                event->write(3);
-//                event->write(4);
-//                event->init();
-//                data->push_back(event);
-//                return true;
-//            }))
-//            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-//                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-//                event->write(4);
-//                event->write(5);
-//                event->init();
-//                data->push_back(event);
-//                return true;
-//            }))
-//            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-//                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
-//                event->write(4);
-//                event->write(6);
-//                event->init();
-//                data->push_back(event);
-//                return true;
-//            }))
-//            .WillOnce(Return(true));
-//
-//    int triggerId = 5;
-//    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-//                                      logEventMatcherIndex, eventMatcherWizard,
-//                                      tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
-//                                      pullerManager);
-//
-//    vector<shared_ptr<LogEvent>> allData;
-//
-//    LogEvent trigger(triggerId, bucketStartTimeNs + 3);
-//    trigger.init();
-//    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    trigger.setElapsedTimestampNs(bucketStartTimeNs + 10);
-//    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
-//    EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->size());
-//    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
-//    trigger.setElapsedTimestampNs(bucketStartTimeNs + 20);
-//    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
-//    EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
-//    trigger.setElapsedTimestampNs(bucket2StartTimeNs + 1);
-//    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
-//
-//    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.size());
-//    auto bucketIt = gaugeProducer.mPastBuckets.begin();
-//    EXPECT_EQ(1UL, bucketIt->second.back().mGaugeAtoms.size());
-//    EXPECT_EQ(3, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value);
-//    EXPECT_EQ(4, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value);
-//    bucketIt++;
-//    EXPECT_EQ(2UL, bucketIt->second.back().mGaugeAtoms.size());
-//    EXPECT_EQ(4, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value);
-//    EXPECT_EQ(5, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value);
-//    EXPECT_EQ(6, bucketIt->second.back().mGaugeAtoms[1].mFields->begin()->mValue.int_value);
-//}
-//
-///*
-// * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size
-// * is smaller than the "min_bucket_size_nanos" specified in the metric config.
-// */
-//TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
-//    GaugeMetric metric;
-//    metric.set_id(metricId);
-//    metric.set_bucket(FIVE_MINUTES);
-//    metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
-//    metric.set_min_bucket_size_nanos(10000000000);  // 10 seconds
-//
-//    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-//
-//    UidMap uidMap;
-//    SimpleAtomMatcher atomMatcher;
-//    atomMatcher.set_atom_id(tagId);
-//    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
-//        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
-//
-//    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-//    EXPECT_CALL(*pullerManager, Pull(tagId, _))
-//            // Bucket start.
-//            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-//                data->clear();
-//                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
-//                event->write("field1");
-//                event->write(10);
-//                event->init();
-//                data->push_back(event);
-//                return true;
-//            }));
-//
-//    int triggerId = 5;
-//    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-//                                      logEventMatcherIndex, eventMatcherWizard,
-//                                      tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
-//                                      pullerManager);
-//
-//    LogEvent trigger(triggerId, bucketStartTimeNs + 3);
-//    trigger.init();
-//    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger);
-//
-//    // Check dump report.
-//    ProtoOutputStream output;
-//    std::set<string> strSet;
-//    gaugeProducer.onDumpReport(bucketStartTimeNs + 9000000, true /* include recent buckets */,
-//                                true, FAST /* dump_latency */, &strSet, &output);
-//
-//    StatsLogReport report = outputStreamToProto(&output);
-//    EXPECT_TRUE(report.has_gauge_metrics());
-//    EXPECT_EQ(0, report.gauge_metrics().data_size());
-//    EXPECT_EQ(1, report.gauge_metrics().skipped_size());
-//
-//    EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
-//              report.gauge_metrics().skipped(0).start_bucket_elapsed_millis());
-//    EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000),
-//              report.gauge_metrics().skipped(0).end_bucket_elapsed_millis());
-//    EXPECT_EQ(1, report.gauge_metrics().skipped(0).drop_event_size());
-//
-//    auto dropEvent = report.gauge_metrics().skipped(0).drop_event(0);
-//    EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason());
-//    EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), dropEvent.drop_time_millis());
-//}
+TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) {
+    GaugeMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_gauge_fields_filter()->set_include_all(false);
+    metric.set_max_pull_delay_sec(INT_MAX);
+    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+    gaugeFieldMatcher->set_field(tagId);
+    gaugeFieldMatcher->add_child()->set_field(1);
+    gaugeFieldMatcher->add_child()->set_field(3);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(makeLogEvent(tagId, bucketStartTimeNs + 10, 3, "some value", 11));
+                return true;
+            }));
+
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    allData.push_back(makeLogEvent(tagId, bucket2StartTimeNs + 1, 10, "some value", 11));
+
+    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
+    EXPECT_EQ(INT, it->mValue.getType());
+    EXPECT_EQ(10, it->mValue.int_value);
+    it++;
+    EXPECT_EQ(11, it->mValue.int_value);
+    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+    EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin()
+                         ->second.back()
+                         .mGaugeAtoms.front()
+                         .mFields->begin()
+                         ->mValue.int_value);
+
+    allData.clear();
+    allData.push_back(makeLogEvent(tagId, bucket3StartTimeNs + 10, 24, "some value", 25));
+    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
+    EXPECT_EQ(INT, it->mValue.getType());
+    EXPECT_EQ(24, it->mValue.int_value);
+    it++;
+    EXPECT_EQ(INT, it->mValue.getType());
+    EXPECT_EQ(25, it->mValue.int_value);
+    // One dimension.
+    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
+    it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
+    EXPECT_EQ(INT, it->mValue.getType());
+    EXPECT_EQ(10L, it->mValue.int_value);
+    it++;
+    EXPECT_EQ(INT, it->mValue.getType());
+    EXPECT_EQ(11L, it->mValue.int_value);
+
+    gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs);
+    EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
+    // One dimension.
+    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+    EXPECT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size());
+    it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
+    EXPECT_EQ(INT, it->mValue.getType());
+    EXPECT_EQ(24L, it->mValue.int_value);
+    it++;
+    EXPECT_EQ(INT, it->mValue.getType());
+    EXPECT_EQ(25L, it->mValue.int_value);
+}
+
+TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) {
+    sp<AlarmMonitor> alarmMonitor;
+    GaugeMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_gauge_fields_filter()->set_include_all(true);
+
+    Alert alert;
+    alert.set_id(101);
+    alert.set_metric_id(metricId);
+    alert.set_trigger_if_sum_gt(25);
+    alert.set_num_buckets(100);
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard,
+                                      -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs,
+                                      bucketStartTimeNs, pullerManager);
+
+    sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
+    EXPECT_TRUE(anomalyTracker != nullptr);
+
+    LogEvent event1(/*uid=*/0, /*pid=*/0);
+    CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
+    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
+    EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
+
+    gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+    EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
+    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
+    EXPECT_EQ(eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
+    // Partial buckets are not sent to anomaly tracker.
+    EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+
+    // Create an event in the same partial bucket.
+    LogEvent event2(/*uid=*/0, /*pid=*/0);
+    CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 1, 10);
+    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
+    EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
+    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
+    // Partial buckets are not sent to anomaly tracker.
+    EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+
+    // Next event should trigger creation of new bucket and send previous full bucket to anomaly
+    // tracker.
+    LogEvent event3(/*uid=*/0, /*pid=*/0);
+    CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 1, 10);
+    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
+    EXPECT_EQ(1L, gaugeProducer.mCurrentBucketNum);
+    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ((int64_t)bucketStartTimeNs + bucketSizeNs, gaugeProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(1, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+
+    // Next event should trigger creation of new bucket.
+    LogEvent event4(/*uid=*/0, /*pid=*/0);
+    CreateTwoValueLogEvent(&event4, tagId, bucketStartTimeNs + 125 * NS_PER_SEC, 1, 10);
+    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
+    EXPECT_EQ(2L, gaugeProducer.mCurrentBucketNum);
+    EXPECT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
+}
+
+TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) {
+    GaugeMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_max_pull_delay_sec(INT_MAX);
+    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+    gaugeFieldMatcher->set_field(tagId);
+    gaugeFieldMatcher->add_child()->set_field(2);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Return(false))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(tagId, eventUpgradeTimeNs, 2));
+                return true;
+            }));
+
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
+    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
+                         ->second.front()
+                         .mFields->begin()
+                         ->mValue.int_value);
+
+    gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
+    EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin()
+                         ->second.front()
+                         .mFields->begin()
+                         ->mValue.int_value);
+
+    allData.clear();
+    allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 1, 3));
+    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin()
+                         ->second.front()
+                         .mFields->begin()
+                         ->mValue.int_value);
+}
+
+TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
+    GaugeMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_max_pull_delay_sec(INT_MAX);
+    metric.set_split_bucket_for_app_upgrade(false);
+    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+    gaugeFieldMatcher->set_field(tagId);
+    gaugeFieldMatcher->add_child()->set_field(2);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
+
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
+    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
+                         ->second.front()
+                         .mFields->begin()
+                         ->mValue.int_value);
+
+    gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
+    EXPECT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
+    EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
+                         ->second.front()
+                         .mFields->begin()
+                         ->mValue.int_value);
+}
+
+TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) {
+    GaugeMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_max_pull_delay_sec(INT_MAX);
+    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+    gaugeFieldMatcher->set_field(tagId);
+    gaugeFieldMatcher->add_child()->set_field(2);
+    metric.set_condition(StringToId("SCREEN_ON"));
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 100));
+                return true;
+            }));
+
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+                                      eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs,
+                                      bucketStartTimeNs, pullerManager);
+
+    gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin()
+                           ->second.front()
+                           .mFields->begin()
+                           ->mValue.int_value);
+    EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
+    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin()
+                           ->second.front()
+                           .mFields->begin()
+                           ->mValue.int_value);
+    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+    EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()
+                           ->second.back()
+                           .mGaugeAtoms.front()
+                           .mFields->begin()
+                           ->mValue.int_value);
+
+    gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10);
+    gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10);
+    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()
+                            ->second.back()
+                            .mGaugeAtoms.front()
+                            .mFields->begin()
+                            ->mValue.int_value);
+}
+
+TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) {
+    const int conditionTag = 65;
+    GaugeMetric metric;
+    metric.set_id(1111111);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_gauge_fields_filter()->set_include_all(true);
+    metric.set_condition(StringToId("APP_DIED"));
+    metric.set_max_pull_delay_sec(INT_MAX);
+    auto dim = metric.mutable_dimensions_in_what();
+    dim->set_field(tagId);
+    dim->add_child()->set_field(1);
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    EXPECT_CALL(*wizard, query(_, _, _))
+            .WillRepeatedly(
+                    Invoke([](const int conditionIndex, const ConditionKey& conditionParameters,
+                              const bool isPartialLink) {
+                        int pos[] = {1, 0, 0};
+                        Field f(conditionTag, pos, 0);
+                        HashableDimensionKey key;
+                        key.mutableValues()->emplace_back(f, Value((int32_t)1000000));
+
+                        return ConditionState::kTrue;
+                    }));
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 10, 1000, 100));
+                return true;
+            }));
+
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex,
+                                      eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs,
+                                      bucketStartTimeNs, pullerManager);
+
+    gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8);
+
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    const auto& key = gaugeProducer.mCurrentSlicedBucket->begin()->first;
+    EXPECT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1000, key.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+
+    EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1000, 110));
+    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
+
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+}
+
+TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) {
+    sp<AlarmMonitor> alarmMonitor;
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false));
+
+    GaugeMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_max_pull_delay_sec(INT_MAX);
+    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+    gaugeFieldMatcher->set_field(tagId);
+    gaugeFieldMatcher->add_child()->set_field(2);
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+    Alert alert;
+    alert.set_id(101);
+    alert.set_metric_id(metricId);
+    alert.set_trigger_if_sum_gt(25);
+    alert.set_num_buckets(2);
+    const int32_t refPeriodSec = 60;
+    alert.set_refractory_period_secs(refPeriodSec);
+    sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
+
+    int tagId = 1;
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 13));
+    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()
+                           ->second.front()
+                           .mFields->begin()
+                           ->mValue.int_value);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
+
+    std::shared_ptr<LogEvent> event2 =
+            CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 20, 15);
+
+    allData.clear();
+    allData.push_back(event2);
+    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()
+                           ->second.front()
+                           .mFields->begin()
+                           ->mValue.int_value);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
+              std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec);
+
+    allData.clear();
+    allData.push_back(
+            CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10, 26));
+    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 2 * bucketSizeNs);
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin()
+                           ->second.front()
+                           .mFields->begin()
+                           ->mValue.int_value);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
+              std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
+
+    // This event does not have the gauge field. Thus the current bucket value is 0.
+    allData.clear();
+    allData.push_back(CreateNoValuesLogEvent(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10));
+    gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 3 * bucketSizeNs);
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty());
+}
+
+TEST(GaugeMetricProducerTest, TestPullOnTrigger) {
+    GaugeMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
+    metric.mutable_gauge_fields_filter()->set_include_all(false);
+    metric.set_max_pull_delay_sec(INT_MAX);
+    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+    gaugeFieldMatcher->set_field(tagId);
+    gaugeFieldMatcher->add_child()->set_field(1);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 4));
+                return true;
+            }))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20, 5));
+                return true;
+            }))
+            .WillOnce(Return(true));
+
+    int triggerId = 5;
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId, triggerId,
+                                      tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+    EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
+
+    LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
+    CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 10);
+    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
+    triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20);
+    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
+    EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
+    triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1);
+    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
+
+    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.size());
+    EXPECT_EQ(4, gaugeProducer.mPastBuckets.begin()
+                         ->second.back()
+                         .mGaugeAtoms[0]
+                         .mFields->begin()
+                         ->mValue.int_value);
+    EXPECT_EQ(5, gaugeProducer.mPastBuckets.begin()
+                         ->second.back()
+                         .mGaugeAtoms[1]
+                         .mFields->begin()
+                         ->mValue.int_value);
+}
+
+TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) {
+    GaugeMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
+    metric.mutable_gauge_fields_filter()->set_include_all(true);
+    metric.set_max_pull_delay_sec(INT_MAX);
+    auto dimensionMatcher = metric.mutable_dimensions_in_what();
+    // use field 1 as dimension.
+    dimensionMatcher->set_field(tagId);
+    dimensionMatcher->add_child()->set_field(1);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 3, 3, 4));
+                return true;
+            }))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 10, 4, 5));
+                return true;
+            }))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 4, 6));
+                return true;
+            }))
+            .WillOnce(Return(true));
+
+    int triggerId = 5;
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId, triggerId,
+                                      tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+    LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
+    CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3);
+    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 10);
+    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
+    EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
+    triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20);
+    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
+    EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
+    triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1);
+    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
+
+    EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.size());
+    auto bucketIt = gaugeProducer.mPastBuckets.begin();
+    EXPECT_EQ(1UL, bucketIt->second.back().mGaugeAtoms.size());
+    EXPECT_EQ(3, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value);
+    EXPECT_EQ(4, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value);
+    bucketIt++;
+    EXPECT_EQ(2UL, bucketIt->second.back().mGaugeAtoms.size());
+    EXPECT_EQ(4, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value);
+    EXPECT_EQ(5, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value);
+    EXPECT_EQ(6, bucketIt->second.back().mGaugeAtoms[1].mFields->begin()->mValue.int_value);
+}
+
+/*
+ * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size
+ * is smaller than the "min_bucket_size_nanos" specified in the metric config.
+ */
+TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
+    GaugeMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(FIVE_MINUTES);
+    metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
+    metric.set_min_bucket_size_nanos(10000000000);  // 10 seconds
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    UidMap uidMap;
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    sp<EventMatcherWizard> eventMatcherWizard =
+            new EventMatcherWizard({new SimpleLogMatchingTracker(
+                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            // Bucket start.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 10));
+                return true;
+            }));
+
+    int triggerId = 5;
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId, triggerId,
+                                      tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+
+    LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
+    CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3);
+    gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
+
+    // Check dump report.
+    ProtoOutputStream output;
+    std::set<string> strSet;
+    gaugeProducer.onDumpReport(bucketStartTimeNs + 9000000, true /* include recent buckets */, true,
+                               FAST /* dump_latency */, &strSet, &output);
+
+    StatsLogReport report = outputStreamToProto(&output);
+    EXPECT_TRUE(report.has_gauge_metrics());
+    EXPECT_EQ(0, report.gauge_metrics().data_size());
+    EXPECT_EQ(1, report.gauge_metrics().skipped_size());
+
+    EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
+              report.gauge_metrics().skipped(0).start_bucket_elapsed_millis());
+    EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000),
+              report.gauge_metrics().skipped(0).end_bucket_elapsed_millis());
+    EXPECT_EQ(1, report.gauge_metrics().skipped(0).drop_event_size());
+
+    auto dropEvent = report.gauge_metrics().skipped(0).drop_event(0);
+    EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason());
+    EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), dropEvent.drop_time_millis());
+}
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index ae6769e..c1d4693 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -1582,9 +1582,9 @@
             ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
 
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    auto it = valueProducer->mCurrentSlicedBucket.begin();
-    auto& interval1 = it->second[0];
-    auto& baseInfo1 =
+    const auto& it = valueProducer->mCurrentSlicedBucket.begin();
+    ValueMetricProducer::Interval& interval1 = it->second[0];
+    ValueMetricProducer::BaseInfo& baseInfo1 =
             valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0];
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo1.hasBase);
@@ -1611,16 +1611,9 @@
             break;
         }
     }
-    // auto itBase = valueProducer->mCurrentBaseInfo.begin();
-    // for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) {
-    //     if (itBase != iterBase) {
-    //         break;
-    //     }
-    // }
     EXPECT_TRUE(it2 != it);
-    // EXPECT_TRUE(itBase != iterBase);
-    auto& interval2 = it2->second[0];
-    auto& baseInfo2 =
+    ValueMetricProducer::Interval& interval2 = it2->second[0];
+    ValueMetricProducer::BaseInfo& baseInfo2 =
             valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
     EXPECT_EQ(2, it2->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
@@ -1647,23 +1640,28 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
 
     EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
-    it = valueProducer->mCurrentSlicedBucket.begin();
-    it2 = std::next(valueProducer->mCurrentSlicedBucket.begin());
-    interval1 = it->second[0];
-    interval2 = it2->second[0];
-    baseInfo1 = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0];
-    baseInfo2 = valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
+    // Get new references now that entries have been deleted from the map
+    const auto& it3 = valueProducer->mCurrentSlicedBucket.begin();
+    const auto& it4 = std::next(valueProducer->mCurrentSlicedBucket.begin());
+    EXPECT_EQ(it3->second.size(), 1);
+    EXPECT_EQ(it4->second.size(), 1);
+    ValueMetricProducer::Interval& interval3 = it3->second[0];
+    ValueMetricProducer::Interval& interval4 = it4->second[0];
+    ValueMetricProducer::BaseInfo& baseInfo3 =
+            valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat())->second[0];
+    ValueMetricProducer::BaseInfo& baseInfo4 =
+            valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat())->second[0];
 
-    EXPECT_EQ(true, baseInfo1.hasBase);
-    EXPECT_EQ(5, baseInfo1.base.long_value);
-    EXPECT_EQ(false, interval1.hasValue);
-    EXPECT_EQ(5, interval1.value.long_value);
+    EXPECT_EQ(true, baseInfo3.hasBase);
+    EXPECT_EQ(5, baseInfo3.base.long_value);
+    EXPECT_EQ(false, interval3.hasValue);
+    EXPECT_EQ(5, interval3.value.long_value);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 
-    EXPECT_EQ(true, baseInfo2.hasBase);
-    EXPECT_EQ(13, baseInfo2.base.long_value);
-    EXPECT_EQ(false, interval2.hasValue);
-    EXPECT_EQ(8, interval2.value.long_value);
+    EXPECT_EQ(true, baseInfo4.hasBase);
+    EXPECT_EQ(13, baseInfo4.base.long_value);
+    EXPECT_EQ(false, interval4.hasValue);
+    EXPECT_EQ(8, interval4.value.long_value);
 
     EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
 }
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index 4c55683..5eef92e 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -12,18 +12,20 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <gtest/gtest.h>
+#include "src/shell/ShellSubscriber.h"
 
+#include <gtest/gtest.h>
+#include <stdio.h>
 #include <unistd.h>
+
+#include <vector>
+
 #include "frameworks/base/cmds/statsd/src/atoms.pb.h"
 #include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
 #include "frameworks/base/cmds/statsd/src/shell/shell_data.pb.h"
-#include "src/shell/ShellSubscriber.h"
 #include "stats_event.h"
 #include "tests/metrics/metrics_test_helper.h"
-
-#include <stdio.h>
-#include <vector>
+#include "tests/statsd_test_util.h"
 
 using namespace android::os::statsd;
 using android::sp;
@@ -118,18 +120,9 @@
     vector<std::shared_ptr<LogEvent>> pushedList;
 
     // Create the LogEvent from an AStatsEvent
-    AStatsEvent* statsEvent = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(statsEvent, 29 /*screen_state_atom_id*/);
-    AStatsEvent_overwriteTimestamp(statsEvent, 1000);
-    AStatsEvent_writeInt32(statsEvent, ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-    AStatsEvent_build(statsEvent);
-    size_t size;
-    uint8_t* buffer = AStatsEvent_getBuffer(statsEvent, &size);
-    std::shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
-    logEvent->parseBuffer(buffer, size);
-    AStatsEvent_release(statsEvent);
-
-    pushedList.push_back(logEvent);
+    std::unique_ptr<LogEvent> logEvent = CreateScreenStateChangedEvent(
+            1000 /*timestamp*/, ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    pushedList.push_back(std::move(logEvent));
 
     // create a simple config to get screen events
     ShellSubscription config;
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
index b1633c6..a0e0095 100644
--- a/cmds/statsd/tests/state/StateTracker_test.cpp
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -13,11 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include <gtest/gtest.h>
-#include "state/StateManager.h"
 #include "state/StateTracker.h"
-#include "state/StateListener.h"
 
+#include <gtest/gtest.h>
+
+#include "state/StateListener.h"
+#include "state/StateManager.h"
+#include "stats_event.h"
 #include "tests/statsd_test_util.h"
 
 #ifdef __ANDROID__
@@ -26,6 +28,8 @@
 namespace os {
 namespace statsd {
 
+const int32_t timestampNs = 1000;
+
 /**
  * Mock StateListener class for testing.
  * Stores primary key and state pairs.
@@ -56,95 +60,49 @@
     return output.mValue.int_value;
 }
 
-// TODO(b/149590301): Update these helpers to use new socket schema.
-//// START: build event functions.
-//// State with no primary fields - ScreenStateChanged
-//std::shared_ptr<LogEvent> buildScreenEvent(int state) {
-//    std::shared_ptr<LogEvent> event =
-//            std::make_shared<LogEvent>(util::SCREEN_STATE_CHANGED, 1000 /*timestamp*/);
-//    event->write((int32_t)state);
-//    event->init();
-//    return event;
-//}
-//
-//// State with one primary field - UidProcessStateChanged
-//std::shared_ptr<LogEvent> buildUidProcessEvent(int uid, int state) {
-//    std::shared_ptr<LogEvent> event =
-//            std::make_shared<LogEvent>(util::UID_PROCESS_STATE_CHANGED, 1000 /*timestamp*/);
-//    event->write((int32_t)uid);
-//    event->write((int32_t)state);
-//    event->init();
-//    return event;
-//}
-//
-//// State with first uid in attribution chain as primary field - WakelockStateChanged
-//std::shared_ptr<LogEvent> buildPartialWakelockEvent(int uid, const std::string& tag, bool acquire) {
-//    std::vector<AttributionNodeInternal> chain;
-//    chain.push_back(AttributionNodeInternal());
-//    AttributionNodeInternal& attr = chain.back();
-//    attr.set_uid(uid);
-//
-//    std::shared_ptr<LogEvent> event =
-//            std::make_shared<LogEvent>(util::WAKELOCK_STATE_CHANGED, 1000 /* timestamp */);
-//    event->write(chain);
-//    event->write((int32_t)1);  // PARTIAL_WAKE_LOCK
-//    event->write(tag);
-//    event->write(acquire ? 1 : 0);
-//    event->init();
-//    return event;
-//}
-//
-//// State with multiple primary fields - OverlayStateChanged
-//std::shared_ptr<LogEvent> buildOverlayEvent(int uid, const std::string& packageName, int state) {
-//    std::shared_ptr<LogEvent> event =
-//            std::make_shared<LogEvent>(util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
-//    event->write((int32_t)uid);
-//    event->write(packageName);
-//    event->write(true);  // using_alert_window
-//    event->write((int32_t)state);
-//    event->init();
-//    return event;
-//}
-//
-//// Incorrect event - missing fields
-//std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, int state) {
-//    std::shared_ptr<LogEvent> event =
-//            std::make_shared<LogEvent>(util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
-//    event->write((int32_t)uid);
-//    event->write(packageName);
-//    event->write((int32_t)state);
-//    event->init();
-//    return event;
-//}
-//
-//// Incorrect event - exclusive state has wrong type
-//std::shared_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::string& packageName) {
-//    std::shared_ptr<LogEvent> event =
-//            std::make_shared<LogEvent>(util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
-//    event->write((int32_t)uid);
-//    event->write(packageName);
-//    event->write(true);
-//    event->write("string");  // exclusive state: string instead of int
-//    event->init();
-//    return event;
-//}
-//
-//std::shared_ptr<LogEvent> buildBleScanEvent(int uid, bool acquire, bool reset) {
-//    std::vector<AttributionNodeInternal> chain;
-//    chain.push_back(AttributionNodeInternal());
-//    AttributionNodeInternal& attr = chain.back();
-//    attr.set_uid(uid);
-//
-//    std::shared_ptr<LogEvent> event =
-//            std::make_shared<LogEvent>(util::BLE_SCAN_STATE_CHANGED, 1000);
-//    event->write(chain);
-//    event->write(reset ? 2 : acquire ? 1 : 0);  // PARTIAL_WAKE_LOCK
-//    event->write(0);                            // filtered
-//    event->write(0);                            // first match
-//    event->write(0);                            // opportunistic
-//    event->init();
-//    return event;
-//}
+// START: build event functions.
+// Incorrect event - missing fields
+std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName,
+                                                     int state) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, 1000);
+
+    AStatsEvent_writeInt32(statsEvent, uid);
+    AStatsEvent_writeString(statsEvent, packageName.c_str());
+    // Missing field 3 - using_alert_window.
+    AStatsEvent_writeInt32(statsEvent, state);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+
+    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+    return logEvent;
+}
+
+// Incorrect event - exclusive state has wrong type
+std::unique_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::string& packageName) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, 1000);
+
+    AStatsEvent_writeInt32(statsEvent, uid);
+    AStatsEvent_writeString(statsEvent, packageName.c_str());
+    AStatsEvent_writeInt32(statsEvent, true);       // using_alert_window
+    AStatsEvent_writeString(statsEvent, "string");  // exclusive state: string instead of int
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+
+    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+    return logEvent;
+}
 // END: build event functions.
 
 // START: get primary key functions
@@ -293,302 +251,323 @@
     EXPECT_EQ(0, mgr.getStateTrackersCount());
     EXPECT_EQ(-1, mgr.getListenersCount(util::SCREEN_STATE_CHANGED));
 }
-// TODO(b/149590301): Update these tests to use new socket schema.
-///**
-// * Test a binary state atom with nested counting.
-// *
-// * To go from an "ON" state to an "OFF" state with nested counting, we must see
-// * an equal number of "OFF" events as "ON" events.
-// * For example, ACQUIRE, ACQUIRE, RELEASE will still be in the ACQUIRE state.
-// * ACQUIRE, ACQUIRE, RELEASE, RELEASE will be in the RELEASE state.
-// */
-//TEST(StateTrackerTest, TestStateChangeNested) {
-//    sp<TestStateListener> listener = new TestStateListener();
-//    StateManager mgr;
-//    mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener);
-//
-//    std::shared_ptr<LogEvent> event1 =
-//            buildPartialWakelockEvent(1000 /* uid */, "tag", true /*acquire*/);
-//    mgr.onLogEvent(*event1);
-//    EXPECT_EQ(1, listener->updates.size());
-//    EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
-//    EXPECT_EQ(1, listener->updates[0].mState);
-//    listener->updates.clear();
-//
-//    std::shared_ptr<LogEvent> event2 =
-//            buildPartialWakelockEvent(1000 /* uid */, "tag", true /*acquire*/);
-//    mgr.onLogEvent(*event2);
-//    EXPECT_EQ(0, listener->updates.size());
-//
-//    std::shared_ptr<LogEvent> event3 =
-//            buildPartialWakelockEvent(1000 /* uid */, "tag", false /*release*/);
-//    mgr.onLogEvent(*event3);
-//    EXPECT_EQ(0, listener->updates.size());
-//
-//    std::shared_ptr<LogEvent> event4 =
-//            buildPartialWakelockEvent(1000 /* uid */, "tag", false /*release*/);
-//    mgr.onLogEvent(*event4);
-//    EXPECT_EQ(1, listener->updates.size());
-//    EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
-//    EXPECT_EQ(0, listener->updates[0].mState);
-//}
-//
-///**
-// * Test a state atom with a reset state.
-// *
-// * If the reset state value is seen, every state in the map is set to the default
-// * state and every listener is notified.
-// */
-//TEST(StateTrackerTest, TestStateChangeReset) {
-//    sp<TestStateListener> listener = new TestStateListener();
-//    StateManager mgr;
-//    mgr.registerListener(util::BLE_SCAN_STATE_CHANGED, listener);
-//
-//    std::shared_ptr<LogEvent> event1 =
-//            buildBleScanEvent(1000 /* uid */, true /*acquire*/, false /*reset*/);
-//    mgr.onLogEvent(*event1);
-//    EXPECT_EQ(1, listener->updates.size());
-//    EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
-//    EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
-//    listener->updates.clear();
-//
-//    std::shared_ptr<LogEvent> event2 =
-//            buildBleScanEvent(2000 /* uid */, true /*acquire*/, false /*reset*/);
-//    mgr.onLogEvent(*event2);
-//    EXPECT_EQ(1, listener->updates.size());
-//    EXPECT_EQ(2000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
-//    EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
-//    listener->updates.clear();
-//
-//    std::shared_ptr<LogEvent> event3 =
-//            buildBleScanEvent(2000 /* uid */, false /*acquire*/, true /*reset*/);
-//    mgr.onLogEvent(*event3);
-//    EXPECT_EQ(2, listener->updates.size());
-//    EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[0].mState);
-//    EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[1].mState);
-//}
-//
-///**
-// * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
-// * updates listener for states without primary keys.
-// */
-//TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) {
-//    sp<TestStateListener> listener1 = new TestStateListener();
-//    StateManager mgr;
-//    mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
-//
-//    // log event
-//    std::shared_ptr<LogEvent> event =
-//            buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-//    mgr.onLogEvent(*event);
-//
-//    // check listener was updated
-//    EXPECT_EQ(1, listener1->updates.size());
-//    EXPECT_EQ(DEFAULT_DIMENSION_KEY, listener1->updates[0].mKey);
-//    EXPECT_EQ(2, listener1->updates[0].mState);
-//
-//    // check StateTracker was updated by querying for state
-//    HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY;
-//    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-//              getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey));
-//}
-//
-///**
-// * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
-// * updates listener for states with one primary key.
-// */
-//TEST(StateTrackerTest, TestStateChangeOnePrimaryField) {
-//    sp<TestStateListener> listener1 = new TestStateListener();
-//    StateManager mgr;
-//    mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener1);
-//
-//    // log event
-//    std::shared_ptr<LogEvent> event =
-//            buildUidProcessEvent(1000 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP);
-//    mgr.onLogEvent(*event);
-//
-//    // check listener was updated
-//    EXPECT_EQ(1, listener1->updates.size());
-//    EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
-//    EXPECT_EQ(1002, listener1->updates[0].mState);
-//
-//    // check StateTracker was updated by querying for state
-//    HashableDimensionKey queryKey;
-//    getUidProcessKey(1000 /* uid */, &queryKey);
-//    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP,
-//              getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey));
-//}
-//
-//TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) {
-//    sp<TestStateListener> listener1 = new TestStateListener();
-//    StateManager mgr;
-//    mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener1);
-//
-//    // Log event.
-//    std::shared_ptr<LogEvent> event =
-//            buildPartialWakelockEvent(1001 /* uid */, "tag1", true /* acquire */);
-//    mgr.onLogEvent(*event);
-//
-//    EXPECT_EQ(1, mgr.getStateTrackersCount());
-//    EXPECT_EQ(1, mgr.getListenersCount(util::WAKELOCK_STATE_CHANGED));
-//
-//    // Check listener was updated.
-//    EXPECT_EQ(1, listener1->updates.size());
-//    EXPECT_EQ(3, listener1->updates[0].mKey.getValues().size());
-//    EXPECT_EQ(1001, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
-//    EXPECT_EQ(1, listener1->updates[0].mKey.getValues()[1].mValue.int_value);
-//    EXPECT_EQ("tag1", listener1->updates[0].mKey.getValues()[2].mValue.str_value);
-//    EXPECT_EQ(WakelockStateChanged::ACQUIRE, listener1->updates[0].mState);
-//
-//    // Check StateTracker was updated by querying for state.
-//    HashableDimensionKey queryKey;
-//    getPartialWakelockKey(1001 /* uid */, "tag1", &queryKey);
-//    EXPECT_EQ(WakelockStateChanged::ACQUIRE,
-//              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey));
-//
-//    // No state stored for this query key.
-//    HashableDimensionKey queryKey2;
-//    getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2);
-//    EXPECT_EQ(WakelockStateChanged::RELEASE,
-//              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey2));
-//
-//    // Partial query fails.
-//    HashableDimensionKey queryKey3;
-//    getPartialWakelockKey(1001 /* uid */, &queryKey3);
-//    EXPECT_EQ(WakelockStateChanged::RELEASE,
-//              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey3));
-//}
-//
-///**
-// * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
-// * updates listener for states with multiple primary keys.
-// */
-//TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) {
-//    sp<TestStateListener> listener1 = new TestStateListener();
-//    StateManager mgr;
-//    mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1);
-//
-//    // log event
-//    std::shared_ptr<LogEvent> event =
-//            buildOverlayEvent(1000 /* uid */, "package1", 1);  // state: ENTERED
-//    mgr.onLogEvent(*event);
-//
-//    // check listener was updated
-//    EXPECT_EQ(1, listener1->updates.size());
-//    EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
-//    EXPECT_EQ(1, listener1->updates[0].mState);
-//
-//    // check StateTracker was updated by querying for state
-//    HashableDimensionKey queryKey;
-//    getOverlayKey(1000 /* uid */, "package1", &queryKey);
-//    EXPECT_EQ(OverlayStateChanged::ENTERED,
-//              getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey));
-//}
-//
-///**
-// * Test StateManager's onLogEvent and StateListener's onStateChanged
-// * when there is an error extracting state from log event. Listener is not
-// * updated of state change.
-// */
-//TEST(StateTrackerTest, TestStateChangeEventError) {
-//    sp<TestStateListener> listener1 = new TestStateListener();
-//    StateManager mgr;
-//    mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1);
-//
-//    // log event
-//    std::shared_ptr<LogEvent> event1 =
-//            buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */);
-//    std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2");
-//
-//    // check listener was updated
-//    mgr.onLogEvent(*event1);
-//    EXPECT_EQ(0, listener1->updates.size());
-//    mgr.onLogEvent(*event2);
-//    EXPECT_EQ(0, listener1->updates.size());
-//}
-//
-//TEST(StateTrackerTest, TestStateQuery) {
-//    sp<TestStateListener> listener1 = new TestStateListener();
-//    sp<TestStateListener> listener2 = new TestStateListener();
-//    sp<TestStateListener> listener3 = new TestStateListener();
-//    sp<TestStateListener> listener4 = new TestStateListener();
-//    StateManager mgr;
-//    mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
-//    mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener2);
-//    mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener3);
-//    mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener4);
-//
-//    std::shared_ptr<LogEvent> event1 = buildUidProcessEvent(
-//            1000,
-//            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  //  state value: 1002
-//    std::shared_ptr<LogEvent> event2 = buildUidProcessEvent(
-//            1001,
-//            android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE);  //  state value:
-//                                                                                //  1003
-//    std::shared_ptr<LogEvent> event3 = buildUidProcessEvent(
-//            1002,
-//            android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT);  //  state value: 1000
-//    std::shared_ptr<LogEvent> event4 = buildUidProcessEvent(
-//            1001,
-//            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  //  state value: 1002
-//    std::shared_ptr<LogEvent> event5 =
-//            buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-//    std::shared_ptr<LogEvent> event6 =
-//            buildOverlayEvent(1000, "package1", OverlayStateChanged::ENTERED);
-//    std::shared_ptr<LogEvent> event7 =
-//            buildOverlayEvent(1000, "package2", OverlayStateChanged::EXITED);
-//    std::shared_ptr<LogEvent> event8 = buildPartialWakelockEvent(1005, "tag1", true);
-//    std::shared_ptr<LogEvent> event9 = buildPartialWakelockEvent(1005, "tag2", false);
-//
-//    mgr.onLogEvent(*event1);
-//    mgr.onLogEvent(*event2);
-//    mgr.onLogEvent(*event3);
-//    mgr.onLogEvent(*event5);
-//    mgr.onLogEvent(*event5);
-//    mgr.onLogEvent(*event6);
-//    mgr.onLogEvent(*event7);
-//    mgr.onLogEvent(*event8);
-//    mgr.onLogEvent(*event9);
-//
-//    // Query for UidProcessState of uid 1001
-//    HashableDimensionKey queryKey1;
-//    getUidProcessKey(1001, &queryKey1);
-//    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE,
-//              getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1));
-//
-//    // Query for UidProcessState of uid 1004 - not in state map
-//    HashableDimensionKey queryKey2;
-//    getUidProcessKey(1004, &queryKey2);
-//    EXPECT_EQ(-1, getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED,
-//                              queryKey2));  // default state
-//
-//    // Query for UidProcessState of uid 1001 - after change in state
-//    mgr.onLogEvent(*event4);
-//    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP,
-//              getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1));
-//
-//    // Query for ScreenState
-//    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-//              getStateInt(mgr, util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
-//
-//    // Query for OverlayState of uid 1000, package name "package2"
-//    HashableDimensionKey queryKey3;
-//    getOverlayKey(1000, "package2", &queryKey3);
-//    EXPECT_EQ(OverlayStateChanged::EXITED,
-//              getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey3));
-//
-//    // Query for WakelockState of uid 1005, tag 2
-//    HashableDimensionKey queryKey4;
-//    getPartialWakelockKey(1005, "tag2", &queryKey4);
-//    EXPECT_EQ(WakelockStateChanged::RELEASE,
-//              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey4));
-//
-//    // Query for WakelockState of uid 1005, tag 1
-//    HashableDimensionKey queryKey5;
-//    getPartialWakelockKey(1005, "tag1", &queryKey5);
-//    EXPECT_EQ(WakelockStateChanged::ACQUIRE,
-//              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey5));
-//}
+
+/**
+ * Test a binary state atom with nested counting.
+ *
+ * To go from an "ON" state to an "OFF" state with nested counting, we must see
+ * an equal number of "OFF" events as "ON" events.
+ * For example, ACQUIRE, ACQUIRE, RELEASE will still be in the ACQUIRE state.
+ * ACQUIRE, ACQUIRE, RELEASE, RELEASE will be in the RELEASE state.
+ */
+TEST(StateTrackerTest, TestStateChangeNested) {
+    sp<TestStateListener> listener = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener);
+
+    std::vector<int> attributionUids1 = {1000};
+    std::vector<string> attributionTags1 = {"tag"};
+
+    std::unique_ptr<LogEvent> event1 = CreateAcquireWakelockEvent(timestampNs, attributionUids1,
+                                                                  attributionTags1, "wakelockName");
+    mgr.onLogEvent(*event1);
+    EXPECT_EQ(1, listener->updates.size());
+    EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
+    EXPECT_EQ(1, listener->updates[0].mState);
+    listener->updates.clear();
+
+    std::unique_ptr<LogEvent> event2 = CreateAcquireWakelockEvent(
+            timestampNs + 1000, attributionUids1, attributionTags1, "wakelockName");
+    mgr.onLogEvent(*event2);
+    EXPECT_EQ(0, listener->updates.size());
+
+    std::unique_ptr<LogEvent> event3 = CreateReleaseWakelockEvent(
+            timestampNs + 2000, attributionUids1, attributionTags1, "wakelockName");
+    mgr.onLogEvent(*event3);
+    EXPECT_EQ(0, listener->updates.size());
+
+    std::unique_ptr<LogEvent> event4 = CreateReleaseWakelockEvent(
+            timestampNs + 3000, attributionUids1, attributionTags1, "wakelockName");
+    mgr.onLogEvent(*event4);
+    EXPECT_EQ(1, listener->updates.size());
+    EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
+    EXPECT_EQ(0, listener->updates[0].mState);
+}
+
+/**
+ * Test a state atom with a reset state.
+ *
+ * If the reset state value is seen, every state in the map is set to the default
+ * state and every listener is notified.
+ */
+TEST(StateTrackerTest, TestStateChangeReset) {
+    sp<TestStateListener> listener = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(util::BLE_SCAN_STATE_CHANGED, listener);
+
+    std::vector<int> attributionUids1 = {1000};
+    std::vector<string> attributionTags1 = {"tag1"};
+    std::vector<int> attributionUids2 = {2000};
+
+    std::unique_ptr<LogEvent> event1 =
+            CreateBleScanStateChangedEvent(timestampNs, attributionUids1, attributionTags1,
+                                           BleScanStateChanged::ON, false, false, false);
+    mgr.onLogEvent(*event1);
+    EXPECT_EQ(1, listener->updates.size());
+    EXPECT_EQ(1000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
+    EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
+    listener->updates.clear();
+
+    std::unique_ptr<LogEvent> event2 =
+            CreateBleScanStateChangedEvent(timestampNs + 1000, attributionUids2, attributionTags1,
+                                           BleScanStateChanged::ON, false, false, false);
+    mgr.onLogEvent(*event2);
+    EXPECT_EQ(1, listener->updates.size());
+    EXPECT_EQ(2000, listener->updates[0].mKey.getValues()[0].mValue.int_value);
+    EXPECT_EQ(BleScanStateChanged::ON, listener->updates[0].mState);
+    listener->updates.clear();
+
+    std::unique_ptr<LogEvent> event3 =
+            CreateBleScanStateChangedEvent(timestampNs + 2000, attributionUids2, attributionTags1,
+                                           BleScanStateChanged::RESET, false, false, false);
+    mgr.onLogEvent(*event3);
+    EXPECT_EQ(2, listener->updates.size());
+    EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[0].mState);
+    EXPECT_EQ(BleScanStateChanged::OFF, listener->updates[1].mState);
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states without primary keys.
+ */
+TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) {
+    sp<TestStateListener> listener1 = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
+
+    // log event
+    std::unique_ptr<LogEvent> event = CreateScreenStateChangedEvent(
+            timestampNs, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    mgr.onLogEvent(*event);
+
+    // check listener was updated
+    EXPECT_EQ(1, listener1->updates.size());
+    EXPECT_EQ(DEFAULT_DIMENSION_KEY, listener1->updates[0].mKey);
+    EXPECT_EQ(2, listener1->updates[0].mState);
+
+    // check StateTracker was updated by querying for state
+    HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY;
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+              getStateInt(mgr, util::SCREEN_STATE_CHANGED, queryKey));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states with one primary key.
+ */
+TEST(StateTrackerTest, TestStateChangeOnePrimaryField) {
+    sp<TestStateListener> listener1 = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener1);
+
+    // log event
+    std::unique_ptr<LogEvent> event = CreateUidProcessStateChangedEvent(
+            timestampNs, 1000 /*uid*/, android::app::ProcessStateEnum::PROCESS_STATE_TOP);
+    mgr.onLogEvent(*event);
+
+    // check listener was updated
+    EXPECT_EQ(1, listener1->updates.size());
+    EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+    EXPECT_EQ(1002, listener1->updates[0].mState);
+
+    // check StateTracker was updated by querying for state
+    HashableDimensionKey queryKey;
+    getUidProcessKey(1000 /* uid */, &queryKey);
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP,
+              getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey));
+}
+
+TEST(StateTrackerTest, TestStateChangePrimaryFieldAttrChain) {
+    sp<TestStateListener> listener1 = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener1);
+
+    // Log event.
+    std::vector<int> attributionUids = {1001};
+    std::vector<string> attributionTags = {"tag1"};
+
+    std::unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(timestampNs, attributionUids,
+                                                                 attributionTags, "wakelockName");
+    mgr.onLogEvent(*event);
+    EXPECT_EQ(1, mgr.getStateTrackersCount());
+    EXPECT_EQ(1, mgr.getListenersCount(util::WAKELOCK_STATE_CHANGED));
+
+    // Check listener was updated.
+    EXPECT_EQ(1, listener1->updates.size());
+    EXPECT_EQ(3, listener1->updates[0].mKey.getValues().size());
+    EXPECT_EQ(1001, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+    EXPECT_EQ(1, listener1->updates[0].mKey.getValues()[1].mValue.int_value);
+    EXPECT_EQ("wakelockName", listener1->updates[0].mKey.getValues()[2].mValue.str_value);
+    EXPECT_EQ(WakelockStateChanged::ACQUIRE, listener1->updates[0].mState);
+
+    // Check StateTracker was updated by querying for state.
+    HashableDimensionKey queryKey;
+    getPartialWakelockKey(1001 /* uid */, "wakelockName", &queryKey);
+    EXPECT_EQ(WakelockStateChanged::ACQUIRE,
+              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey));
+
+    // No state stored for this query key.
+    HashableDimensionKey queryKey2;
+    getPartialWakelockKey(1002 /* uid */, "tag1", &queryKey2);
+    EXPECT_EQ(WakelockStateChanged::RELEASE,
+              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey2));
+
+    // Partial query fails.
+    HashableDimensionKey queryKey3;
+    getPartialWakelockKey(1001 /* uid */, &queryKey3);
+    EXPECT_EQ(WakelockStateChanged::RELEASE,
+              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey3));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states with multiple primary keys.
+ */
+TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) {
+    sp<TestStateListener> listener1 = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1);
+
+    // log event
+    std::unique_ptr<LogEvent> event = CreateOverlayStateChangedEvent(
+            timestampNs, 1000 /* uid */, "package1", true /*using_alert_window*/,
+            OverlayStateChanged::ENTERED);
+    mgr.onLogEvent(*event);
+
+    // check listener was updated
+    EXPECT_EQ(1, listener1->updates.size());
+    EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+    EXPECT_EQ(1, listener1->updates[0].mState);
+
+    // check StateTracker was updated by querying for state
+    HashableDimensionKey queryKey;
+    getOverlayKey(1000 /* uid */, "package1", &queryKey);
+    EXPECT_EQ(OverlayStateChanged::ENTERED,
+              getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged
+ * when there is an error extracting state from log event. Listener is not
+ * updated of state change.
+ */
+TEST(StateTrackerTest, TestStateChangeEventError) {
+    sp<TestStateListener> listener1 = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener1);
+
+    // log event
+    std::shared_ptr<LogEvent> event1 =
+            buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */);
+    std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2");
+
+    // check listener was updated
+    mgr.onLogEvent(*event1);
+    EXPECT_EQ(0, listener1->updates.size());
+    mgr.onLogEvent(*event2);
+    EXPECT_EQ(0, listener1->updates.size());
+}
+
+TEST(StateTrackerTest, TestStateQuery) {
+    sp<TestStateListener> listener1 = new TestStateListener();
+    sp<TestStateListener> listener2 = new TestStateListener();
+    sp<TestStateListener> listener3 = new TestStateListener();
+    sp<TestStateListener> listener4 = new TestStateListener();
+    StateManager mgr;
+    mgr.registerListener(util::SCREEN_STATE_CHANGED, listener1);
+    mgr.registerListener(util::UID_PROCESS_STATE_CHANGED, listener2);
+    mgr.registerListener(util::OVERLAY_STATE_CHANGED, listener3);
+    mgr.registerListener(util::WAKELOCK_STATE_CHANGED, listener4);
+
+    std::unique_ptr<LogEvent> event1 = CreateUidProcessStateChangedEvent(
+            timestampNs, 1000 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  //  state value: 1002
+    std::unique_ptr<LogEvent> event2 = CreateUidProcessStateChangedEvent(
+            timestampNs + 1000, 1001 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE);  //  state value:
+                                                                                //  1003
+    std::unique_ptr<LogEvent> event3 = CreateUidProcessStateChangedEvent(
+            timestampNs + 2000, 1002 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT);  //  state value: 1000
+    std::unique_ptr<LogEvent> event4 = CreateUidProcessStateChangedEvent(
+            timestampNs + 3000, 1001 /*uid*/,
+            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  //  state value: 1002
+    std::unique_ptr<LogEvent> event5 = CreateScreenStateChangedEvent(
+            timestampNs + 4000, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    std::unique_ptr<LogEvent> event6 = CreateOverlayStateChangedEvent(
+            timestampNs + 5000, 1000 /*uid*/, "package1", true /*using_alert_window*/,
+            OverlayStateChanged::ENTERED);
+    std::unique_ptr<LogEvent> event7 = CreateOverlayStateChangedEvent(
+            timestampNs + 6000, 1000 /*uid*/, "package2", true /*using_alert_window*/,
+            OverlayStateChanged::EXITED);
+
+    std::vector<int> attributionUids = {1005};
+    std::vector<string> attributionTags = {"tag"};
+
+    std::unique_ptr<LogEvent> event8 = CreateAcquireWakelockEvent(
+            timestampNs + 7000, attributionUids, attributionTags, "wakelock1");
+    std::unique_ptr<LogEvent> event9 = CreateReleaseWakelockEvent(
+            timestampNs + 8000, attributionUids, attributionTags, "wakelock2");
+
+    mgr.onLogEvent(*event1);
+    mgr.onLogEvent(*event2);
+    mgr.onLogEvent(*event3);
+    mgr.onLogEvent(*event5);
+    mgr.onLogEvent(*event5);
+    mgr.onLogEvent(*event6);
+    mgr.onLogEvent(*event7);
+    mgr.onLogEvent(*event8);
+    mgr.onLogEvent(*event9);
+
+    // Query for UidProcessState of uid 1001
+    HashableDimensionKey queryKey1;
+    getUidProcessKey(1001, &queryKey1);
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE,
+              getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1));
+
+    // Query for UidProcessState of uid 1004 - not in state map
+    HashableDimensionKey queryKey2;
+    getUidProcessKey(1004, &queryKey2);
+    EXPECT_EQ(-1, getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED,
+                              queryKey2));  // default state
+
+    // Query for UidProcessState of uid 1001 - after change in state
+    mgr.onLogEvent(*event4);
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_TOP,
+              getStateInt(mgr, util::UID_PROCESS_STATE_CHANGED, queryKey1));
+
+    // Query for ScreenState
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+              getStateInt(mgr, util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
+
+    // Query for OverlayState of uid 1000, package name "package2"
+    HashableDimensionKey queryKey3;
+    getOverlayKey(1000, "package2", &queryKey3);
+    EXPECT_EQ(OverlayStateChanged::EXITED,
+              getStateInt(mgr, util::OVERLAY_STATE_CHANGED, queryKey3));
+
+    // Query for WakelockState of uid 1005, tag 2
+    HashableDimensionKey queryKey4;
+    getPartialWakelockKey(1005, "wakelock2", &queryKey4);
+    EXPECT_EQ(WakelockStateChanged::RELEASE,
+              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey4));
+
+    // Query for WakelockState of uid 1005, tag 1
+    HashableDimensionKey queryKey5;
+    getPartialWakelockKey(1005, "wakelock1", &queryKey5);
+    EXPECT_EQ(WakelockStateChanged::ACQUIRE,
+              getStateInt(mgr, util::WAKELOCK_STATE_CHANGED, queryKey5));
+}
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 050dbf8..8c8836b 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -428,7 +428,7 @@
 
     return logEvent;
 }
-//
+
 void CreateTwoValueLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs, int32_t value1,
                             int32_t value2) {
     AStatsEvent* statsEvent = AStatsEvent_obtain();
@@ -531,6 +531,18 @@
     return logEvent;
 }
 
+void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, eventTimeNs);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+}
+
 std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
         uint64_t timestampNs, const android::view::DisplayStateEnum state) {
     AStatsEvent* statsEvent = AStatsEvent_obtain();
@@ -600,32 +612,51 @@
     return logEvent;
 }
 
-//std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
-//        const std::vector<AttributionNodeInternal>& attributions, const string& jobName,
-//        const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
-//    auto event = std::make_unique<LogEvent>(util::SCHEDULED_JOB_STATE_CHANGED, timestampNs);
-//    event->write(attributions);
-//    event->write(jobName);
-//    event->write(state);
-//    event->init();
-//    return event;
-//}
-//
-//std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(
-//    const std::vector<AttributionNodeInternal>& attributions,
-//    const string& name, uint64_t timestampNs) {
-//    return CreateScheduledJobStateChangedEvent(
-//            attributions, name, ScheduledJobStateChanged::STARTED, timestampNs);
-//}
-//
-//// Create log event when scheduled job finishes.
-//std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(
-//    const std::vector<AttributionNodeInternal>& attributions,
-//    const string& name, uint64_t timestampNs) {
-//    return CreateScheduledJobStateChangedEvent(
-//            attributions, name, ScheduledJobStateChanged::FINISHED, timestampNs);
-//}
-//
+std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
+        const vector<int>& attributionUids, const vector<string>& attributionTags,
+        const string& jobName, const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    vector<const char*> cTags(attributionTags.size());
+    for (int i = 0; i < cTags.size(); i++) {
+        cTags[i] = attributionTags[i].c_str();
+    }
+
+    AStatsEvent_writeAttributionChain(statsEvent,
+                                      reinterpret_cast<const uint32_t*>(attributionUids.data()),
+                                      cTags.data(), attributionUids.size());
+    AStatsEvent_writeString(statsEvent, jobName.c_str());
+    AStatsEvent_writeInt32(statsEvent, state);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+
+    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+    return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
+                                                       const vector<int>& attributionUids,
+                                                       const vector<string>& attributionTags,
+                                                       const string& jobName) {
+    return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
+                                               ScheduledJobStateChanged::STARTED, timestampNs);
+}
+
+// Create log event when scheduled job finishes.
+std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
+                                                        const vector<int>& attributionUids,
+                                                        const vector<string>& attributionTags,
+                                                        const string& jobName) {
+    return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
+                                               ScheduledJobStateChanged::FINISHED, timestampNs);
+}
+
 std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(uint64_t timestampNs,
                                                           const vector<int>& attributionUids,
                                                           const vector<string>& attributionTags,
@@ -833,6 +864,62 @@
     return logEvent;
 }
 
+std::unique_ptr<LogEvent> CreateBleScanStateChangedEvent(uint64_t timestampNs,
+                                                         const vector<int>& attributionUids,
+                                                         const vector<string>& attributionTags,
+                                                         const BleScanStateChanged::State state,
+                                                         const bool filtered, const bool firstMatch,
+                                                         const bool opportunistic) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, util::BLE_SCAN_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    vector<const char*> cTags(attributionTags.size());
+    for (int i = 0; i < cTags.size(); i++) {
+        cTags[i] = attributionTags[i].c_str();
+    }
+
+    AStatsEvent_writeAttributionChain(statsEvent,
+                                      reinterpret_cast<const uint32_t*>(attributionUids.data()),
+                                      cTags.data(), attributionUids.size());
+    AStatsEvent_writeInt32(statsEvent, state);
+    AStatsEvent_writeInt32(statsEvent, filtered);       // filtered
+    AStatsEvent_writeInt32(statsEvent, firstMatch);     // first match
+    AStatsEvent_writeInt32(statsEvent, opportunistic);  // opportunistic
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+
+    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+    return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateOverlayStateChangedEvent(int64_t timestampNs, const int32_t uid,
+                                                         const string& packageName,
+                                                         const bool usingAlertWindow,
+                                                         const OverlayStateChanged::State state) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, util::OVERLAY_STATE_CHANGED);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+
+    AStatsEvent_writeInt32(statsEvent, uid);
+    AStatsEvent_writeString(statsEvent, packageName.c_str());
+    AStatsEvent_writeInt32(statsEvent, usingAlertWindow);
+    AStatsEvent_writeInt32(statsEvent, state);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+
+    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
+    logEvent->parseBuffer(buf, size);
+    AStatsEvent_release(statsEvent);
+    return logEvent;
+}
+
 sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
                                               const StatsdConfig& config, const ConfigKey& key,
                                               const shared_ptr<IPullAtomCallback>& puller,
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index ead041c..7c01755 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -187,6 +187,8 @@
 
 std::shared_ptr<LogEvent> CreateNoValuesLogEvent(int atomId, int64_t eventTimeNs);
 
+void CreateNoValuesLogEvent(LogEvent* logEvent, int atomId, int64_t eventTimeNs);
+
 // Create log event for screen state changed.
 std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
         uint64_t timestampNs, const android::view::DisplayStateEnum state);
@@ -195,14 +197,16 @@
 std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(uint64_t timestampNs, int level);
 
 // Create log event when scheduled job starts.
-std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(
-    const std::vector<AttributionNodeInternal>& attributions,
-    const string& name, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
+                                                       const vector<int>& attributionUids,
+                                                       const vector<string>& attributionTags,
+                                                       const string& jobName);
 
 // Create log event when scheduled job finishes.
-std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(
-    const std::vector<AttributionNodeInternal>& attributions,
-    const string& name, uint64_t timestampNs);
+std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
+                                                        const vector<int>& attributionUids,
+                                                        const vector<string>& attributionTags,
+                                                        const string& jobName);
 
 // Create log event when battery saver starts.
 std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs);
@@ -247,6 +251,18 @@
 std::unique_ptr<LogEvent> CreateUidProcessStateChangedEvent(
         uint64_t timestampNs, int uid, const android::app::ProcessStateEnum state);
 
+std::unique_ptr<LogEvent> CreateBleScanStateChangedEvent(uint64_t timestampNs,
+                                                         const vector<int>& attributionUids,
+                                                         const vector<string>& attributionTags,
+                                                         const BleScanStateChanged::State state,
+                                                         const bool filtered, const bool firstMatch,
+                                                         const bool opportunistic);
+
+std::unique_ptr<LogEvent> CreateOverlayStateChangedEvent(int64_t timestampNs, const int32_t uid,
+                                                         const string& packageName,
+                                                         const bool usingAlertWindow,
+                                                         const OverlayStateChanged::State state);
+
 // Helper function to create an AttributionNodeInternal proto.
 AttributionNodeInternal CreateAttribution(const int& uid, const string& tag);
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index bd3fee2d..21b56d3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4974,10 +4974,8 @@
     ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
             int configChanges, boolean getNonConfigInstance, String reason) {
         ActivityClientRecord r = mActivities.get(token);
-        Class<? extends Activity> activityClass = null;
         if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
         if (r != null) {
-            activityClass = r.activity.getClass();
             r.activity.mConfigChangeFlags |= configChanges;
             if (finishing) {
                 r.activity.mFinished = true;
@@ -5030,7 +5028,6 @@
         synchronized (mResourcesManager) {
             mActivities.remove(token);
         }
-        StrictMode.decrementExpectedActivityCount(activityClass);
         return r;
     }
 
@@ -5050,6 +5047,7 @@
         ActivityClientRecord r = performDestroyActivity(token, finishing,
                 configChanges, getNonConfigInstance, reason);
         if (r != null) {
+            Class<? extends Activity> activityClass = r.activity.getClass();
             cleanUpPendingRemoveWindows(r, finishing);
             WindowManager wm = r.activity.getWindowManager();
             View v = r.activity.mDecor;
@@ -5074,14 +5072,14 @@
                 }
                 if (wtoken != null && r.mPendingRemoveWindow == null) {
                     WindowManagerGlobal.getInstance().closeAll(wtoken,
-                            r.activity.getClass().getName(), "Activity");
+                            activityClass.getName(), "Activity");
                 } else if (r.mPendingRemoveWindow != null) {
                     // We're preserving only one window, others should be closed so app views
                     // will be detached before the final tear down. It should be done now because
                     // some components (e.g. WebView) rely on detach callbacks to perform receiver
                     // unregister and other cleanup.
                     WindowManagerGlobal.getInstance().closeAllExceptView(token, v,
-                            r.activity.getClass().getName(), "Activity");
+                            activityClass.getName(), "Activity");
                 }
                 r.activity.mDecor = null;
             }
@@ -5093,18 +5091,23 @@
                 // about leaking windows, because that is a bug, so if they are
                 // using this recreate facility then they get to live with leaks.
                 WindowManagerGlobal.getInstance().closeAll(token,
-                        r.activity.getClass().getName(), "Activity");
+                        activityClass.getName(), "Activity");
             }
 
             // Mocked out contexts won't be participating in the normal
             // process lifecycle, but if we're running with a proper
             // ApplicationContext we need to have it tear down things
             // cleanly.
-            Context c = r.activity.getBaseContext();
-            if (c instanceof ContextImpl) {
-                ((ContextImpl) c).scheduleFinalCleanup(
-                        r.activity.getClass().getName(), "Activity");
+            final ContextImpl impl = ContextImpl.getImpl(r.activity);
+            if (impl != null) {
+                impl.scheduleFinalCleanup(activityClass.getName(), "Activity");
             }
+
+            r.activity = null;
+            r.window = null;
+            r.hideForNow = false;
+            r.nextIdle = null;
+            StrictMode.decrementExpectedActivityCount(activityClass);
         }
         if (finishing) {
             try {
@@ -5334,10 +5337,6 @@
 
         handleDestroyActivity(r.token, false, configChanges, true, reason);
 
-        r.activity = null;
-        r.window = null;
-        r.hideForNow = false;
-        r.nextIdle = null;
         // Merge any pending results and pending intents; don't just replace them
         if (pendingResults != null) {
             if (r.pendingResults == null) {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 26db8f3..a5dcefc 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1088,8 +1088,9 @@
     public static final int OP_ACTIVATE_PLATFORM_VPN = AppProtoEnums.APP_OP_ACTIVATE_PLATFORM_VPN;
     /** @hide */
     public static final int OP_LOADER_USAGE_STATS = AppProtoEnums.APP_OP_LOADER_USAGE_STATS;
-    /** @hide Access telephony call audio */
-    public static final int OP_ACCESS_CALL_AUDIO = AppProtoEnums.APP_OP_ACCESS_CALL_AUDIO;
+
+    // App op deprecated/removed.
+    private static final int OP_DEPRECATED_1 = AppProtoEnums.APP_OP_DEPRECATED_1;
 
     /** @hide Auto-revoke app permissions if app is unused for an extended period */
     public static final int OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED =
@@ -1396,9 +1397,6 @@
     @SystemApi
     public static final String OPSTR_MANAGE_EXTERNAL_STORAGE =
             "android:manage_external_storage";
-    /** @hide Access telephony call audio */
-    @SystemApi
-    public static final String OPSTR_ACCESS_CALL_AUDIO = "android:access_call_audio";
 
     /** @hide Auto-revoke app permissions if app is unused for an extended period */
     @SystemApi
@@ -1498,7 +1496,6 @@
             OP_MANAGE_EXTERNAL_STORAGE,
             OP_INTERACT_ACROSS_PROFILES,
             OP_LOADER_USAGE_STATS,
-            OP_ACCESS_CALL_AUDIO,
             OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
             OP_AUTO_REVOKE_MANAGED_BY_INSTALLER,
     };
@@ -1608,7 +1605,7 @@
             OP_INTERACT_ACROSS_PROFILES,        //INTERACT_ACROSS_PROFILES
             OP_ACTIVATE_PLATFORM_VPN,           // ACTIVATE_PLATFORM_VPN
             OP_LOADER_USAGE_STATS,              // LOADER_USAGE_STATS
-            OP_ACCESS_CALL_AUDIO,               // ACCESS_CALL_AUDIO
+            OP_DEPRECATED_1,                    // deprecated
             OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, //AUTO_REVOKE_PERMISSIONS_IF_UNUSED
             OP_AUTO_REVOKE_MANAGED_BY_INSTALLER, //OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
     };
@@ -1713,7 +1710,7 @@
             OPSTR_INTERACT_ACROSS_PROFILES,
             OPSTR_ACTIVATE_PLATFORM_VPN,
             OPSTR_LOADER_USAGE_STATS,
-            OPSTR_ACCESS_CALL_AUDIO,
+            "", // deprecated
             OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
             OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER,
     };
@@ -1819,7 +1816,7 @@
             "INTERACT_ACROSS_PROFILES",
             "ACTIVATE_PLATFORM_VPN",
             "LOADER_USAGE_STATS",
-            "ACCESS_CALL_AUDIO",
+            "deprecated",
             "AUTO_REVOKE_PERMISSIONS_IF_UNUSED",
             "AUTO_REVOKE_MANAGED_BY_INSTALLER",
     };
@@ -1926,7 +1923,7 @@
             android.Manifest.permission.INTERACT_ACROSS_PROFILES,
             null, // no permission for OP_ACTIVATE_PLATFORM_VPN
             android.Manifest.permission.LOADER_USAGE_STATS,
-            Manifest.permission.ACCESS_CALL_AUDIO,
+            null, // deprecated operation
             null, // no permission for OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
             null, // no permission for OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
     };
@@ -2033,7 +2030,7 @@
             null, // INTERACT_ACROSS_PROFILES
             null, // ACTIVATE_PLATFORM_VPN
             null, // LOADER_USAGE_STATS
-            null, // ACCESS_CALL_AUDIO
+            null, // deprecated operation
             null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
             null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
     };
@@ -2139,7 +2136,7 @@
             null, // INTERACT_ACROSS_PROFILES
             null, // ACTIVATE_PLATFORM_VPN
             null, // LOADER_USAGE_STATS
-            null, // ACCESS_CALL_AUDIO
+            null, // deprecated operation
             null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
             null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
     };
@@ -2244,7 +2241,7 @@
             AppOpsManager.MODE_DEFAULT, // INTERACT_ACROSS_PROFILES
             AppOpsManager.MODE_IGNORED, // ACTIVATE_PLATFORM_VPN
             AppOpsManager.MODE_DEFAULT, // LOADER_USAGE_STATS
-            AppOpsManager.MODE_DEFAULT, // ACCESS_CALL_AUDIO
+            AppOpsManager.MODE_IGNORED, // deprecated operation
             AppOpsManager.MODE_DEFAULT, // OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
             AppOpsManager.MODE_ALLOWED, // OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
     };
@@ -2353,7 +2350,7 @@
             false, // INTERACT_ACROSS_PROFILES
             false, // ACTIVATE_PLATFORM_VPN
             false, // LOADER_USAGE_STATS
-            false, // ACCESS_CALL_AUDIO
+            false, // deprecated operation
             false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
             false, // AUTO_REVOKE_MANAGED_BY_INSTALLER
     };
@@ -8446,7 +8443,9 @@
 
     /**
      * Pulls current AppOps access report and picks package and op to watch for next access report
-     *
+     * Returns null if no reports were collected since last call. There is no guarantee of report
+     * collection, hence this method should be called periodically even if no report was collected
+     * to pick different package and op to watch.
      * @hide
      */
     @SystemApi
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index 61be01f..0ecc003 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -499,9 +499,11 @@
 
     /**
      * Return the defining kernel user identifier, maybe different from {@link #getRealUid} and
-     * {@link #getPackageUid}, if an external service was bound with the flag
-     * {@link android.content.Context#BIND_EXTERNAL_SERVICE} - in this case, this field here
-     * will be the kernel user identifier of the external service provider.
+     * {@link #getPackageUid}, if an external service has the
+     * {@link android.R.styleable#AndroidManifestService_useAppZygote android:useAppZygote} set
+     * to <code>true<code> and was bound with the flag
+     * {@link android.content.Context#BIND_EXTERNAL_SERVICE} - in this case, this field here will
+     * be the kernel user identifier of the external service provider.
      */
     public int getDefiningUid() {
         return mDefiningUid;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9ccfe8d..d8757c3 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1920,7 +1920,9 @@
         return SystemServiceRegistry.getSystemServiceName(serviceClass);
     }
 
-    private boolean isUiContext() {
+    /** @hide */
+    @Override
+    public boolean isUiContext() {
         return mIsSystemOrSystemUiContext || mIsUiContext || isSystemOrSystemUI();
     }
 
@@ -2414,8 +2416,7 @@
                 : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
         final List<ResourcesLoader> loaders = mResources.getLoaders();
 
-        // TODO(b/128338354): Rename to createTokenResources
-        return mResourcesManager.createBaseActivityResources(mToken, resDir, splitResDirs,
+        return mResourcesManager.createBaseTokenResources(mToken, resDir, splitResDirs,
                 overlayDirs, libDirs, displayId, null /* overrideConfig */,
                 compatInfo, mClassLoader, loaders);
     }
@@ -2684,7 +2685,7 @@
 
         // Create the base resources for which all configuration contexts for this Activity
         // will be rebased upon.
-        context.setResources(resourcesManager.createBaseActivityResources(activityToken,
+        context.setResources(resourcesManager.createBaseTokenResources(activityToken,
                 packageInfo.getResDir(),
                 splitDirs,
                 packageInfo.getOverlayDirs(),
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index b8221b4..833bfed 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -99,6 +99,7 @@
     void unregisterUidObserver(in IUidObserver observer);
     boolean isUidActive(int uid, String callingPackage);
     int getUidProcessState(int uid, in String callingPackage);
+    boolean isUidActiveOrForeground(int uid, String callingPackage);
     // =============== End of transactions used on native side as well ============================
 
     // Special low-level communication with activity manager.
@@ -143,9 +144,6 @@
     void attachApplication(in IApplicationThread app, long startSeq);
     List<ActivityManager.RunningTaskInfo> getTasks(int maxNum);
     @UnsupportedAppUsage
-    List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, int ignoreActivityType,
-            int ignoreWindowingMode);
-    @UnsupportedAppUsage
     void moveTaskToFront(in IApplicationThread caller, in String callingPackage, int task,
             int flags, in Bundle options);
     @UnsupportedAppUsage
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 03717ec..e476993 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -155,8 +155,8 @@
     boolean removeTask(int taskId);
     void removeAllVisibleRecentTasks();
     List<ActivityManager.RunningTaskInfo> getTasks(int maxNum);
-    List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, int ignoreActivityType,
-            int ignoreWindowingMode);
+    List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum,
+            boolean filterOnlyVisibleRecents);
     boolean shouldUpRecreateTask(in IBinder token, in String destAffinity);
     boolean navigateUpTo(in IBinder token, in Intent target, int resultCode,
             in Intent resultData);
@@ -298,13 +298,6 @@
     void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration,
             in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
 
-    /**
-     * Dismisses PiP
-     * @param animate True if the dismissal should be animated.
-     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
-     *                          default animation duration should be used.
-     */
-    void dismissPip(boolean animate, int animationDuration);
     void suppressResizeConfigChanges(boolean suppress);
     void moveTasksToFullscreenStack(int fromStackId, boolean onTop);
     boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 948546b..78d3581 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -48,8 +48,6 @@
     void clearData(String pkg, int uid, boolean fromApp);
     void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, int displayId, @nullable ITransientNotificationCallback callback);
     void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId);
-    // TODO(b/144152069): Remove this after assessing impact on dogfood.
-    void enqueueTextOrCustomToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId, boolean isCustom);
     void cancelToast(String pkg, IBinder token);
     void finishToken(String pkg, IBinder token);
 
@@ -123,10 +121,14 @@
     // INotificationListener method.
     @UnsupportedAppUsage
     StatusBarNotification[] getActiveNotifications(String callingPkg);
+    StatusBarNotification[] getActiveNotificationsWithAttribution(String callingPkg,
+            String callingAttributionTag);
     @UnsupportedAppUsage
     StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count, boolean includeSnoozed);
+    StatusBarNotification[] getHistoricalNotificationsWithAttribution(String callingPkg,
+            String callingAttributionTag, int count, boolean includeSnoozed);
 
-    NotificationHistory getNotificationHistory(String callingPkg);
+    NotificationHistory getNotificationHistory(String callingPkg, String callingAttributionTag);
 
     void registerListener(in INotificationListener listener, in ComponentName component, int userid);
     void unregisterListener(in INotificationListener listener, int userid);
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index d650801..10f7835 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -801,6 +801,11 @@
         makePaths(mActivityThread, isBundledApp, mApplicationInfo, zipPaths, libPaths);
 
         String libraryPermittedPath = mDataDir;
+        if (mActivityThread == null) {
+            // In a zygote context where mActivityThread is null we can't access the app data dir
+            // and including this in libraryPermittedPath would cause SELinux denials.
+            libraryPermittedPath = "";
+        }
 
         if (isBundledApp) {
             // For bundled apps, add the base directory of the app (e.g.,
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 811b9c0..0e97e3f 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -29,6 +29,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Build;
@@ -841,7 +842,8 @@
     }
 
     /**
-     * Returns the notification channel settings for a given channel and conversation id.
+     * Returns the notification channel settings for a given channel and
+     * {@link ShortcutInfo#getId() conversation id}.
      *
      * <p>The channel must belong to your package, or to a package you are an approved notification
      * delegate for (see {@link #canNotifyAsPackage(String)}), or it will not be returned. To query
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 5f75603..392c05a 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -695,26 +695,26 @@
     }
 
     /**
-     * Creates base resources for an Activity. Calls to
+     * Creates base resources for a binder token. Calls to
      * {@link #getResources(IBinder, String, String[], String[], String[], int, Configuration,
-     * CompatibilityInfo, ClassLoader, List)} with the same activityToken will have their override
+     * CompatibilityInfo, ClassLoader, List)} with the same binder token will have their override
      * configurations merged with the one specified here.
      *
-     * @param activityToken Represents an Activity.
+     * @param token Represents an {@link Activity} or {@link WindowContext}.
      * @param resDir The base resource path. Can be null (only framework resources will be loaded).
      * @param splitResDirs An array of split resource paths. Can be null.
      * @param overlayDirs An array of overlay paths. Can be null.
      * @param libDirs An array of resource library paths. Can be null.
      * @param displayId The ID of the display for which to create the resources.
      * @param overrideConfig The configuration to apply on top of the base configuration. Can be
-     *                       null. This provides the base override for this Activity.
+     *                       {@code null}. This provides the base override for this token.
      * @param compatInfo The compatibility settings to use. Cannot be null. A default to use is
      *                   {@link CompatibilityInfo#DEFAULT_COMPATIBILITY_INFO}.
      * @param classLoader The class loader to use when inflating Resources. If null, the
      *                    {@link ClassLoader#getSystemClassLoader()} is used.
      * @return a Resources object from which to access resources.
      */
-    public @Nullable Resources createBaseActivityResources(@NonNull IBinder activityToken,
+    public @Nullable Resources createBaseTokenResources(@NonNull IBinder token,
             @Nullable String resDir,
             @Nullable String[] splitResDirs,
             @Nullable String[] overlayDirs,
@@ -739,24 +739,24 @@
             classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
 
             if (DEBUG) {
-                Slog.d(TAG, "createBaseActivityResources activity=" + activityToken
+                Slog.d(TAG, "createBaseActivityResources activity=" + token
                         + " with key=" + key);
             }
 
             synchronized (this) {
                 // Force the creation of an ActivityResourcesStruct.
-                getOrCreateActivityResourcesStructLocked(activityToken);
+                getOrCreateActivityResourcesStructLocked(token);
             }
 
             // Update any existing Activity Resources references.
-            updateResourcesForActivity(activityToken, overrideConfig, displayId,
+            updateResourcesForActivity(token, overrideConfig, displayId,
                     false /* movedToDifferentDisplay */);
 
-            cleanupReferences(activityToken);
-            rebaseKeyForActivity(activityToken, key);
+            cleanupReferences(token);
+            rebaseKeyForActivity(token, key);
 
             synchronized (this) {
-                Resources resources = findResourcesForActivityLocked(activityToken, key,
+                Resources resources = findResourcesForActivityLocked(token, key,
                         classLoader);
                 if (resources != null) {
                     return resources;
@@ -764,7 +764,7 @@
             }
 
             // Now request an actual Resources object.
-            return createResources(activityToken, key, classLoader);
+            return createResources(token, key, classLoader);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
diff --git a/core/java/android/app/TaskEmbedder.java b/core/java/android/app/TaskEmbedder.java
index 5ebcc46..b8ad308 100644
--- a/core/java/android/app/TaskEmbedder.java
+++ b/core/java/android/app/TaskEmbedder.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
@@ -291,7 +292,7 @@
      * @see #startActivity(PendingIntent)
      */
     public void startActivity(@NonNull Intent intent) {
-        final ActivityOptions options = prepareActivityOptions();
+        final ActivityOptions options = prepareActivityOptions(null);
         mContext.startActivity(intent, options.toBundle());
     }
 
@@ -304,7 +305,7 @@
      * @see #startActivity(PendingIntent)
      */
     public void startActivity(@NonNull Intent intent, UserHandle user) {
-        final ActivityOptions options = prepareActivityOptions();
+        final ActivityOptions options = prepareActivityOptions(null);
         mContext.startActivityAsUser(intent, options.toBundle(), user);
     }
 
@@ -316,7 +317,7 @@
      * @see #startActivity(Intent)
      */
     public void startActivity(@NonNull PendingIntent pendingIntent) {
-        final ActivityOptions options = prepareActivityOptions();
+        final ActivityOptions options = prepareActivityOptions(null);
         try {
             pendingIntent.send(null /* context */, 0 /* code */, null /* intent */,
                     null /* onFinished */, null /* handler */, null /* requiredPermission */,
@@ -337,8 +338,7 @@
      */
     public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
             @NonNull ActivityOptions options) {
-
-        options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId());
+        prepareActivityOptions(options);
         try {
             pendingIntent.send(mContext, 0 /* code */, fillInIntent,
                     null /* onFinished */, null /* handler */, null /* requiredPermission */,
@@ -364,21 +364,25 @@
             @NonNull ActivityOptions options, @Nullable Rect sourceBounds) {
         LauncherApps service =
                 (LauncherApps) mContext.getSystemService(Context.LAUNCHER_APPS_SERVICE);
-        options.setLaunchDisplayId(mVirtualDisplay.getDisplay().getDisplayId());
+        prepareActivityOptions(options);
         service.startShortcut(shortcut, sourceBounds, options.toBundle());
     }
 
     /**
-     * Check if container is ready to launch and create {@link ActivityOptions} to target the
-     * virtual display.
+     * Check if container is ready to launch and modify {@param options} to target the virtual
+     * display, creating them if necessary.
      */
-    private ActivityOptions prepareActivityOptions() {
+    private ActivityOptions prepareActivityOptions(ActivityOptions options) {
         if (mVirtualDisplay == null) {
             throw new IllegalStateException(
                     "Trying to start activity before ActivityView is ready.");
         }
-        final ActivityOptions options = ActivityOptions.makeBasic();
+        if (options == null) {
+            options = ActivityOptions.makeBasic();
+        }
         options.setLaunchDisplayId(getDisplayId());
+        options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        options.setTaskAlwaysOnTop(true);
         return options;
     }
 
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 6b40890..37e07de 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -141,6 +141,8 @@
     public static final int ACTIVITY_TYPE_RECENTS = 3;
     /** Assistant activity type. */
     public static final int ACTIVITY_TYPE_ASSISTANT = 4;
+    /** Dream activity type. */
+    public static final int ACTIVITY_TYPE_DREAM = 5;
 
     /** @hide */
     @IntDef(prefix = { "ACTIVITY_TYPE_" }, value = {
@@ -149,6 +151,7 @@
             ACTIVITY_TYPE_HOME,
             ACTIVITY_TYPE_RECENTS,
             ACTIVITY_TYPE_ASSISTANT,
+            ACTIVITY_TYPE_DREAM,
     })
     public @interface ActivityType {}
 
@@ -746,9 +749,11 @@
      * @hide
      */
     public boolean isAlwaysOnTop() {
-        return mWindowingMode == WINDOWING_MODE_PINNED || (mAlwaysOnTop == ALWAYS_ON_TOP_ON
-                && (mWindowingMode == WINDOWING_MODE_FREEFORM
-                    || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW));
+        if (mWindowingMode == WINDOWING_MODE_PINNED) return true;
+        if (mActivityType == ACTIVITY_TYPE_DREAM) return true;
+        if (mAlwaysOnTop != ALWAYS_ON_TOP_ON) return false;
+        return mWindowingMode == WINDOWING_MODE_FREEFORM
+                    || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW;
     }
 
     /**
@@ -798,7 +803,7 @@
 
     /** @hide */
     public static boolean supportSplitScreenWindowingMode(int activityType) {
-        return activityType != ACTIVITY_TYPE_ASSISTANT;
+        return activityType != ACTIVITY_TYPE_ASSISTANT && activityType != ACTIVITY_TYPE_DREAM;
     }
 
     /** @hide */
@@ -823,6 +828,7 @@
             case ACTIVITY_TYPE_HOME: return "home";
             case ACTIVITY_TYPE_RECENTS: return "recents";
             case ACTIVITY_TYPE_ASSISTANT: return "assistant";
+            case ACTIVITY_TYPE_DREAM: return "dream";
         }
         return String.valueOf(applicationType);
     }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c4458b3..10309a9 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4424,11 +4424,13 @@
      * the current factory reset protection (FRP) policy set previously by
      * {@link #setFactoryResetProtectionPolicy}.
      * <p>
-     * This method can also be called by the FRP management agent on device, in which case,
-     * it can pass {@code null} as the ComponentName.
+     * This method can also be called by the FRP management agent on device or with the permission
+     * {@link android.Manifest.permission#MASTER_CLEAR}, in which case, it can pass {@code null}
+     * as the ComponentName.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with or
-     *              {@code null} if called by the FRP management agent on device.
+     *              {@code null} if called by the FRP management agent on device or with the
+     *              permission {@link android.Manifest.permission#MASTER_CLEAR}.
      * @return The current FRP policy object or {@code null} if no policy is set.
      * @throws SecurityException if {@code admin} is not a device owner, a profile owner of
      *                           an organization-owned device or the FRP management agent.
diff --git a/core/java/android/app/admin/FactoryResetProtectionPolicy.java b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
index 954db04..aa94e81 100644
--- a/core/java/android/app/admin/FactoryResetProtectionPolicy.java
+++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
@@ -43,6 +43,12 @@
  * reset protection policy for the device by calling the {@code DevicePolicyManager} method
  * {@link DevicePolicyManager#setFactoryResetProtectionPolicy(ComponentName,
  * FactoryResetProtectionPolicy)}}.
+ * <p>
+ * Normally factory reset protection does not kick in if the device is factory reset via Settings.
+ * This is also the case when a device owner sets factory reset protection policy. However,
+ * when a profile owner of an organization-owned device sets factory reset protection policy that
+ * locks the device to specific accounts, the policy will take effect even if factory reset is
+ * performed from Settings.
  *
  * @see DevicePolicyManager#setFactoryResetProtectionPolicy
  * @see DevicePolicyManager#getFactoryResetProtectionPolicy
@@ -236,4 +242,16 @@
         }
     }
 
+    /**
+     * Returns if the policy will result in factory reset protection being locked to
+     * admin-specified accounts.
+     * <p>
+     * When a device has a non-empty factory reset protection policy, trusted factory reset
+     * via Settings will no longer remove factory reset protection from the device.
+     * @hide
+     */
+    public boolean isNotEmpty() {
+        return !mFactoryResetProtectionAccounts.isEmpty() && mFactoryResetProtectionEnabled;
+    }
+
 }
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index e0674d7..fa62a02 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -379,6 +379,7 @@
     public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+        verifyDeviceNotNull(device, "setConnectionPolicy");
         final IBluetoothHearingAid service = getService();
         try {
             if (service != null && isEnabled()
@@ -428,6 +429,7 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
+        verifyDeviceNotNull(device, "getConnectionPolicy");
         final IBluetoothHearingAid service = getService();
         try {
             if (service != null && isEnabled()
@@ -504,6 +506,7 @@
         if (VDBG) {
             log("getHiSyncId(" + device + ")");
         }
+        verifyDeviceNotNull(device, "getConnectionPolicy");
         final IBluetoothHearingAid service = getService();
         try {
             if (service == null) {
@@ -577,6 +580,13 @@
         return false;
     }
 
+    private void verifyDeviceNotNull(BluetoothDevice device, String methodName) {
+        if (device == null) {
+            Log.e(TAG, methodName + ": device param is null");
+            throw new IllegalArgumentException("Device cannot be null");
+        }
+    }
+
     private boolean isValidDevice(BluetoothDevice device) {
         if (device == null) return false;
 
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 65eb642..9cf6569 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -47,6 +47,7 @@
 import android.os.CancellationSignal;
 import android.os.IBinder;
 import android.os.ICancellationSignal;
+import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteCallback;
@@ -303,7 +304,11 @@
         @Override
         public void getTypeAsync(Uri uri, RemoteCallback callback) {
             final Bundle result = new Bundle();
-            result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri));
+            try {
+                result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getType(uri));
+            } catch (Exception e) {
+                putExceptionInBundle(result, ContentResolver.REMOTE_CALLBACK_ERROR, e);
+            }
             callback.sendResult(result);
         }
 
@@ -585,8 +590,12 @@
         public void canonicalizeAsync(String callingPkg, @Nullable String attributionTag, Uri uri,
                 RemoteCallback callback) {
             final Bundle result = new Bundle();
-            result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
-                    canonicalize(callingPkg, attributionTag, uri));
+            try {
+                result.putParcelable(ContentResolver.REMOTE_CALLBACK_RESULT,
+                        canonicalize(callingPkg, attributionTag, uri));
+            } catch (Exception e) {
+                putExceptionInBundle(result, ContentResolver.REMOTE_CALLBACK_ERROR, e);
+            }
             callback.sendResult(result);
         }
 
@@ -700,6 +709,22 @@
 
             return AppOpsManager.MODE_ALLOWED;
         }
+
+        private void putExceptionInBundle(Bundle bundle, String key, Exception e) {
+            Parcel parcel = Parcel.obtain();
+            try {
+                try {
+                    parcel.writeException(e);
+                } catch (Exception ex) {
+                    // getType threw an unparcelable exception. Wrap the message into
+                    // a parcelable exception type
+                    parcel.writeException(new IllegalStateException(e.getMessage()));
+                }
+                bundle.putByteArray(key, parcel.marshall());
+            } finally {
+                parcel.recycle();
+            }
+        }
     }
 
     boolean checkUser(int pid, int uid, Context context) {
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 7510ce7..59862ae 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -55,6 +55,7 @@
 import android.os.IBinder;
 import android.os.ICancellationSignal;
 import android.os.OperationCanceledException;
+import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
@@ -735,6 +736,9 @@
     /** @hide */
     public static final String REMOTE_CALLBACK_RESULT = "result";
 
+    /** @hide */
+    public static final String REMOTE_CALLBACK_ERROR = "error";
+
     /**
      * How long we wait for an attached process to publish its content providers
      * before we decide it must be hung.
@@ -874,6 +878,9 @@
                 final StringResultListener resultListener = new StringResultListener();
                 provider.getTypeAsync(url, new RemoteCallback(resultListener));
                 resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
+                if (resultListener.exception != null) {
+                    throw resultListener.exception;
+                }
                 return resultListener.result;
             } catch (RemoteException e) {
                 // Arbitrary and not worth documenting, as Activity
@@ -898,6 +905,9 @@
                     resolveUserId(url),
                     new RemoteCallback(resultListener));
             resultListener.waitForResult(REMOTE_CONTENT_PROVIDER_TIMEOUT_MILLIS);
+            if (resultListener.exception != null) {
+                throw resultListener.exception;
+            }
             return resultListener.result;
         } catch (RemoteException e) {
             // We just failed to send a oneway request to the System Server. Nothing to do.
@@ -915,15 +925,41 @@
         @GuardedBy("this")
         public T result;
 
+        @GuardedBy("this")
+        public RuntimeException exception;
+
         @Override
         public void onResult(Bundle result) {
             synchronized (this) {
-                this.result = getResultFromBundle(result);
+                this.exception = getExceptionFromBundle(result);
+                if (this.exception == null) {
+                    this.result = getResultFromBundle(result);
+                }
                 done = true;
                 notifyAll();
             }
         }
 
+        private RuntimeException getExceptionFromBundle(Bundle result) {
+            byte[] bytes = result.getByteArray(REMOTE_CALLBACK_ERROR);
+            if (bytes == null) {
+                return null;
+            }
+
+            Parcel parcel = Parcel.obtain();
+            try {
+                parcel.unmarshall(bytes, 0, bytes.length);
+                parcel.setDataPosition(0);
+                parcel.readException();
+            } catch (RuntimeException ex) {
+                return ex;
+            } finally {
+                parcel.recycle();
+            }
+
+            return null;
+        }
+
         protected abstract T getResultFromBundle(Bundle result);
 
         public void waitForResult(long timeout) {
@@ -1250,6 +1286,9 @@
             provider.canonicalizeAsync(mPackageName, mAttributionTag, url,
                     new RemoteCallback(resultListener));
             resultListener.waitForResult(CONTENT_PROVIDER_TIMEOUT_MILLIS);
+            if (resultListener.exception != null) {
+                throw resultListener.exception;
+            }
             return resultListener.result;
         } catch (RemoteException e) {
             // Arbitrary and not worth documenting, as Activity
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2fe935e..e21a31e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -6103,4 +6103,13 @@
                     + "get a UI context from ActivityThread#getSystemUiContext()");
         }
     }
+
+    /**
+     * Indicates if this context is a visual context such as {@link android.app.Activity} or
+     * a context created from {@link #createWindowContext(int, Bundle)}.
+     * @hide
+     */
+    public boolean isUiContext() {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
 }
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index d389d2a..5dc41e4 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1145,4 +1145,12 @@
             mBase.setContentCaptureOptions(options);
         }
     }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean isUiContext() {
+        return mBase.isUiContext();
+    }
 }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 95385ee..b1d6c83 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -11215,6 +11215,17 @@
                 && hasWebURI();
     }
 
+    private boolean isImageCaptureIntent() {
+        return (MediaStore.ACTION_IMAGE_CAPTURE.equals(mAction)
+                || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(mAction)
+                || MediaStore.ACTION_VIDEO_CAPTURE.equals(mAction));
+    }
+
+    /** @hide */
+    public boolean isImplicitImageCaptureIntent() {
+        return mPackage == null && mComponent == null && isImageCaptureIntent();
+    }
+
     /**
      * @hide
      */
@@ -11241,9 +11252,7 @@
                 }
                 putParcelableArrayListExtra(EXTRA_STREAM, newStreams);
             }
-        } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
-                || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(action)
-                || MediaStore.ACTION_VIDEO_CAPTURE.equals(action)) {
+        } else if (isImageCaptureIntent()) {
             final Uri output = getParcelableExtra(MediaStore.EXTRA_OUTPUT);
             if (output != null) {
                 putExtra(MediaStore.EXTRA_OUTPUT, maybeAddUserId(output, contentUserHint));
@@ -11349,9 +11358,7 @@
                 }
             } catch (ClassCastException e) {
             }
-        } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)
-                || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(action)
-                || MediaStore.ACTION_VIDEO_CAPTURE.equals(action)) {
+        } else if (isImageCaptureIntent()) {
             final Uri output;
             try {
                 output = getParcelableExtra(MediaStore.EXTRA_OUTPUT);
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 7d5fca4..b6706011 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -752,6 +752,30 @@
     public @interface ApplicationInfoPrivateFlags {}
 
     /**
+     * Constant corresponding to <code>allowed</code> in the
+     * {@link android.R.attr#autoRevokePermissions} attribute.
+     *
+     * @hide
+     */
+    public static final int AUTO_REVOKE_ALLOWED = 0;
+
+    /**
+     * Constant corresponding to <code>discouraged</code> in the
+     * {@link android.R.attr#autoRevokePermissions} attribute.
+     *
+     * @hide
+     */
+    public static final int AUTO_REVOKE_DISCOURAGED = 1;
+
+    /**
+     * Constant corresponding to <code>disallowed</code> in the
+     * {@link android.R.attr#autoRevokePermissions} attribute.
+     *
+     * @hide
+     */
+    public static final int AUTO_REVOKE_DISALLOWED = 2;
+
+    /**
      * Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
      * @hide
      */
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 6ba811e..7578ede 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -487,6 +487,34 @@
         }
     }
 
+    /**
+     * Clears the app-op for {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} back to
+     * its default value for every package on the device.
+     *
+     * <p>This method can be used to ensure that app-op state is not left around on existing users
+     * for previously-configured profiles.
+     *
+     * <p>If the caller does not have the {@link android.Manifest.permission
+     * #CONFIGURE_INTERACT_ACROSS_PROFILES} permission, then they must have the permissions that
+     * would have been required to call {@link android.app.AppOpsManager#setMode(int, int, String,
+     * int)}, which includes {@link android.Manifest.permission#MANAGE_APP_OPS_MODES}.
+     *
+     * <p>Also requires either {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or {@link
+     * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
+     *
+     * @hide
+     */
+    @RequiresPermission(
+            allOf={android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES,
+                    android.Manifest.permission.INTERACT_ACROSS_USERS})
+    public void clearInteractAcrossProfilesAppOps() {
+        try {
+            mService.clearInteractAcrossProfilesAppOps();
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
     private void verifyCanAccessUser(UserHandle userHandle) {
         if (!getTargetUserProfiles().contains(userHandle)) {
             throw new SecurityException("Not allowed to access " + userHandle);
diff --git a/core/java/android/content/pm/DataLoaderParams.java b/core/java/android/content/pm/DataLoaderParams.java
index 99c0907..a791026 100644
--- a/core/java/android/content/pm/DataLoaderParams.java
+++ b/core/java/android/content/pm/DataLoaderParams.java
@@ -17,12 +17,8 @@
 package android.content.pm;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.ComponentName;
-import android.os.ParcelFileDescriptor;
-
-import java.util.Map;
 
 /**
  * This class represents the parameters used to configure a Data Loader.
@@ -44,7 +40,7 @@
      */
     public static final @NonNull DataLoaderParams forStreaming(@NonNull ComponentName componentName,
             @NonNull String arguments) {
-        return new DataLoaderParams(DataLoaderType.STREAMING, componentName, arguments, null);
+        return new DataLoaderParams(DataLoaderType.STREAMING, componentName, arguments);
     }
 
     /**
@@ -55,29 +51,17 @@
      */
     public static final @NonNull DataLoaderParams forIncremental(
             @NonNull ComponentName componentName, @NonNull String arguments) {
-        return new DataLoaderParams(DataLoaderType.INCREMENTAL, componentName, arguments, null);
+        return new DataLoaderParams(DataLoaderType.INCREMENTAL, componentName, arguments);
     }
 
     /** @hide */
     public DataLoaderParams(@NonNull @DataLoaderType int type, @NonNull ComponentName componentName,
-            @NonNull String arguments, @Nullable Map<String, ParcelFileDescriptor> namedFds) {
+            @NonNull String arguments) {
         DataLoaderParamsParcel data = new DataLoaderParamsParcel();
         data.type = type;
         data.packageName = componentName.getPackageName();
         data.className = componentName.getClassName();
         data.arguments = arguments;
-        if (namedFds == null || namedFds.isEmpty()) {
-            data.dynamicArgs = new NamedParcelFileDescriptor[0];
-        } else {
-            data.dynamicArgs = new NamedParcelFileDescriptor[namedFds.size()];
-            int i = 0;
-            for (Map.Entry<String, ParcelFileDescriptor> namedFd : namedFds.entrySet()) {
-                data.dynamicArgs[i] = new NamedParcelFileDescriptor();
-                data.dynamicArgs[i].name = namedFd.getKey();
-                data.dynamicArgs[i].fd = namedFd.getValue();
-                i += 1;
-            }
-        }
         mData = data;
     }
 
diff --git a/core/java/android/content/pm/DataLoaderParamsParcel.aidl b/core/java/android/content/pm/DataLoaderParamsParcel.aidl
index e05843b..d40012fd 100644
--- a/core/java/android/content/pm/DataLoaderParamsParcel.aidl
+++ b/core/java/android/content/pm/DataLoaderParamsParcel.aidl
@@ -17,7 +17,6 @@
 package android.content.pm;
 
 import android.content.pm.DataLoaderType;
-import android.content.pm.NamedParcelFileDescriptor;
 
 /**
  * Class for holding data loader configuration parameters.
@@ -28,5 +27,4 @@
     @utf8InCpp String packageName;
     @utf8InCpp String className;
     @utf8InCpp String arguments;
-    NamedParcelFileDescriptor[] dynamicArgs;
 }
diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
index 9b0dae2..e2850f1 100644
--- a/core/java/android/content/pm/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -40,4 +40,5 @@
     boolean canConfigureInteractAcrossProfiles(in String packageName);
     boolean canUserAttemptToConfigureInteractAcrossProfiles(in String packageName);
     void resetInteractAcrossProfilesAppOps(in List<String> packageNames);
+    void clearInteractAcrossProfilesAppOps();
 }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 50bee85..1e0b2e3 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1118,6 +1118,7 @@
          * {@hide}
          */
         @SystemApi
+        @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2)
         public @Nullable DataLoaderParams getDataLoaderParams() {
             try {
                 DataLoaderParamsParcel data = mSession.getDataLoaderParams();
@@ -1157,6 +1158,7 @@
          * {@hide}
          */
         @SystemApi
+        @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2)
         public void addFile(@FileLocation int location, @NonNull String name, long lengthBytes,
                 @NonNull byte[] metadata, @Nullable byte[] signature) {
             try {
@@ -1180,6 +1182,7 @@
          * {@hide}
          */
         @SystemApi
+        @RequiresPermission(android.Manifest.permission.USE_INSTALLER_V2)
         public void removeFile(@FileLocation int location, @NonNull String name) {
             try {
                 mSession.removeFile(location, name);
@@ -1927,7 +1930,9 @@
          * {@hide}
          */
         @SystemApi
-        @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
+        @RequiresPermission(allOf = {
+                Manifest.permission.INSTALL_PACKAGES,
+                Manifest.permission.USE_INSTALLER_V2})
         public void setDataLoaderParams(@NonNull DataLoaderParams dataLoaderParams) {
             this.dataLoaderParams = dataLoaderParams;
         }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 20ddba4..1dadbda 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1837,6 +1837,12 @@
 
         pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
 
+        final boolean isolatedSplits = sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifest_isolatedSplits, false);
+        if (isolatedSplits) {
+            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING;
+        }
+
         pkg.mCompileSdkVersion = sa.getInteger(
                 com.android.internal.R.styleable.AndroidManifest_compileSdkVersion, 0);
         pkg.applicationInfo.compileSdkVersion = pkg.mCompileSdkVersion;
@@ -1912,10 +1918,6 @@
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE;
         }
 
-        if (sa.getBoolean(com.android.internal.R.styleable.AndroidManifest_isolatedSplits, false)) {
-            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING;
-        }
-
         // Resource boolean are -1, so 1 means we don't know the value.
         int supportsSmallScreens = 1;
         int supportsNormalScreens = 1;
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 39f2858..4c95532 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -192,9 +192,7 @@
 
     ParsingPackage setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging);
 
-    ParsingPackage setDontAutoRevokePermissions(boolean dontAutoRevokePermissions);
-
-    ParsingPackage setAllowDontAutoRevokePermissions(boolean allowDontAutoRevokePermissions);
+    ParsingPackage setAutoRevokePermissions(int autoRevokePermissions);
 
     ParsingPackage setPreserveLegacyExternalStorage(boolean preserveLegacyExternalStorage);
 
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index f2ab60a..894ad55 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -30,7 +30,6 @@
 import android.content.pm.FeatureInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageParser;
-import android.content.pm.ProcessInfo;
 import android.content.pm.parsing.component.ParsedActivity;
 import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedComponent;
@@ -405,8 +404,7 @@
     private boolean hasFragileUserData;
     private boolean cantSaveState;
     private boolean allowNativeHeapPointerTagging;
-    private boolean dontAutoRevokePermissions;
-    private boolean allowDontAutoRevokePermissions;
+    private int autoRevokePermissions;
     private boolean preserveLegacyExternalStorage;
 
     protected int gwpAsanMode;
@@ -435,6 +433,10 @@
                     R.styleable.AndroidManifest_compileSdkVersion, 0));
             setCompileSdkVersionCodename(manifestArray.getNonConfigurationString(
                     R.styleable.AndroidManifest_compileSdkVersionCodename, 0));
+
+            setIsolatedSplitLoading(manifestArray.getBoolean(
+                    R.styleable.AndroidManifest_isolatedSplits, false));
+
         }
     }
 
@@ -1089,8 +1091,7 @@
         dest.writeBoolean(this.hasFragileUserData);
         dest.writeBoolean(this.cantSaveState);
         dest.writeBoolean(this.allowNativeHeapPointerTagging);
-        dest.writeBoolean(this.dontAutoRevokePermissions);
-        dest.writeBoolean(this.allowDontAutoRevokePermissions);
+        dest.writeInt(this.autoRevokePermissions);
         dest.writeBoolean(this.preserveLegacyExternalStorage);
         dest.writeArraySet(this.mimeGroups);
         dest.writeInt(this.gwpAsanMode);
@@ -1249,8 +1250,7 @@
         this.hasFragileUserData = in.readBoolean();
         this.cantSaveState = in.readBoolean();
         this.allowNativeHeapPointerTagging = in.readBoolean();
-        this.dontAutoRevokePermissions = in.readBoolean();
-        this.allowDontAutoRevokePermissions = in.readBoolean();
+        this.autoRevokePermissions = in.readInt();
         this.preserveLegacyExternalStorage = in.readBoolean();
         this.mimeGroups = (ArraySet<String>) in.readArraySet(boot);
         this.gwpAsanMode = in.readInt();
@@ -2026,13 +2026,8 @@
     }
 
     @Override
-    public boolean isDontAutoRevokePermmissions() {
-        return dontAutoRevokePermissions;
-    }
-
-    @Override
-    public boolean isAllowDontAutoRevokePermmissions() {
-        return allowDontAutoRevokePermissions;
+    public int getAutoRevokePermissions() {
+        return autoRevokePermissions;
     }
 
     @Override
@@ -2506,14 +2501,8 @@
     }
 
     @Override
-    public ParsingPackageImpl setDontAutoRevokePermissions(boolean value) {
-        dontAutoRevokePermissions = value;
-        return this;
-    }
-
-    @Override
-    public ParsingPackageImpl setAllowDontAutoRevokePermissions(boolean value) {
-        allowDontAutoRevokePermissions = value;
+    public ParsingPackageImpl setAutoRevokePermissions(int value) {
+        autoRevokePermissions = value;
         return this;
     }
 
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index e700c6a5..687bc23 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -771,11 +771,7 @@
     /** @see ApplicationInfo#PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING */
     boolean isAllowNativeHeapPointerTagging();
 
-    /** @see ApplicationInfo#PRIVATE_FLAG2_DONT_AUTO_REVOKE_PERMISSIONS */
-    boolean isDontAutoRevokePermmissions();
-
-    /** @see ApplicationInfo#PRIVATE_FLAG2_ALLOW_DONT_AUTO_REVOKE_PERMISSIONS */
-    boolean isAllowDontAutoRevokePermmissions();
+    int getAutoRevokePermissions();
 
     boolean hasPreserveLegacyExternalStorage();
 
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 789904a..e90ccdf 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -386,15 +386,14 @@
             return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME);
         }
 
-        TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
+        final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
         try {
-            boolean isCoreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
-
-            ParsingPackage pkg = mCallback.startParsingPackage(pkgName, apkPath, codePath,
-                    manifestArray, isCoreApp);
-
-            ParseResult<ParsingPackage> result = parseBaseApkTags(input, pkg, manifestArray,
-                    res, parser, flags);
+            final boolean isCoreApp =
+                    parser.getAttributeBooleanValue(null, "coreApp", false);
+            final ParsingPackage pkg = mCallback.startParsingPackage(
+                    pkgName, apkPath, codePath, manifestArray, isCoreApp);
+            final ParseResult<ParsingPackage> result =
+                    parseBaseApkTags(input, pkg, manifestArray, res, parser, flags);
             if (result.isError()) {
                 return result;
             }
@@ -620,14 +619,12 @@
             return sharedUserResult;
         }
 
-        pkg.setInstallLocation(anInt(PackageParser.PARSE_DEFAULT_INSTALL_LOCATION,
+        pkg.setInstallLocation(anInteger(PackageParser.PARSE_DEFAULT_INSTALL_LOCATION,
                 R.styleable.AndroidManifest_installLocation, sa))
-                .setTargetSandboxVersion(anInt(PackageParser.PARSE_DEFAULT_TARGET_SANDBOX,
+                .setTargetSandboxVersion(anInteger(PackageParser.PARSE_DEFAULT_TARGET_SANDBOX,
                         R.styleable.AndroidManifest_targetSandboxVersion, sa))
                 /* Set the global "on SD card" flag */
-                .setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0)
-                .setIsolatedSplitLoading(
-                        bool(false, R.styleable.AndroidManifest_isolatedSplits, sa));
+                .setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0);
 
         boolean foundApp = false;
         final int depth = parser.getDepth();
@@ -1829,8 +1826,7 @@
                 .setUseEmbeddedDex(bool(false, R.styleable.AndroidManifestApplication_useEmbeddedDex, sa))
                 .setUsesNonSdkApi(bool(false, R.styleable.AndroidManifestApplication_usesNonSdkApi, sa))
                 .setVmSafeMode(bool(false, R.styleable.AndroidManifestApplication_vmSafeMode, sa))
-                .setDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_requestAutoRevokePermissionsExemption, sa))
-                .setAllowDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_allowAutoRevokePermissionsExemption, sa))
+                .setAutoRevokePermissions(anInt(R.styleable.AndroidManifestApplication_autoRevokePermissions, sa))
                 // targetSdkVersion gated
                 .setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa))
                 .setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa))
@@ -2659,6 +2655,10 @@
         return sa.getInt(attribute, defaultValue);
     }
 
+    private static int anInteger(int defaultValue, @StyleableRes int attribute, TypedArray sa) {
+        return sa.getInteger(attribute, defaultValue);
+    }
+
     private static int anInt(@StyleableRes int attribute, TypedArray sa) {
         return sa.getInt(attribute, 0);
     }
@@ -2689,6 +2689,6 @@
         boolean hasFeature(String feature);
 
         ParsingPackage startParsingPackage(String packageName, String baseCodePath, String codePath,
-                TypedArray manifestArray, boolean isCoreApp);
+                @NonNull TypedArray manifestArray, boolean isCoreApp);
     }
 }
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
index 8bcaf82..e7219ca 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
@@ -26,7 +26,9 @@
 oneway interface IBiometricServiceReceiverInternal {
     // Notify BiometricService that authentication was successful. If user confirmation is required,
     // the auth token must be submitted into KeyStore.
-    void onAuthenticationSucceeded(boolean requireConfirmation, in byte[] token);
+    // TODO(b/151967372): Strength should be changed to authenticatorId
+    void onAuthenticationSucceeded(boolean requireConfirmation, in byte[] token,
+            boolean isStrongBiometric);
     // Notify BiometricService authentication was rejected.
     void onAuthenticationFailed();
     // Notify BiometricService than an error has occured. Forward to the correct receiver depending
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index 3d763e6..425218a 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -106,11 +106,11 @@
 
     public static SoundModel api2aidlSoundModel(SoundTrigger.SoundModel apiModel) {
         SoundModel aidlModel = new SoundModel();
-        aidlModel.type = apiModel.type;
-        aidlModel.uuid = api2aidlUuid(apiModel.uuid);
-        aidlModel.vendorUuid = api2aidlUuid(apiModel.vendorUuid);
-        aidlModel.data = byteArrayToSharedMemory(apiModel.data, "SoundTrigger SoundModel");
-        aidlModel.dataSize = apiModel.data.length;
+        aidlModel.type = apiModel.getType();
+        aidlModel.uuid = api2aidlUuid(apiModel.getUuid());
+        aidlModel.vendorUuid = api2aidlUuid(apiModel.getVendorUuid());
+        aidlModel.data = byteArrayToSharedMemory(apiModel.getData(), "SoundTrigger SoundModel");
+        aidlModel.dataSize = apiModel.getData().length;
         return aidlModel;
     }
 
@@ -122,20 +122,20 @@
             SoundTrigger.KeyphraseSoundModel apiModel) {
         PhraseSoundModel aidlModel = new PhraseSoundModel();
         aidlModel.common = api2aidlSoundModel(apiModel);
-        aidlModel.phrases = new Phrase[apiModel.keyphrases.length];
-        for (int i = 0; i < apiModel.keyphrases.length; ++i) {
-            aidlModel.phrases[i] = api2aidlPhrase(apiModel.keyphrases[i]);
+        aidlModel.phrases = new Phrase[apiModel.getKeyphrases().length];
+        for (int i = 0; i < apiModel.getKeyphrases().length; ++i) {
+            aidlModel.phrases[i] = api2aidlPhrase(apiModel.getKeyphrases()[i]);
         }
         return aidlModel;
     }
 
     public static Phrase api2aidlPhrase(SoundTrigger.Keyphrase apiPhrase) {
         Phrase aidlPhrase = new Phrase();
-        aidlPhrase.id = apiPhrase.id;
-        aidlPhrase.recognitionModes = api2aidlRecognitionModes(apiPhrase.recognitionModes);
-        aidlPhrase.users = Arrays.copyOf(apiPhrase.users, apiPhrase.users.length);
-        aidlPhrase.locale = apiPhrase.locale.toLanguageTag();
-        aidlPhrase.text = apiPhrase.text;
+        aidlPhrase.id = apiPhrase.getId();
+        aidlPhrase.recognitionModes = api2aidlRecognitionModes(apiPhrase.getRecognitionModes());
+        aidlPhrase.users = Arrays.copyOf(apiPhrase.getUsers(), apiPhrase.getUsers().length);
+        aidlPhrase.locale = apiPhrase.getLocale().toLanguageTag();
+        aidlPhrase.text = apiPhrase.getText();
         return aidlPhrase;
     }
 
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index a74871d2..98c4f61 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -104,76 +104,37 @@
 
         /**
          * If set the underlying module supports AEC.
-         * Describes bit field {@link ModuleProperties#audioCapabilities}
+         * Describes bit field {@link ModuleProperties#mAudioCapabilities}
          */
         public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 0x1;
         /**
          * If set, the underlying module supports noise suppression.
-         * Describes bit field {@link ModuleProperties#audioCapabilities}
+         * Describes bit field {@link ModuleProperties#mAudioCapabilities}
          */
         public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 0x2;
 
-        /** Unique module ID provided by the native service */
-        public final int id;
-
-        /** human readable voice detection engine implementor */
+        private final int mId;
         @NonNull
-        public final String implementor;
-
-        /** human readable voice detection engine description */
+        private final String mImplementor;
         @NonNull
-        public final String description;
-
-        /** Unique voice engine Id (changes with each version) */
+        private final String mDescription;
         @NonNull
-        public final UUID uuid;
-
-        /** Voice detection engine version */
-        public final int version;
-
-        /**
-         * String naming the architecture used for running the supported models.
-         * (eg. a platform running models on a DSP could implement this string to convey the DSP
-         * architecture used)
-         */
+        private final UUID mUuid;
+        private final int mVersion;
         @NonNull
-        public final String supportedModelArch;
-
-        /** Maximum number of active sound models */
-        public final int maxSoundModels;
-
-        /** Maximum number of key phrases */
-        public final int maxKeyphrases;
-
-        /** Maximum number of users per key phrase */
-        public final int maxUsers;
-
-        /** Supported recognition modes (bit field, RECOGNITION_MODE_VOICE_TRIGGER ...) */
+        private final String mSupportedModelArch;
+        private final int mMaxSoundModels;
+        private final int mMaxKeyphrases;
+        private final int mMaxUsers;
         @RecognitionModes
-        public final int recognitionModes;
-
-        /** Supports seamless transition to capture mode after recognition */
-        public final boolean supportsCaptureTransition;
-
-        /** Maximum buffering capacity in ms if supportsCaptureTransition() is true */
-        public final int maxBufferMs;
-
-        /** Supports capture by other use cases while detection is active */
-        public final boolean supportsConcurrentCapture;
-
-        /** Rated power consumption when detection is active with TDB silence/sound/speech ratio */
-        public final int powerConsumptionMw;
-
-        /** Returns the trigger (key phrase) capture in the binary data of the
-         * recognition callback event */
-        public final boolean returnsTriggerInEvent;
-
-        /**
-         * Bit field encoding of the AudioCapabilities
-         * supported by the firmware.
-         */
+        private final int mRecognitionModes;
+        private final boolean mSupportsCaptureTransition;
+        private final int mMaxBufferMillis;
+        private final boolean mSupportsConcurrentCapture;
+        private final int mPowerConsumptionMw;
+        private final boolean mReturnsTriggerInEvent;
         @AudioCapabilities
-        public final int audioCapabilities;
+        private final int mAudioCapabilities;
 
         ModuleProperties(int id, @NonNull String implementor, @NonNull String description,
                 @NonNull String uuid, int version, @NonNull String supportedModelArch,
@@ -181,22 +142,116 @@
                 @RecognitionModes int recognitionModes, boolean supportsCaptureTransition,
                 int maxBufferMs, boolean supportsConcurrentCapture, int powerConsumptionMw,
                 boolean returnsTriggerInEvent, int audioCapabilities) {
-            this.id = id;
-            this.implementor = requireNonNull(implementor);
-            this.description = requireNonNull(description);
-            this.uuid = UUID.fromString(requireNonNull(uuid));
-            this.version = version;
-            this.supportedModelArch = requireNonNull(supportedModelArch);
-            this.maxSoundModels = maxSoundModels;
-            this.maxKeyphrases = maxKeyphrases;
-            this.maxUsers = maxUsers;
-            this.recognitionModes = recognitionModes;
-            this.supportsCaptureTransition = supportsCaptureTransition;
-            this.maxBufferMs = maxBufferMs;
-            this.supportsConcurrentCapture = supportsConcurrentCapture;
-            this.powerConsumptionMw = powerConsumptionMw;
-            this.returnsTriggerInEvent = returnsTriggerInEvent;
-            this.audioCapabilities = audioCapabilities;
+            this.mId = id;
+            this.mImplementor = requireNonNull(implementor);
+            this.mDescription = requireNonNull(description);
+            this.mUuid = UUID.fromString(requireNonNull(uuid));
+            this.mVersion = version;
+            this.mSupportedModelArch = requireNonNull(supportedModelArch);
+            this.mMaxSoundModels = maxSoundModels;
+            this.mMaxKeyphrases = maxKeyphrases;
+            this.mMaxUsers = maxUsers;
+            this.mRecognitionModes = recognitionModes;
+            this.mSupportsCaptureTransition = supportsCaptureTransition;
+            this.mMaxBufferMillis = maxBufferMs;
+            this.mSupportsConcurrentCapture = supportsConcurrentCapture;
+            this.mPowerConsumptionMw = powerConsumptionMw;
+            this.mReturnsTriggerInEvent = returnsTriggerInEvent;
+            this.mAudioCapabilities = audioCapabilities;
+        }
+
+        /** Unique module ID provided by the native service */
+        public int getId() {
+            return mId;
+        }
+
+        /** human readable voice detection engine implementor */
+        @NonNull
+        public String getImplementor() {
+            return mImplementor;
+        }
+
+        /** human readable voice detection engine description */
+        @NonNull
+        public String getDescription() {
+            return mDescription;
+        }
+
+        /** Unique voice engine Id (changes with each version) */
+        @NonNull
+        public UUID getUuid() {
+            return mUuid;
+        }
+
+        /** Voice detection engine version */
+        public int getVersion() {
+            return mVersion;
+        }
+
+        /**
+         * String naming the architecture used for running the supported models.
+         * (eg. a platform running models on a DSP could implement this string to convey the DSP
+         * architecture used)
+         */
+        @NonNull
+        public String getSupportedModelArch() {
+            return mSupportedModelArch;
+        }
+
+        /** Maximum number of active sound models */
+        public int getMaxSoundModels() {
+            return mMaxSoundModels;
+        }
+
+        /** Maximum number of key phrases */
+        public int getMaxKeyphrases() {
+            return mMaxKeyphrases;
+        }
+
+        /** Maximum number of users per key phrase */
+        public int getMaxUsers() {
+            return mMaxUsers;
+        }
+
+        /** Supported recognition modes (bit field, RECOGNITION_MODE_VOICE_TRIGGER ...) */
+        @RecognitionModes
+        public int getRecognitionModes() {
+            return mRecognitionModes;
+        }
+
+        /** Supports seamless transition to capture mode after recognition */
+        public boolean isCaptureTransitionSupported() {
+            return mSupportsCaptureTransition;
+        }
+
+        /** Maximum buffering capacity in ms if supportsCaptureTransition() is true */
+        public int getMaxBufferMillis() {
+            return mMaxBufferMillis;
+        }
+
+        /** Supports capture by other use cases while detection is active */
+        public boolean isConcurrentCaptureSupported() {
+            return mSupportsConcurrentCapture;
+        }
+
+        /** Rated power consumption when detection is active with TDB silence/sound/speech ratio */
+        public int getPowerConsumptionMw() {
+            return mPowerConsumptionMw;
+        }
+
+        /** Returns the trigger (key phrase) capture in the binary data of the
+         * recognition callback event */
+        public boolean isTriggerReturnedInEvent() {
+            return mReturnsTriggerInEvent;
+        }
+
+        /**
+         * Bit field encoding of the AudioCapabilities
+         * supported by the firmware.
+         */
+        @AudioCapabilities
+        public int getAudioCapabilities() {
+            return mAudioCapabilities;
         }
 
         public static final @android.annotation.NonNull Parcelable.Creator<ModuleProperties> CREATOR
@@ -235,22 +290,22 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(id);
-            dest.writeString(implementor);
-            dest.writeString(description);
-            dest.writeString(uuid.toString());
-            dest.writeInt(version);
-            dest.writeString(supportedModelArch);
-            dest.writeInt(maxSoundModels);
-            dest.writeInt(maxKeyphrases);
-            dest.writeInt(maxUsers);
-            dest.writeInt(recognitionModes);
-            dest.writeByte((byte) (supportsCaptureTransition ? 1 : 0));
-            dest.writeInt(maxBufferMs);
-            dest.writeByte((byte) (supportsConcurrentCapture ? 1 : 0));
-            dest.writeInt(powerConsumptionMw);
-            dest.writeByte((byte) (returnsTriggerInEvent ? 1 : 0));
-            dest.writeInt(audioCapabilities);
+            dest.writeInt(getId());
+            dest.writeString(getImplementor());
+            dest.writeString(getDescription());
+            dest.writeString(getUuid().toString());
+            dest.writeInt(getVersion());
+            dest.writeString(getSupportedModelArch());
+            dest.writeInt(getMaxSoundModels());
+            dest.writeInt(getMaxKeyphrases());
+            dest.writeInt(getMaxUsers());
+            dest.writeInt(getRecognitionModes());
+            dest.writeByte((byte) (isCaptureTransitionSupported() ? 1 : 0));
+            dest.writeInt(getMaxBufferMillis());
+            dest.writeByte((byte) (isConcurrentCaptureSupported() ? 1 : 0));
+            dest.writeInt(getPowerConsumptionMw());
+            dest.writeByte((byte) (isTriggerReturnedInEvent() ? 1 : 0));
+            dest.writeInt(getAudioCapabilities());
         }
 
         @Override
@@ -260,16 +315,17 @@
 
         @Override
         public String toString() {
-            return "ModuleProperties [id=" + id + ", implementor=" + implementor + ", description="
-                    + description + ", uuid=" + uuid + ", version=" + version
-                    + " , supportedModelArch=" + supportedModelArch + ", maxSoundModels="
-                    + maxSoundModels + ", maxKeyphrases=" + maxKeyphrases + ", maxUsers="
-                    + maxUsers + ", recognitionModes=" + recognitionModes
-                    + ", supportsCaptureTransition=" + supportsCaptureTransition + ", maxBufferMs="
-                    + maxBufferMs + ", supportsConcurrentCapture=" + supportsConcurrentCapture
-                    + ", powerConsumptionMw=" + powerConsumptionMw
-                    + ", returnsTriggerInEvent=" + returnsTriggerInEvent
-                    + ", audioCapabilities=" + audioCapabilities + "]";
+            return "ModuleProperties [id=" + getId() + ", implementor=" + getImplementor()
+                    + ", description=" + getDescription() + ", uuid=" + getUuid()
+                    + ", version=" + getVersion() + " , supportedModelArch="
+                    + getSupportedModelArch() + ", maxSoundModels=" + getMaxSoundModels()
+                    + ", maxKeyphrases=" + getMaxKeyphrases() + ", maxUsers=" + getMaxUsers()
+                    + ", recognitionModes=" + getRecognitionModes() + ", supportsCaptureTransition="
+                    + isCaptureTransitionSupported() + ", maxBufferMs=" + getMaxBufferMillis()
+                    + ", supportsConcurrentCapture=" + isConcurrentCaptureSupported()
+                    + ", powerConsumptionMw=" + getPowerConsumptionMw()
+                    + ", returnsTriggerInEvent=" + isTriggerReturnedInEvent()
+                    + ", audioCapabilities=" + getAudioCapabilities() + "]";
         }
     }
 
@@ -305,44 +361,64 @@
          */
         public static final int TYPE_GENERIC_SOUND = 1;
 
-        /** Unique sound model identifier */
         @NonNull
-        public final UUID uuid;
-
-        /** Sound model type (e.g. TYPE_KEYPHRASE); */
+        private final UUID mUuid;
         @SoundModelType
-        public final int type;
-
-        /** Unique sound model vendor identifier */
+        private final int mType;
         @NonNull
-        public final UUID vendorUuid;
-
-        /** vendor specific version number of the model */
-        public final int version;
-
-        /** Opaque data. For use by vendor implementation and enrollment application */
+        private final UUID mVendorUuid;
+        private final int mVersion;
         @NonNull
-        public final byte[] data;
+        private final byte[] mData;
 
         /** @hide */
         public SoundModel(@NonNull UUID uuid, @Nullable UUID vendorUuid, @SoundModelType int type,
                 @Nullable byte[] data, int version) {
-            this.uuid = requireNonNull(uuid);
-            this.vendorUuid = vendorUuid != null ? vendorUuid : new UUID(0, 0);
-            this.type = type;
-            this.version = version;
-            this.data = data != null ? data : new byte[0];
+            this.mUuid = requireNonNull(uuid);
+            this.mVendorUuid = vendorUuid != null ? vendorUuid : new UUID(0, 0);
+            this.mType = type;
+            this.mVersion = version;
+            this.mData = data != null ? data : new byte[0];
+        }
+
+        /** Unique sound model identifier */
+        @NonNull
+        public UUID getUuid() {
+            return mUuid;
+        }
+
+        /** Sound model type (e.g. TYPE_KEYPHRASE); */
+        @SoundModelType
+        public int getType() {
+            return mType;
+        }
+
+        /** Unique sound model vendor identifier */
+        @NonNull
+        public UUID getVendorUuid() {
+            return mVendorUuid;
+        }
+
+        /** vendor specific version number of the model */
+        public int getVersion() {
+            return mVersion;
+        }
+
+        /** Opaque data. For use by vendor implementation and enrollment application */
+        @NonNull
+        public byte[] getData() {
+            return mData;
         }
 
         @Override
         public int hashCode() {
             final int prime = 31;
             int result = 1;
-            result = prime * result + version;
-            result = prime * result + Arrays.hashCode(data);
-            result = prime * result + type;
-            result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
-            result = prime * result + ((vendorUuid == null) ? 0 : vendorUuid.hashCode());
+            result = prime * result + getVersion();
+            result = prime * result + Arrays.hashCode(getData());
+            result = prime * result + getType();
+            result = prime * result + ((getUuid() == null) ? 0 : getUuid().hashCode());
+            result = prime * result + ((getVendorUuid() == null) ? 0 : getVendorUuid().hashCode());
             return result;
         }
 
@@ -358,27 +434,27 @@
                 return false;
             }
             SoundModel other = (SoundModel) obj;
-            if (type != other.type) {
+            if (getType() != other.getType()) {
                 return false;
             }
-            if (uuid == null) {
-                if (other.uuid != null) {
+            if (getUuid() == null) {
+                if (other.getUuid() != null) {
                     return false;
                 }
-            } else if (!uuid.equals(other.uuid)) {
+            } else if (!getUuid().equals(other.getUuid())) {
                 return false;
             }
-            if (vendorUuid == null) {
-                if (other.vendorUuid != null) {
+            if (getVendorUuid() == null) {
+                if (other.getVendorUuid() != null) {
                     return false;
                 }
-            } else if (!vendorUuid.equals(other.vendorUuid)) {
+            } else if (!getVendorUuid().equals(other.getVendorUuid())) {
                 return false;
             }
-            if (!Arrays.equals(data, other.data)) {
+            if (!Arrays.equals(getData(), other.getData())) {
                 return false;
             }
-            if (version != other.version) {
+            if (getVersion() != other.getVersion()) {
                 return false;
             }
             return true;
@@ -390,34 +466,16 @@
      * {@link KeyphraseSoundModel}
      */
     public static final class Keyphrase implements Parcelable {
-        /** Unique identifier for this keyphrase */
-        public final int id;
 
-        /**
-         * Recognition modes supported for this key phrase in the model
-         *
-         * @see #RECOGNITION_MODE_VOICE_TRIGGER
-         * @see #RECOGNITION_MODE_USER_IDENTIFICATION
-         * @see #RECOGNITION_MODE_USER_AUTHENTICATION
-         * @see #RECOGNITION_MODE_GENERIC
-         */
+        private final int mId;
         @RecognitionModes
-        public final int recognitionModes;
-
-        /** Locale of the keyphrase. */
+        private final int mRecognitionModes;
         @NonNull
-        public final Locale locale;
-
-        /** Key phrase text */
+        private final Locale mLocale;
         @NonNull
-        public final String text;
-
-        /**
-         * Users this key phrase has been trained for. countains sound trigger specific user IDs
-         * derived from system user IDs {@link android.os.UserHandle#getIdentifier()}.
-         */
+        private final String mText;
         @NonNull
-        public final int[] users;
+        private final int[] mUsers;
 
         /**
          * Constructor for Keyphrase describes a key phrase that can be detected by a
@@ -432,11 +490,50 @@
          */
         public Keyphrase(int id, @RecognitionModes int recognitionModes, @NonNull Locale locale,
                 @NonNull String text, @Nullable int[] users) {
-            this.id = id;
-            this.recognitionModes = recognitionModes;
-            this.locale = requireNonNull(locale);
-            this.text = requireNonNull(text);
-            this.users = users != null ? users : new int[0];
+            this.mId = id;
+            this.mRecognitionModes = recognitionModes;
+            this.mLocale = requireNonNull(locale);
+            this.mText = requireNonNull(text);
+            this.mUsers = users != null ? users : new int[0];
+        }
+
+        /** Unique identifier for this keyphrase */
+        public int getId() {
+            return mId;
+        }
+
+        /**
+         * Recognition modes supported for this key phrase in the model
+         *
+         * @see #RECOGNITION_MODE_VOICE_TRIGGER
+         * @see #RECOGNITION_MODE_USER_IDENTIFICATION
+         * @see #RECOGNITION_MODE_USER_AUTHENTICATION
+         * @see #RECOGNITION_MODE_GENERIC
+         */
+        @RecognitionModes
+        public int getRecognitionModes() {
+            return mRecognitionModes;
+        }
+
+        /** Locale of the keyphrase. */
+        @NonNull
+        public Locale getLocale() {
+            return mLocale;
+        }
+
+        /** Key phrase text */
+        @NonNull
+        public String getText() {
+            return mText;
+        }
+
+        /**
+         * Users this key phrase has been trained for. countains sound trigger specific user IDs
+         * derived from system user IDs {@link android.os.UserHandle#getIdentifier()}.
+         */
+        @NonNull
+        public int[] getUsers() {
+            return mUsers;
         }
 
         public static final @NonNull Parcelable.Creator<Keyphrase> CREATOR =
@@ -472,13 +569,13 @@
 
         @Override
         public void writeToParcel(@NonNull Parcel dest, int flags) {
-            dest.writeInt(id);
-            dest.writeInt(recognitionModes);
-            dest.writeString(locale.toLanguageTag());
-            dest.writeString(text);
-            if (users != null) {
-                dest.writeInt(users.length);
-                dest.writeIntArray(users);
+            dest.writeInt(getId());
+            dest.writeInt(getRecognitionModes());
+            dest.writeString(getLocale().toLanguageTag());
+            dest.writeString(getText());
+            if (getUsers() != null) {
+                dest.writeInt(getUsers().length);
+                dest.writeIntArray(getUsers());
             } else {
                 dest.writeInt(-1);
             }
@@ -494,11 +591,11 @@
         public int hashCode() {
             final int prime = 31;
             int result = 1;
-            result = prime * result + ((text == null) ? 0 : text.hashCode());
-            result = prime * result + id;
-            result = prime * result + ((locale == null) ? 0 : locale.hashCode());
-            result = prime * result + recognitionModes;
-            result = prime * result + Arrays.hashCode(users);
+            result = prime * result + ((getText() == null) ? 0 : getText().hashCode());
+            result = prime * result + getId();
+            result = prime * result + ((getLocale() == null) ? 0 : getLocale().hashCode());
+            result = prime * result + getRecognitionModes();
+            result = prime * result + Arrays.hashCode(getUsers());
             return result;
         }
 
@@ -514,27 +611,27 @@
                 return false;
             }
             Keyphrase other = (Keyphrase) obj;
-            if (text == null) {
-                if (other.text != null) {
+            if (getText() == null) {
+                if (other.getText() != null) {
                     return false;
                 }
-            } else if (!text.equals(other.text)) {
+            } else if (!getText().equals(other.getText())) {
                 return false;
             }
-            if (id != other.id) {
+            if (getId() != other.getId()) {
                 return false;
             }
-            if (locale == null) {
-                if (other.locale != null) {
+            if (getLocale() == null) {
+                if (other.getLocale() != null) {
                     return false;
                 }
-            } else if (!locale.equals(other.locale)) {
+            } else if (!getLocale().equals(other.getLocale())) {
                 return false;
             }
-            if (recognitionModes != other.recognitionModes) {
+            if (getRecognitionModes() != other.getRecognitionModes()) {
                 return false;
             }
-            if (!Arrays.equals(users, other.users)) {
+            if (!Arrays.equals(getUsers(), other.getUsers())) {
                 return false;
             }
             return true;
@@ -542,9 +639,9 @@
 
         @Override
         public String toString() {
-            return "Keyphrase [id=" + id + ", recognitionModes=" + recognitionModes
-                    + ", locale=" + locale.toLanguageTag() + ", text=" + text
-                    + ", users=" + Arrays.toString(users) + "]";
+            return "Keyphrase [id=" + getId() + ", recognitionModes=" + getRecognitionModes()
+                    + ", locale=" + getLocale().toLanguageTag() + ", text=" + getText()
+                    + ", users=" + Arrays.toString(getUsers()) + "]";
         }
     }
 
@@ -554,15 +651,15 @@
      * and the list of corresponding {@link Keyphrase} descriptors.
      */
     public static final class KeyphraseSoundModel extends SoundModel implements Parcelable {
-        /** Key phrases in this sound model */
+
         @NonNull
-        public final Keyphrase[] keyphrases; // keyword phrases in model
+        private final Keyphrase[] mKeyphrases;
 
         public KeyphraseSoundModel(
                 @NonNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data,
                 @Nullable Keyphrase[] keyphrases, int version) {
             super(uuid, vendorUuid, TYPE_KEYPHRASE, data, version);
-            this.keyphrases = keyphrases != null ? keyphrases : new Keyphrase[0];
+            this.mKeyphrases = keyphrases != null ? keyphrases : new Keyphrase[0];
         }
 
         public KeyphraseSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
@@ -570,6 +667,12 @@
             this(uuid, vendorUuid, data, keyphrases, -1);
         }
 
+        /** Key phrases in this sound model */
+        @NonNull
+        public Keyphrase[] getKeyphrases() {
+            return mKeyphrases;
+        }
+
         public static final @NonNull Parcelable.Creator<KeyphraseSoundModel> CREATOR =
                 new Parcelable.Creator<KeyphraseSoundModel>() {
             @NonNull
@@ -608,32 +711,32 @@
 
         @Override
         public void writeToParcel(@NonNull Parcel dest, int flags) {
-            dest.writeString(uuid.toString());
-            if (vendorUuid == null) {
+            dest.writeString(getUuid().toString());
+            if (getVendorUuid() == null) {
                 dest.writeInt(-1);
             } else {
-                dest.writeInt(vendorUuid.toString().length());
-                dest.writeString(vendorUuid.toString());
+                dest.writeInt(getVendorUuid().toString().length());
+                dest.writeString(getVendorUuid().toString());
             }
-            dest.writeInt(version);
-            dest.writeBlob(data);
-            dest.writeTypedArray(keyphrases, flags);
+            dest.writeInt(getVersion());
+            dest.writeBlob(getData());
+            dest.writeTypedArray(getKeyphrases(), flags);
         }
 
         @Override
         public String toString() {
-            return "KeyphraseSoundModel [keyphrases=" + Arrays.toString(keyphrases)
-                    + ", uuid=" + uuid + ", vendorUuid=" + vendorUuid
-                    + ", type=" + type
-                    + ", data=" + (data == null ? 0 : data.length)
-                    + ", version=" + version + "]";
+            return "KeyphraseSoundModel [keyphrases=" + Arrays.toString(getKeyphrases())
+                    + ", uuid=" + getUuid() + ", vendorUuid=" + getVendorUuid()
+                    + ", type=" + getType()
+                    + ", data=" + (getData() == null ? 0 : getData().length)
+                    + ", version=" + getVersion() + "]";
         }
 
         @Override
         public int hashCode() {
             final int prime = 31;
             int result = super.hashCode();
-            result = prime * result + Arrays.hashCode(keyphrases);
+            result = prime * result + Arrays.hashCode(getKeyphrases());
             return result;
         }
 
@@ -649,7 +752,7 @@
                 return false;
             }
             KeyphraseSoundModel other = (KeyphraseSoundModel) obj;
-            if (!Arrays.equals(keyphrases, other.keyphrases)) {
+            if (!Arrays.equals(getKeyphrases(), other.getKeyphrases())) {
                 return false;
             }
             return true;
@@ -706,23 +809,23 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeString(uuid.toString());
-            if (vendorUuid == null) {
+            dest.writeString(getUuid().toString());
+            if (getVendorUuid() == null) {
                 dest.writeInt(-1);
             } else {
-                dest.writeInt(vendorUuid.toString().length());
-                dest.writeString(vendorUuid.toString());
+                dest.writeInt(getVendorUuid().toString().length());
+                dest.writeString(getVendorUuid().toString());
             }
-            dest.writeBlob(data);
-            dest.writeInt(version);
+            dest.writeBlob(getData());
+            dest.writeInt(getVersion());
         }
 
         @Override
         public String toString() {
-            return "GenericSoundModel [uuid=" + uuid + ", vendorUuid=" + vendorUuid
-                    + ", type=" + type
-                    + ", data=" + (data == null ? 0 : data.length)
-                    + ", version=" + version + "]";
+            return "GenericSoundModel [uuid=" + getUuid() + ", vendorUuid=" + getVendorUuid()
+                    + ", type=" + getType()
+                    + ", data=" + (getData() == null ? 0 : getData().length)
+                    + ", version=" + getVersion() + "]";
         }
     }
 
@@ -1825,7 +1928,7 @@
     /**
      * Get an interface on a hardware module to control sound models and recognition on
      * this module.
-     * @param moduleId Sound module system identifier {@link ModuleProperties#id}. mandatory.
+     * @param moduleId Sound module system identifier {@link ModuleProperties#mId}. mandatory.
      * @param listener {@link StatusListener} interface. Mandatory.
      * @param handler the Handler that will receive the callabcks. Can be null if default handler
      *                is OK.
diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java
index c31cb4e..9b3e8c9 100644
--- a/core/java/android/inputmethodservice/InlineSuggestionSession.java
+++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java
@@ -166,9 +166,14 @@
             }
             return;
         }
-
+        // The IME doesn't have information about the virtual view id for the child views in the
+        // web view, so we are only comparing the parent view id here. This means that for cases
+        // where there are two input fields in the web view, they will have the same view id
+        // (although different virtual child id), and we will not be able to distinguish them.
+        final AutofillId imeClientFieldId = mClientAutofillIdSupplier.get();
         if (!mComponentName.getPackageName().equals(mClientPackageNameSupplier.get())
-                || !fieldId.equalsIgnoreSession(mClientAutofillIdSupplier.get())) {
+                || imeClientFieldId == null
+                || fieldId.getViewId() != imeClientFieldId.getViewId()) {
             if (DEBUG) {
                 Log.d(TAG,
                         "handleOnInlineSuggestionsResponse() called on the wrong package/field "
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 7c34ddc..782fff2 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -60,7 +60,6 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
-import android.util.Size;
 import android.view.Gravity;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
@@ -1249,6 +1248,7 @@
                 WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false);
         mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars());
         mWindow.getWindow().getAttributes().setFitInsetsSides(Side.all() & ~Side.BOTTOM);
+        mWindow.getWindow().getAttributes().setFitInsetsIgnoringVisibility(true);
 
         // IME layout should always be inset by navigation bar, no matter its current visibility,
         // unless automotive requests it, since automotive may hide the navigation bar.
@@ -1479,8 +1479,8 @@
      */
     public int getMaxWidth() {
         final WindowManager windowManager = getSystemService(WindowManager.class);
-        final Size windowSize = windowManager.getCurrentWindowMetrics().getSize();
-        return windowSize.getWidth();
+        final Rect windowBounds = windowManager.getCurrentWindowMetrics().getBounds();
+        return windowBounds.width();
     }
     
     /**
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 2a323e5..7332ede 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -705,6 +705,36 @@
     @Deprecated
     public static final int TYPE_TEST = 18; // TODO: Remove this once NetworkTypes are unused.
 
+    /**
+     * @deprecated Use {@link NetworkCapabilities} instead.
+     * @hide
+     */
+    @Deprecated
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "TYPE_" }, value = {
+                TYPE_NONE,
+                TYPE_MOBILE,
+                TYPE_WIFI,
+                TYPE_MOBILE_MMS,
+                TYPE_MOBILE_SUPL,
+                TYPE_MOBILE_DUN,
+                TYPE_MOBILE_HIPRI,
+                TYPE_WIMAX,
+                TYPE_BLUETOOTH,
+                TYPE_DUMMY,
+                TYPE_ETHERNET,
+                TYPE_MOBILE_FOTA,
+                TYPE_MOBILE_IMS,
+                TYPE_MOBILE_CBS,
+                TYPE_WIFI_P2P,
+                TYPE_MOBILE_IA,
+                TYPE_MOBILE_EMERGENCY,
+                TYPE_PROXY,
+                TYPE_VPN,
+                TYPE_TEST
+    })
+    public @interface LegacyNetworkType {}
+
     // Deprecated constants for return values of startUsingNetworkFeature. They used to live
     // in com.android.internal.telephony.PhoneConstants until they were made inaccessible.
     private static final int DEPRECATED_PHONE_CONSTANT_APN_ALREADY_ACTIVE = 0;
diff --git a/core/java/android/net/InvalidPacketException.java b/core/java/android/net/InvalidPacketException.java
index b3b0f11..1873d77 100644
--- a/core/java/android/net/InvalidPacketException.java
+++ b/core/java/android/net/InvalidPacketException.java
@@ -27,7 +27,7 @@
  * @hide
  */
 @SystemApi
-public class InvalidPacketException extends Exception {
+public final class InvalidPacketException extends Exception {
     private final int mError;
 
     // Must match SocketKeepalive#ERROR_INVALID_IP_ADDRESS.
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 5c754a1..8119df9 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -32,18 +34,52 @@
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * A Utility class for handling for communicating between bearer-specific
+ * A utility class for handling for communicating between bearer-specific
  * code and ConnectivityService.
  *
+ * An agent manages the life cycle of a network. A network starts its
+ * life cycle when {@link register} is called on NetworkAgent. The network
+ * is then connecting. When full L3 connectivity has been established,
+ * the agent shoud call {@link setConnected} to inform the system that
+ * this network is ready to use. When the network disconnects its life
+ * ends and the agent should call {@link unregister}, at which point the
+ * system will clean up and free resources.
+ * Any reconnection becomes a new logical network, so after a network
+ * is disconnected the agent cannot be used any more. Network providers
+ * should create a new NetworkAgent instance to handle new connections.
+ *
  * A bearer may have more than one NetworkAgent if it can simultaneously
  * support separate networks (IMS / Internet / MMS Apns on cellular, or
  * perhaps connections with different SSID or P2P for Wi-Fi).
  *
+ * This class supports methods to start and stop sending keepalive packets.
+ * Keepalive packets are typically sent at periodic intervals over a network
+ * with NAT when there is no other traffic to avoid the network forcefully
+ * closing the connection. NetworkAgents that manage technologies that
+ * have hardware support for keepalive should implement the related
+ * methods to save battery life. NetworkAgent that cannot get support
+ * without waking up the CPU should not, as this would be prohibitive in
+ * terms of battery - these agents should simply not override the related
+ * methods, which results in the implementation returning
+ * {@link SocketKeepalive.ERROR_UNSUPPORTED} as appropriate.
+ *
+ * Keepalive packets need to be sent at relatively frequent intervals
+ * (a few seconds to a few minutes). As the contents of keepalive packets
+ * depend on the current network status, hardware needs to be configured
+ * to send them and has a limited amount of memory to do so. The HAL
+ * formalizes this as slots that an implementation can configure to send
+ * the correct packets. Devices typically have a small number of slots
+ * per radio technology, and the specific number of slots for each
+ * technology is specified in configuration files.
+ * {@see SocketKeepalive} for details.
+ *
  * @hide
  */
 @SystemApi
@@ -65,7 +101,7 @@
     private final String LOG_TAG;
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
-    private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>();
+    private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>();
     private volatile long mLastBwRefreshTime = 0;
     private static final long BW_REFRESH_MIN_WIN_MS = 500;
     private boolean mBandwidthUpdateScheduled = false;
@@ -74,6 +110,8 @@
     // into the internal API of ConnectivityService.
     @NonNull
     private NetworkInfo mNetworkInfo;
+    @NonNull
+    private final Object mRegisterLock = new Object();
 
     /**
      * The ID of the {@link NetworkProvider} that created this object, or
@@ -158,6 +196,14 @@
      */
     public static final int VALIDATION_STATUS_NOT_VALID = 2;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "VALIDATION_STATUS_" }, value = {
+            VALIDATION_STATUS_VALID,
+            VALIDATION_STATUS_NOT_VALID
+    })
+    public @interface ValidationStatus {}
+
     // TODO: remove.
     /** @hide */
     public static final int VALID_NETWORK = 1;
@@ -202,7 +248,7 @@
      * Sent by ConnectivityService to the NetworkAgent to request that the specified packet be sent
      * periodically on the given interval.
      *
-     *   arg1 = the slot number of the keepalive to start
+     *   arg1 = the hardware slot number of the keepalive to start
      *   arg2 = interval in seconds
      *   obj = KeepalivePacketData object describing the data to be sent
      *
@@ -214,7 +260,7 @@
     /**
      * Requests that the specified keepalive packet be stopped.
      *
-     * arg1 = slot number of the keepalive to stop.
+     * arg1 = hardware slot number of the keepalive to stop.
      *
      * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
      * @hide
@@ -229,7 +275,7 @@
      * This is also sent by KeepaliveTracker to the app's {@link SocketKeepalive},
      * so that the app's {@link SocketKeepalive.Callback} methods can be called.
      *
-     * arg1 = slot number of the keepalive
+     * arg1 = hardware slot number of the keepalive
      * arg2 = error code
      * @hide
      */
@@ -259,7 +305,7 @@
      * remote site will send ACK packets in response to the keepalive packets, the firmware also
      * needs to be configured to properly filter the ACKs to prevent the system from waking up.
      * This does not happen with UDP, so this message is TCP-specific.
-     * arg1 = slot number of the keepalive to filter for.
+     * arg1 = hardware slot number of the keepalive to filter for.
      * obj = the keepalive packet to send repeatedly.
      * @hide
      */
@@ -268,7 +314,7 @@
     /**
      * Sent by the KeepaliveTracker to NetworkAgent to remove a packet filter. See
      * {@link #CMD_ADD_KEEPALIVE_PACKET_FILTER}.
-     * arg1 = slot number of the keepalive packet filter to remove.
+     * arg1 = hardware slot number of the keepalive packet filter to remove.
      * @hide
      */
     public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17;
@@ -441,7 +487,15 @@
                                 + (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ")
                                 + redirectUrl);
                     }
-                    onValidationStatus(msg.arg1 /* status */, redirectUrl);
+                    Uri uri = null;
+                    try {
+                        if (null != redirectUrl) {
+                            uri = Uri.parse(redirectUrl);
+                        }
+                    } catch (Exception e) {
+                        Log.wtf(LOG_TAG, "Surprising URI : " + redirectUrl, e);
+                    }
+                    onValidationStatus(msg.arg1 /* status */, uri);
                     break;
                 }
                 case CMD_SAVE_ACCEPT_UNVALIDATED: {
@@ -489,19 +543,29 @@
 
     /**
      * Register this network agent with ConnectivityService.
+     *
+     * This method can only be called once per network agent.
+     *
      * @return the Network associated with this network agent (which can also be obtained later
      *         by calling getNetwork() on this agent).
+     * @throws IllegalStateException thrown by the system server if this network agent is
+     *         already registered.
      */
     @NonNull
     public Network register() {
         if (VDBG) log("Registering NetworkAgent");
         final ConnectivityManager cm = (ConnectivityManager) mInitialConfiguration.context
                 .getSystemService(Context.CONNECTIVITY_SERVICE);
-        mNetwork = cm.registerNetworkAgent(new Messenger(mHandler),
-                new NetworkInfo(mInitialConfiguration.info),
-                mInitialConfiguration.properties, mInitialConfiguration.capabilities,
-                mInitialConfiguration.score, mInitialConfiguration.config, providerId);
-        mInitialConfiguration = null; // All this memory can now be GC'd
+        synchronized (mRegisterLock) {
+            if (mNetwork != null) {
+                throw new IllegalStateException("Agent already registered");
+            }
+            mNetwork = cm.registerNetworkAgent(new Messenger(mHandler),
+                    new NetworkInfo(mInitialConfiguration.info),
+                    mInitialConfiguration.properties, mInitialConfiguration.capabilities,
+                    mInitialConfiguration.score, mInitialConfiguration.config, providerId);
+            mInitialConfiguration = null; // All this memory can now be GC'd
+        }
         return mNetwork;
     }
 
@@ -544,13 +608,14 @@
      * Must be called by the agent when the network's {@link LinkProperties} change.
      * @param linkProperties the new LinkProperties.
      */
-    public void sendLinkProperties(@NonNull LinkProperties linkProperties) {
+    public final void sendLinkProperties(@NonNull LinkProperties linkProperties) {
         Objects.requireNonNull(linkProperties);
         queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
     }
 
     /**
      * Inform ConnectivityService that this agent has now connected.
+     * Call {@link #unregister} to disconnect.
      */
     public void setConnected() {
         if (mIsLegacy) {
@@ -569,8 +634,7 @@
      */
     public void unregister() {
         if (mIsLegacy) {
-            throw new UnsupportedOperationException(
-                    "Legacy agents can't call unregister.");
+            throw new UnsupportedOperationException("Legacy agents can't call unregister.");
         }
         mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
         queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
@@ -626,7 +690,7 @@
      * @hide TODO: expose something better.
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    public void sendNetworkInfo(NetworkInfo networkInfo) {
+    public final void sendNetworkInfo(NetworkInfo networkInfo) {
         if (!mIsLegacy) {
             throw new UnsupportedOperationException("Only legacy agents can call sendNetworkInfo.");
         }
@@ -637,7 +701,7 @@
      * Must be called by the agent when the network's {@link NetworkCapabilities} change.
      * @param networkCapabilities the new NetworkCapabilities.
      */
-    public void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
+    public final void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
         Objects.requireNonNull(networkCapabilities);
         mBandwidthUpdatePending.set(false);
         mLastBwRefreshTime = System.currentTimeMillis();
@@ -647,9 +711,10 @@
 
     /**
      * Must be called by the agent to update the score of this network.
-     * @param score the new score.
+     *
+     * @param score the new score, between 0 and 99.
      */
-    public void sendNetworkScore(int score) {
+    public final void sendNetworkScore(@IntRange(from = 0, to = 99) int score) {
         if (score < 0) {
             throw new IllegalArgumentException("Score must be >= 0");
         }
@@ -737,11 +802,11 @@
      * subsequent attempts to validate connectivity that fail.
      *
      * @param status one of {@code VALIDATION_STATUS_VALID} or {@code VALIDATION_STATUS_NOT_VALID}.
-     * @param redirectUrl If Internet connectivity is being redirected (e.g., on a captive portal),
+     * @param redirectUri If Internet connectivity is being redirected (e.g., on a captive portal),
      *        this is the destination the probes are being redirected to, otherwise {@code null}.
      */
-    public void onValidationStatus(int status, @Nullable String redirectUrl) {
-        networkStatus(status, redirectUrl);
+    public void onValidationStatus(@ValidationStatus int status, @Nullable Uri redirectUri) {
+        networkStatus(status, redirectUri.toString());
     }
     /** @hide TODO delete once subclasses have moved to onValidationStatus */
     protected void networkStatus(int status, String redirectUrl) {
@@ -770,7 +835,12 @@
      * @param intervalSeconds the interval between packets
      * @param packet the packet to send.
      */
-    public void onStartSocketKeepalive(int slot, int intervalSeconds,
+    // seconds is from SocketKeepalive.MIN_INTERVAL_SEC to MAX_INTERVAL_SEC, but these should
+    // not be exposed as constants because they may change in the future (API guideline 4.8)
+    // and should have getters if exposed at all. Getters can't be used in the annotation,
+    // so the values unfortunately need to be copied.
+    public void onStartSocketKeepalive(int slot,
+            @IntRange(from = 10, to = 3600) int intervalSeconds,
             @NonNull KeepalivePacketData packet) {
         Message msg = mHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, slot, intervalSeconds,
                 packet);
@@ -801,9 +871,11 @@
      * Must be called by the agent when a socket keepalive event occurs.
      *
      * @param slot the hardware slot on which the event occurred.
-     * @param event the event that occurred.
+     * @param event the event that occurred, as one of the SocketKeepalive.ERROR_*
+     *              or SocketKeepalive.SUCCESS constants.
      */
-    public void sendSocketKeepaliveEvent(int slot, int event) {
+    public final void sendSocketKeepaliveEvent(int slot,
+            @SocketKeepalive.KeepaliveEvent int event) {
         queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, event);
     }
     /** @hide TODO delete once callers have moved to sendSocketKeepaliveEvent */
@@ -845,9 +917,18 @@
     }
 
     /**
-     * Called by ConnectivityService to inform this network transport of signal strength thresholds
+     * Called by ConnectivityService to inform this network agent of signal strength thresholds
      * that when crossed should trigger a system wakeup and a NetworkCapabilities update.
      *
+     * When the system updates the list of thresholds that should wake up the CPU for a
+     * given agent it will call this method on the agent. The agent that implement this
+     * should implement it in hardware so as to ensure the CPU will be woken up on breach.
+     * Agents are expected to react to a breach by sending an updated NetworkCapabilities
+     * object with the appropriate signal strength to sendNetworkCapabilities.
+     *
+     * The specific units are bearer-dependent. See details on the units and requests in
+     * {@link NetworkCapabilities.Builder#setSignalStrength}.
+     *
      * @param thresholds the array of thresholds that should trigger wakeups.
      */
     public void onSignalStrengthThresholdsUpdated(@NonNull int[] thresholds) {
diff --git a/core/java/android/net/NetworkAgentConfig.java b/core/java/android/net/NetworkAgentConfig.java
index ca9328a..fee868a 100644
--- a/core/java/android/net/NetworkAgentConfig.java
+++ b/core/java/android/net/NetworkAgentConfig.java
@@ -155,6 +155,7 @@
     /**
      * @return the legacy type
      */
+    @ConnectivityManager.LegacyNetworkType
     public int getLegacyType() {
         return legacyType;
     }
@@ -206,7 +207,7 @@
     /**
      * Builder class to facilitate constructing {@link NetworkAgentConfig} objects.
      */
-    public static class Builder {
+    public static final class Builder {
         private final NetworkAgentConfig mConfig = new NetworkAgentConfig();
 
         /**
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index fcfcebd..af9414c 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -415,6 +415,20 @@
             | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY);
 
     /**
+     * Capabilities that are allowed for test networks. This list must be set so that it is safe
+     * for an unprivileged user to create a network with these capabilities via shell. As such,
+     * it must never contain capabilities that are generally useful to the system, such as
+     * INTERNET, IMS, SUPL, etc.
+     */
+    private static final long TEST_NETWORKS_ALLOWED_CAPABILITIES =
+            (1 << NET_CAPABILITY_NOT_METERED)
+            | (1 << NET_CAPABILITY_NOT_RESTRICTED)
+            | (1 << NET_CAPABILITY_NOT_VPN)
+            | (1 << NET_CAPABILITY_NOT_ROAMING)
+            | (1 << NET_CAPABILITY_NOT_CONGESTED)
+            | (1 << NET_CAPABILITY_NOT_SUSPENDED);
+
+    /**
      * Adds the given capability to this {@code NetworkCapability} instance.
      * Note that when searching for a network to satisfy a request, all capabilities
      * requested must be satisfied.
@@ -646,6 +660,21 @@
     }
 
     /**
+     * Test networks have strong restrictions on what capabilities they can have. Enforce these
+     * restrictions.
+     * @hide
+     */
+    public void restrictCapabilitesForTestNetwork() {
+        final long originalCapabilities = mNetworkCapabilities;
+        final NetworkSpecifier originalSpecifier = mNetworkSpecifier;
+        clearAll();
+        // Reset the transports to only contain TRANSPORT_TEST.
+        mTransportTypes = (1 << TRANSPORT_TEST);
+        mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
+        mNetworkSpecifier = originalSpecifier;
+    }
+
+    /**
      * Representing the transport type.  Apps should generally not care about transport.  A
      * request for a fast internet connection could be satisfied by a number of different
      * transports.  If any are specified here it will be satisfied a Network that matches
@@ -2000,7 +2029,7 @@
      */
     @SystemApi
     @TestApi
-    public static class Builder {
+    public static final class Builder {
         private final NetworkCapabilities mCaps;
 
         /**
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 964f13f..798856d 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -473,9 +473,7 @@
      *
      * @param nc Capabilities that should satisfy this NetworkRequest. null capabilities do not
      *           satisfy any request.
-     * @hide
      */
-    @SystemApi
     public boolean satisfiedBy(@Nullable NetworkCapabilities nc) {
         return networkCapabilities.satisfiedByNetworkCapabilities(nc);
     }
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
index fc9a8f6..8ff8f4c 100644
--- a/core/java/android/net/SocketKeepalive.java
+++ b/core/java/android/net/SocketKeepalive.java
@@ -109,6 +109,16 @@
     })
     public @interface ErrorCode {}
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            SUCCESS,
+            ERROR_INVALID_LENGTH,
+            ERROR_UNSUPPORTED,
+            ERROR_INSUFFICIENT_RESOURCES
+    })
+    public @interface KeepaliveEvent {}
+
     /**
      * The minimum interval in seconds between keepalive packet transmissions.
      *
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
index 84013e7..615ae65 100644
--- a/core/java/android/os/IVibratorService.aidl
+++ b/core/java/android/os/IVibratorService.aidl
@@ -28,7 +28,7 @@
     boolean registerVibratorStateListener(in IVibratorStateListener listener);
     boolean unregisterVibratorStateListener(in IVibratorStateListener listener);
     boolean hasAmplitudeControl();
-    boolean[] areEffectsSupported(in int[] effectIds);
+    int[] areEffectsSupported(in int[] effectIds);
     boolean[] arePrimitivesSupported(in int[] primitiveIds);
     boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, in VibrationEffect effect,
             in VibrationAttributes attributes);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 785b51d..cdc0019 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -2224,15 +2224,27 @@
      * Intent that is broadcast when the state of {@link #isPowerSaveMode()} is about to change.
      * This broadcast is only sent to registered receivers.
      *
+     * @deprecated This is sent at the same time as {@link #ACTION_POWER_SAVE_MODE_CHANGED} so it
+     * does not provide advanced warning. As such it will be removed in future Android versions.
+     * Use {@link #ACTION_POWER_SAVE_MODE_CHANGED} and {@link #isPowerSaveMode()} instead.
+     *
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "Use {@link #ACTION_POWER_SAVE_MODE_CHANGED} instead.")
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
     public static final String ACTION_POWER_SAVE_MODE_CHANGING
             = "android.os.action.POWER_SAVE_MODE_CHANGING";
 
-    /** @hide */
-    @UnsupportedAppUsage
+    /**
+     * @deprecated Use {@link #isPowerSaveMode()} instead.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "Use {@link #isPowerSaveMode()} instead.")
+    @Deprecated
     public static final String EXTRA_POWER_SAVE_MODE = "mode";
 
     /**
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index b7b3c4f..5d2c9d1 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -607,6 +607,9 @@
      *                             started.
      * @param pkgDataInfoMap Map from related package names to private data directory
      *                       volume UUID and inode number.
+     * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory
+     *                       volume UUID and inode number.
+     * @param bindMountAppsData whether zygote needs to mount CE and DE data.
      * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
      * @param zygoteArgs Additional arguments to supply to the zygote process.
      * @return An object that describes the result of the attempt to start the process.
@@ -631,13 +634,17 @@
                                            @Nullable long[] disabledCompatChanges,
                                            @Nullable Map<String, Pair<String, Long>>
                                                    pkgDataInfoMap,
+                                           @Nullable Map<String, Pair<String, Long>>
+                                                   whitelistedDataInfoMap,
+                                           boolean bindMountAppsData,
                                            boolean bindMountAppStorageDirs,
                                            @Nullable String[] zygoteArgs) {
         return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, packageName,
                     zygotePolicyFlags, isTopApp, disabledCompatChanges,
-                    pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs);
+                    pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
+                    bindMountAppStorageDirs, zygoteArgs);
     }
 
     /** @hide */
@@ -661,7 +668,8 @@
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, packageName,
                     /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, /*isTopApp=*/ false,
-                disabledCompatChanges, /* pkgDataInfoMap */ null, false, zygoteArgs);
+                disabledCompatChanges, /* pkgDataInfoMap */ null,
+                /* whitelistedDataInfoMap */ null, false, false, zygoteArgs);
     }
 
     /**
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index d60820e..8cdcd49 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -665,15 +665,17 @@
      * the preparation for unattended update is reset.
      *
      * @param context the Context to use.
-     * @throws IOException if there were any errors setting up unattended update
+     * @throws IOException if there were any errors clearing the unattended update state
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.RECOVERY)
-    public static boolean clearPrepareForUnattendedUpdate(@NonNull Context context)
+    public static void clearPrepareForUnattendedUpdate(@NonNull Context context)
             throws IOException {
         RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
-        return rs.clearLskf();
+        if (!rs.clearLskf()) {
+            throw new IOException("could not reset unattended update state");
+        }
     }
 
     /**
@@ -684,21 +686,22 @@
      * @param context the Context to use.
      * @param updateToken the token used to call {@link #prepareForUnattendedUpdate} before
      * @param reason the reboot reason to give to the {@link PowerManager}
-     * @throws IOException if there were any errors setting up unattended update
-     * @return false if the reboot couldn't proceed because the device wasn't ready for an
+     * @throws IOException if the reboot couldn't proceed because the device wasn't ready for an
      *               unattended reboot or if the {@code updateToken} did not match the previously
      *               given token
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.RECOVERY)
-    public static boolean rebootAndApply(@NonNull Context context, @NonNull String updateToken,
+    public static void rebootAndApply(@NonNull Context context, @NonNull String updateToken,
             @NonNull String reason) throws IOException {
         if (updateToken == null) {
             throw new NullPointerException("updateToken == null");
         }
         RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
-        return rs.rebootWithLskf(updateToken, reason);
+        if (!rs.rebootWithLskf(updateToken, reason)) {
+            throw new IOException("system not prepared to apply update");
+        }
     }
 
     /**
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index da20c7f..2dba8dc 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -21,12 +21,13 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.media.AudioAttributes;
-import android.os.IVibratorStateListener;
 import android.util.ArrayMap;
 import android.util.Log;
+
 import com.android.internal.annotations.GuardedBy;
-import java.util.concurrent.Executor;
+
 import java.util.Objects;
+import java.util.concurrent.Executor;
 
 /**
  * Vibrator implementation that controls the main system vibrator.
@@ -238,13 +239,13 @@
     }
 
     @Override
-    public boolean[] areEffectsSupported(@VibrationEffect.EffectType int... effectIds) {
+    public int[] areEffectsSupported(@VibrationEffect.EffectType int... effectIds) {
         try {
             return mService.areEffectsSupported(effectIds);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to query effect support");
+            throw e.rethrowAsRuntimeException();
         }
-        return new boolean[effectIds.length];
     }
 
     @Override
@@ -254,8 +255,8 @@
             return mService.arePrimitivesSupported(primitiveIds);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to query effect support");
+            throw e.rethrowAsRuntimeException();
         }
-        return new boolean[primitiveIds.length];
     }
 
 
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index aa89b51..ca86157 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -961,7 +961,7 @@
      *
      * @see VibrationEffect#startComposition()
      */
-    public static class Composition {
+    public static final class Composition {
         /** @hide */
         @IntDef(prefix = { "PRIMITIVE_" }, value = {
                 PRIMITIVE_CLICK,
@@ -1020,6 +1020,8 @@
 
         private ArrayList<PrimitiveEffect> mEffects = new ArrayList<>();
 
+        Composition() { }
+
         /**
          * Add a haptic primitive to the end of the current composition.
          *
@@ -1030,7 +1032,7 @@
          *
          * @return The {@link Composition} object to enable adding multiple primitives in one chain.
          */
-        @Nullable
+        @NonNull
         public Composition addPrimitive(@Primitive int primitiveId) {
             addPrimitive(primitiveId, /*scale*/ 1.0f, /*delay*/ 0);
             return this;
@@ -1046,7 +1048,7 @@
          *
          * @return The {@link Composition} object to enable adding multiple primitives in one chain.
          */
-        @Nullable
+        @NonNull
         public Composition addPrimitive(@Primitive int primitiveId,
                 @FloatRange(from = 0f, to = 1f) float scale) {
             addPrimitive(primitiveId, scale, /*delay*/ 0);
@@ -1058,11 +1060,11 @@
          *
          * @param primitiveId The primitive to add
          * @param scale The scale to apply to the intensity of the primitive.
-         * @param delay The amount of time, in milliseconds, to wait before playing the prior
+         * @param delay The amount of time, in milliseconds, to wait between playing the prior
          *              primitive and this one
          * @return The {@link Composition} object to enable adding multiple primitives in one chain.
          */
-        @Nullable
+        @NonNull
         public Composition addPrimitive(@Primitive int primitiveId,
                 @FloatRange(from = 0f, to = 1f) float scale, @IntRange(from = 0) int delay) {
             mEffects.add(new PrimitiveEffect(checkPrimitive(primitiveId), scale, delay));
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index d4da7a8..86d009e 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -32,6 +32,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
 import java.util.concurrent.Executor;
 
 /**
@@ -72,6 +73,37 @@
      */
     public static final int VIBRATION_INTENSITY_HIGH = 3;
 
+    /**
+     * Vibration effect support: unknown
+     *
+     * The hardware doesn't report it's supported effects, so we can't determine whether the
+     * effect is supported or not.
+     */
+    public static final int VIBRATION_EFFECT_SUPPORT_UNKNOWN = 0;
+
+    /**
+     * Vibration effect support: supported
+     *
+     * This effect is supported by the underlying hardware.
+     */
+    public static final int VIBRATION_EFFECT_SUPPORT_YES = 1;
+
+    /**
+     * Vibration effect support: unsupported
+     *
+     * This effect is <b>not</b> supported by the underlying hardware.
+     */
+    public static final int VIBRATION_EFFECT_SUPPORT_NO = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"VIBRATION_EFFECT_SUPPORT_"}, value = {
+            VIBRATION_EFFECT_SUPPORT_UNKNOWN,
+            VIBRATION_EFFECT_SUPPORT_YES,
+            VIBRATION_EFFECT_SUPPORT_NO,
+    })
+    public @interface VibrationEffectSupport {}
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"VIBRATION_INTENSITY_"}, value = {
@@ -318,47 +350,61 @@
     /**
      * Query whether the vibrator supports the given effects.
      *
-     * If the returned array is {@code null}, the hardware doesn't support querying its supported
-     * effects. It may support any or all effects, but there's no way to programmatically know
-     * whether a {@link #vibrate} call will be successful.
+     * Not all hardware reports its effect capabilities, so the system may not necessarily know
+     * whether an effect is supported or not.
      *
-     * If the returned array is non-null, then it will be the same length as the query array and
-     * the value at a given index will contain whether the effect at that same index in the
-     * querying array is supported or not.
+     * The returned array will be the same length as the query array and the value at a given index
+     * will contain {@link #VIBRATION_EFFECT_SUPPORT_YES} if the effect at that same index in the
+     * querying array is supported, {@link #VIBRATION_EFFECT_SUPPORT_NO} if it isn't supported, or
+     * {@link #VIBRATION_EFFECT_SUPPORT_UNKNOWN} if the system can't determine whether it's
+     * supported or not.
      *
      * @param effectIds Which effects to query for.
-     * @return Whether the effects are supported. Null when the hardware doesn't tell us what it
-     *         supports.
+     * @return An array containing the systems current knowledge about whether the given effects
+     * are supported or not.
      */
-    @Nullable
-    public boolean[] areEffectsSupported(
+    @NonNull
+    @VibrationEffectSupport
+    public int[] areEffectsSupported(
             @NonNull @VibrationEffect.EffectType int... effectIds) {
-        return new boolean[effectIds.length];
+        final int[] support = new int[effectIds.length];
+        Arrays.fill(support, VIBRATION_EFFECT_SUPPORT_NO);
+        return support;
     }
 
     /**
      * Query whether the vibrator supports all of the given effects.
      *
-     * If the result is {@code null}, the hardware doesn't support querying its supported
-     * effects. It may support any or all effects, but there's no way to programmatically know
-     * whether a {@link #vibrate} call will be successful.
+     * Not all hardware reports its effect capabilities, so the system may not necessarily know
+     * whether an effect is supported or not.
      *
-     * If the returned array is non-null, then it will return whether all of the effects are
+     * If the result is {@link #VIBRATION_EFFECT_SUPPORT_YES}, all effects in the query are
      * supported by the hardware.
      *
+     * If the result is {@link #VIBRATION_EFFECT_SUPPORT_NO}, at least one of the effects in the
+     * query is not supported.
+     *
+     * If the result is {@link #VIBRATION_EFFECT_SUPPORT_UNKNOWN}, the system doesn't know whether
+     * all of the effects are supported. It may support any or all of the queried effects,
+     * but there's no way to programmatically know whether a {@link #vibrate} call will successfully
+     * cause a vibration. It's guaranteed, however, that none of the queried effects are
+     * definitively unsupported by the hardware.
+     *
      * @param effectIds Which effects to query for.
-     * @return Whether the effects are supported. {@code null} when the hardware doesn't tell us
-     *         what it supports.
+     * @return Whether all of the effects are supported.
      */
-    @Nullable
-    public Boolean areAllEffectsSupported(
+    @VibrationEffectSupport
+    public final int areAllEffectsSupported(
             @NonNull @VibrationEffect.EffectType int... effectIds) {
-        for (boolean supported : areEffectsSupported(effectIds)) {
-            if (!supported) {
-                return false;
+        int support = VIBRATION_EFFECT_SUPPORT_YES;
+        for (int supported : areEffectsSupported(effectIds)) {
+            if (supported == VIBRATION_EFFECT_SUPPORT_NO) {
+                return VIBRATION_EFFECT_SUPPORT_NO;
+            } else if (supported == VIBRATION_EFFECT_SUPPORT_UNKNOWN) {
+                support = VIBRATION_EFFECT_SUPPORT_UNKNOWN;
             }
         }
-        return true;
+        return support;
     }
 
 
@@ -384,7 +430,7 @@
      * @param primitiveIds Which primitives to query for.
      * @return Whether primitives effects are supported.
      */
-    public boolean areAllPrimitivesSupported(
+    public final boolean areAllPrimitivesSupported(
             @NonNull @VibrationEffect.Composition.Primitive int... primitiveIds) {
         for (boolean supported : arePrimitivesSupported(primitiveIds)) {
             if (!supported) {
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 5f3f14f..a4c99c0 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -333,6 +333,9 @@
      *                             started.
      * @param pkgDataInfoMap Map from related package names to private data directory
      *                       volume UUID and inode number.
+     * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory
+     *                       volume UUID and inode number.
+     * @param bindMountAppsData whether zygote needs to mount CE and DE data.
      * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
      *
      * @param zygoteArgs Additional arguments to supply to the Zygote process.
@@ -355,6 +358,9 @@
                                                   @Nullable long[] disabledCompatChanges,
                                                   @Nullable Map<String, Pair<String, Long>>
                                                           pkgDataInfoMap,
+                                                  @Nullable Map<String, Pair<String, Long>>
+                                                          whitelistedDataInfoMap,
+                                                  boolean bindMountAppsData,
                                                   boolean bindMountAppStorageDirs,
                                                   @Nullable String[] zygoteArgs) {
         // TODO (chriswailes): Is there a better place to check this value?
@@ -367,7 +373,8 @@
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
                     packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,
-                    pkgDataInfoMap, bindMountAppStorageDirs, zygoteArgs);
+                    pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
+                    bindMountAppStorageDirs, zygoteArgs);
         } catch (ZygoteStartFailedEx ex) {
             Log.e(LOG_TAG,
                     "Starting VM process through Zygote failed");
@@ -608,6 +615,9 @@
      * @param disabledCompatChanges a list of disabled compat changes for the process being started.
      * @param pkgDataInfoMap Map from related package names to private data directory volume UUID
      *                       and inode number.
+     * @param whitelistedDataInfoMap Map from whitelisted package names to private data directory
+     *                       volume UUID and inode number.
+     * @param bindMountAppsData whether zygote needs to mount CE and DE data.
      * @param bindMountAppStorageDirs whether zygote needs to mount Android/obb and Android/data.
      * @param extraArgs Additional arguments to supply to the zygote process.
      * @return An object that describes the result of the attempt to start the process.
@@ -631,6 +641,9 @@
                                                       @Nullable long[] disabledCompatChanges,
                                                       @Nullable Map<String, Pair<String, Long>>
                                                               pkgDataInfoMap,
+                                                      @Nullable Map<String, Pair<String, Long>>
+                                                              whitelistedDataInfoMap,
+                                                      boolean bindMountAppsData,
                                                       boolean bindMountAppStorageDirs,
                                                       @Nullable String[] extraArgs)
                                                       throws ZygoteStartFailedEx {
@@ -728,11 +741,33 @@
             }
             argsForZygote.add(sb.toString());
         }
+        if (whitelistedDataInfoMap != null && whitelistedDataInfoMap.size() > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(Zygote.WHITELISTED_DATA_INFO_MAP);
+            sb.append("=");
+            boolean started = false;
+            for (Map.Entry<String, Pair<String, Long>> entry : whitelistedDataInfoMap.entrySet()) {
+                if (started) {
+                    sb.append(',');
+                }
+                started = true;
+                sb.append(entry.getKey());
+                sb.append(',');
+                sb.append(entry.getValue().first);
+                sb.append(',');
+                sb.append(entry.getValue().second);
+            }
+            argsForZygote.add(sb.toString());
+        }
 
         if (bindMountAppStorageDirs) {
             argsForZygote.add(Zygote.BIND_MOUNT_APP_STORAGE_DIRS);
         }
 
+        if (bindMountAppsData) {
+            argsForZygote.add(Zygote.BIND_MOUNT_APP_DATA_DIRS);
+        }
+
         if (disabledCompatChanges != null && disabledCompatChanges.length > 0) {
             StringBuilder sb = new StringBuilder();
             sb.append("--disabled-compat-changes=");
@@ -1291,6 +1326,7 @@
                     true /* startChildZygote */, null /* packageName */,
                     ZYGOTE_POLICY_FLAG_SYSTEM_PROCESS /* zygotePolicyFlags */, false /* isTopApp */,
                     null /* disabledCompatChanges */, null /* pkgDataInfoMap */,
+                    null /* whitelistedDataInfoMap */, false /* bindMountAppsData*/,
                     /* bindMountAppStorageDirs */ false, extraArgs);
 
         } catch (ZygoteStartFailedEx ex) {
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
index 71f931d..5cc73ca 100644
--- a/core/java/android/os/incremental/V4Signature.java
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -72,16 +72,16 @@
      * V4 signature data.
      */
     public static class SigningInfo {
-        public final byte[] v3Digest;  // used to match with the corresponding APK
+        public final byte[] apkDigest;  // used to match with the corresponding APK
         public final byte[] certificate; // ASN.1 DER form
         public final byte[] additionalData; // a free-form binary data blob
         public final byte[] publicKey; // ASN.1 DER, must match the certificate
         public final int signatureAlgorithmId; // see the APK v2 doc for the list
         public final byte[] signature;
 
-        SigningInfo(byte[] v3Digest, byte[] certificate, byte[] additionalData,
+        SigningInfo(byte[] apkDigest, byte[] certificate, byte[] additionalData,
                 byte[] publicKey, int signatureAlgorithmId, byte[] signature) {
-            this.v3Digest = v3Digest;
+            this.apkDigest = apkDigest;
             this.certificate = certificate;
             this.additionalData = additionalData;
             this.publicKey = publicKey;
@@ -94,13 +94,13 @@
          */
         public static SigningInfo fromByteArray(byte[] bytes) throws IOException {
             ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
-            byte[] v3Digest = readBytes(buffer);
+            byte[] apkDigest = readBytes(buffer);
             byte[] certificate = readBytes(buffer);
             byte[] additionalData = readBytes(buffer);
             byte[] publicKey = readBytes(buffer);
             int signatureAlgorithmId = buffer.getInt();
             byte[] signature = readBytes(buffer);
-            return new SigningInfo(v3Digest, certificate, additionalData, publicKey,
+            return new SigningInfo(apkDigest, certificate, additionalData, publicKey,
                     signatureAlgorithmId, signature);
         }
     }
@@ -150,7 +150,7 @@
         final int size =
                 4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize(
                         hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize(
-                        signingInfo.v3Digest) + bytesSize(signingInfo.certificate) + bytesSize(
+                        signingInfo.apkDigest) + bytesSize(signingInfo.certificate) + bytesSize(
                         signingInfo.additionalData);
         ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN);
         buffer.putInt(size);
@@ -159,7 +159,7 @@
         buffer.put(hashingInfo.log2BlockSize);
         writeBytes(buffer, hashingInfo.salt);
         writeBytes(buffer, hashingInfo.rawRootHash);
-        writeBytes(buffer, signingInfo.v3Digest);
+        writeBytes(buffer, signingInfo.apkDigest);
         writeBytes(buffer, signingInfo.certificate);
         writeBytes(buffer, signingInfo.additionalData);
         return buffer.array();
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index bbc936d..99bdfd1 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -194,4 +194,5 @@
     boolean needsCheckpoint() = 86;
     void abortChanges(in String message, boolean retry) = 87;
     void clearUserKeyAuth(int userId, int serialNumber, in byte[] token, in byte[] secret) = 88;
+    void fixupAppDir(in String path) = 89;
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 1454aac..aee32ed 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -162,7 +162,12 @@
     /** {@hide} */
     public static final String PROP_SETTINGS_FUSE = FeatureFlagUtils.PERSIST_PREFIX
             + FeatureFlagUtils.SETTINGS_FUSE_FLAG;
-
+    /**
+     * Property that determines whether {@link OP_LEGACY_STORAGE} is sticky for
+     * legacy apps.
+     * @hide
+     */
+    public static final String PROP_LEGACY_OP_STICKY = "persist.sys.legacy_storage_sticky";
 
     /** {@hide} */
     public static final String UUID_PRIVATE_INTERNAL = null;
@@ -2470,6 +2475,36 @@
         }
     }
 
+    /**
+     * Asks StorageManager to fixup the permissions of an application-private directory.
+     *
+     * On devices without sdcardfs, filesystem permissions aren't magically fixed up. This
+     * is problematic mostly in application-private directories, which are owned by the
+     * application itself; if another process with elevated permissions creates a file
+     * in these directories, the UID will be wrong, and the owning package won't be able
+     * to access the files.
+     *
+     * This API can be used to recursively fix up the permissions on the passed in path.
+     * The default platform user of this API is the DownloadProvider, which can download
+     * things in application-private directories on their behalf.
+     *
+     * This API doesn't require any special permissions, because it merely changes the
+     * permissions of a directory to what they should anyway be.
+     *
+     * @param path the path for which we should fix up the permissions
+     *
+     * @hide
+     */
+    public void fixupAppDir(@NonNull File path) {
+        try {
+            mStorageManager.fixupAppDir(path.getCanonicalPath());
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to get canonical path for " + path.getPath(), e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /** {@hide} */
     private static void setCacheBehavior(File path, String name, boolean enabled)
             throws IOException {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 60f10cd..530585d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6564,13 +6564,6 @@
          * Setting specifying if the accessibility shortcut is enabled.
          * @hide
          */
-        public static final String ACCESSIBILITY_SHORTCUT_ENABLED =
-                "accessibility_shortcut_enabled";
-
-        /**
-         * Setting specifying if the accessibility shortcut is enabled.
-         * @hide
-         */
         public static final String ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN =
                 "accessibility_shortcut_on_lock_screen";
 
@@ -6596,11 +6589,9 @@
                 "accessibility_shortcut_target_service";
 
         /**
-         * Setting specifying the accessibility services, accessibility shortcut targets,
-         * or features to be toggled via the accessibility button in the navigation bar.
-         *
-         * <p> This is a colon-separated string list which contains the flattened
-         * {@link ComponentName} and the class name of a system class implementing a supported
+         * Setting specifying the accessibility service or feature to be toggled via the
+         * accessibility button in the navigation bar. This is either a flattened
+         * {@link ComponentName} or the class name of a system class implementing a supported
          * accessibility feature.
          * @hide
          */
@@ -6609,15 +6600,14 @@
 
         /**
          * Setting specifying the accessibility services, accessibility shortcut targets,
-         * or features to be toggled via the long press accessibility button in the navigation bar.
+         * or features to be toggled via the accessibility button in the navigation bar.
          *
          * <p> This is a colon-separated string list which contains the flattened
          * {@link ComponentName} and the class name of a system class implementing a supported
          * accessibility feature.
          * @hide
          */
-        public static final String ACCESSIBILITY_BUTTON_LONG_PRESS_TARGETS =
-                "accessibility_button_long_press_targets";
+        public static final String ACCESSIBILITY_BUTTON_TARGETS = "accessibility_button_targets";
 
         /**
          * The system class name of magnification controller which is a target to be toggled via
@@ -6782,8 +6772,8 @@
          * zoom in the display content and is targeted to low vision users. The current
          * magnification scale is controlled by {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}.
          *
-         * @deprecated Use {@link #ACCESSIBILITY_BUTTON_TARGET_COMPONENT} instead.
-         * {@link #ACCESSIBILITY_BUTTON_TARGET_COMPONENT} holds the magnification system class name
+         * @deprecated Use {@link #ACCESSIBILITY_BUTTON_TARGETS} instead.
+         * {@link #ACCESSIBILITY_BUTTON_TARGETS} holds the magnification system class name
          * when navigation bar magnification is enabled.
          * @hide
          */
@@ -8581,6 +8571,16 @@
         public static final String QS_TILES = "sysui_qs_tiles";
 
         /**
+         * Whether this user has enabled Quick controls.
+         *
+         * 0 indicates disabled and 1 indicates enabled. A non existent value should be treated as
+         * enabled.
+         *
+         * @hide
+         */
+        public static final String CONTROLS_ENABLED = "controls_enabled";
+
+        /**
          * Specifies whether the web action API is enabled.
          *
          * @hide
diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java
index a9addba..b6a8ced 100644
--- a/core/java/android/service/autofill/InlinePresentation.java
+++ b/core/java/android/service/autofill/InlinePresentation.java
@@ -49,7 +49,8 @@
     private final @NonNull InlinePresentationSpec mInlinePresentationSpec;
 
     /**
-     * Indicates whether the UI should be pinned, hence non-scrollable, in the host.
+     * Indicates whether the UI should be pinned, hence non-scrollable and non-filterable, in the
+     * host.
      */
     private final boolean mPinned;
 
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index cecfe24..61744e42 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -18,7 +18,7 @@
 import static android.view.contentcapture.ContentCaptureHelper.sDebug;
 import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
 import static android.view.contentcapture.ContentCaptureHelper.toList;
-import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID;
+import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID;
 
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index 9accf5b..4262c40 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -304,12 +304,11 @@
         Preconditions.checkNotNull(context);
         Preconditions.checkNotNull(componentName);
         Preconditions.checkNotNull(control);
-        final ComponentName sysuiComponent = ComponentName.unflattenFromString(
-                context.getResources().getString(
-                        com.android.internal.R.string.config_systemUIServiceComponent));
+        final String controlsPackage = context.getString(
+                com.android.internal.R.string.config_controlsPackage);
         Intent intent = new Intent(ACTION_ADD_CONTROL);
         intent.putExtra(Intent.EXTRA_COMPONENT_NAME, componentName);
-        intent.setPackage(sysuiComponent.getPackageName());
+        intent.setPackage(controlsPackage);
         if (isStatelessControl(control)) {
             intent.putExtra(EXTRA_CONTROL, control);
         } else {
diff --git a/core/java/android/service/controls/actions/ControlAction.java b/core/java/android/service/controls/actions/ControlAction.java
index 37a75f0..10f526d 100644
--- a/core/java/android/service/controls/actions/ControlAction.java
+++ b/core/java/android/service/controls/actions/ControlAction.java
@@ -136,7 +136,8 @@
     /**
      * Response code for the {@code consumer} in
      * {@link ControlsProviderService#performControlAction} indicating that in order for the action
-     * to be performed, acknowledgment from the user is required.
+     * to be performed, acknowledgment from the user is required. Any non-empty string returned
+     * from {@link #getChallengeValue} shall be treated as a positive acknowledgment.
      */
     public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 3;
     /**
diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java
index 0170726..c047dc0 100644
--- a/core/java/android/service/dataloader/DataLoaderService.java
+++ b/core/java/android/service/dataloader/DataLoaderService.java
@@ -29,7 +29,6 @@
 import android.content.pm.IDataLoaderStatusListener;
 import android.content.pm.InstallationFile;
 import android.content.pm.InstallationFileParcel;
-import android.content.pm.NamedParcelFileDescriptor;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.util.ExceptionUtils;
@@ -133,16 +132,6 @@
                         }
                     }
                 }
-                if (params.dynamicArgs != null) {
-                    NamedParcelFileDescriptor[] fds = params.dynamicArgs;
-                    for (NamedParcelFileDescriptor nfd : fds) {
-                        try {
-                            nfd.fd.close();
-                        } catch (IOException e) {
-                            Slog.e(TAG, "Failed to close DynamicArgs parcel file descriptor " + e);
-                        }
-                    }
-                }
             }
         }
 
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index e70311f..c9be1c1 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -15,8 +15,6 @@
  */
 package android.service.dreams;
 
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
-
 import android.annotation.IdRes;
 import android.annotation.LayoutRes;
 import android.annotation.NonNull;
@@ -1071,7 +1069,6 @@
     private void onWindowCreated(Window w) {
         mWindow = w;
         mWindow.setCallback(this);
-        mWindow.setType(TYPE_DREAM);
         mWindow.requestFeature(Window.FEATURE_NO_TITLE);
 
         WindowManager.LayoutParams lp = mWindow.getAttributes();
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 7d070b1..97cd760 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -487,7 +487,7 @@
             ModuleProperties properties =
                     mModelManagementService.getDspModuleProperties();
             if (properties != null) {
-                return properties.audioCapabilities;
+                return properties.getAudioCapabilities();
             }
 
             return 0;
@@ -780,15 +780,16 @@
             audioCapabilities |= AUDIO_CAPABILITY_NOISE_SUPPRESSION;
         }
 
-        int code = STATUS_ERROR;
+        int code;
         try {
             code = mModelManagementService.startRecognition(
                     mKeyphraseMetadata.id, mLocale.toLanguageTag(), mInternalCallback,
                     new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
                             recognitionExtra, null /* additional data */, audioCapabilities));
         } catch (RemoteException e) {
-            Slog.w(TAG, "RemoteException in startRecognition!", e);
+            throw e.rethrowFromSystemServer();
         }
+
         if (code != STATUS_OK) {
             Slog.w(TAG, "startRecognition() failed with error code " + code);
         }
@@ -796,12 +797,12 @@
     }
 
     private int stopRecognitionLocked() {
-        int code = STATUS_ERROR;
+        int code;
         try {
             code = mModelManagementService.stopRecognition(mKeyphraseMetadata.id,
                     mInternalCallback);
         } catch (RemoteException e) {
-            Slog.w(TAG, "RemoteException in stopRecognition!", e);
+            throw e.rethrowFromSystemServer();
         }
 
         if (code != STATUS_OK) {
@@ -968,12 +969,12 @@
                 }
             }
 
-            ModuleProperties dspModuleProperties = null;
+            ModuleProperties dspModuleProperties;
             try {
                 dspModuleProperties =
                         mModelManagementService.getDspModuleProperties();
             } catch (RemoteException e) {
-                Slog.w(TAG, "RemoteException in getDspProperties!", e);
+                throw e.rethrowFromSystemServer();
             }
 
             // No DSP available
@@ -989,7 +990,7 @@
                 mKeyphraseMetadata = mModelManagementService.getEnrolledKeyphraseMetadata(
                         mText, mLocale.toLanguageTag());
             } catch (RemoteException e) {
-                Slog.w(TAG, "RemoteException in internalUpdateEnrolledKeyphraseMetadata", e);
+                throw e.rethrowFromSystemServer();
             }
         }
     }
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index b54e4d9..45d3465 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -35,6 +35,7 @@
 import android.os.ServiceManager;
 import android.provider.Settings;
 import android.util.ArraySet;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceActionCheckCallback;
@@ -47,6 +48,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -63,6 +65,8 @@
  * separate process from this one.
  */
 public class VoiceInteractionService extends Service {
+    static final String TAG = VoiceInteractionService.class.getSimpleName();
+
     /**
      * The {@link Intent} that must be declared as handled by the service.
      * To be supported, the service must also require the
@@ -240,9 +244,22 @@
     public void onReady() {
         mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
                 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
+        Objects.requireNonNull(mSystemService);
+        try {
+            mSystemService.asBinder().linkToDeath(mDeathRecipient, 0);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "unable to link to death with system service");
+        }
         mKeyphraseEnrollmentInfo = new KeyphraseEnrollmentInfo(getPackageManager());
     }
 
+    private IBinder.DeathRecipient mDeathRecipient = () -> {
+        Log.e(TAG, "system service binder died shutting down");
+        Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+                VoiceInteractionService::onShutdownInternal, VoiceInteractionService.this));
+    };
+
+
     private void onShutdownInternal() {
         onShutdown();
         // Stop any active recognitions when shutting down.
@@ -349,16 +366,24 @@
     }
 
     private void safelyShutdownHotwordDetector() {
-        try {
-            synchronized (mLock) {
-                if (mHotwordDetector != null) {
-                    mHotwordDetector.stopRecognition();
-                    mHotwordDetector.invalidate();
-                    mHotwordDetector = null;
-                }
+        synchronized (mLock) {
+            if (mHotwordDetector == null) {
+                return;
             }
-        } catch (Exception ex) {
-            // Ignore.
+
+            try {
+                mHotwordDetector.stopRecognition();
+            } catch (Exception ex) {
+                // Ignore.
+            }
+
+            try {
+                mHotwordDetector.invalidate();
+            } catch (Exception ex) {
+                // Ignore.
+            }
+
+            mHotwordDetector = null;
         }
     }
 
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 04be71f..346fe29 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -24,6 +24,7 @@
 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
 import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
+import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
 import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
 
 import android.util.ArrayMap;
@@ -117,7 +118,10 @@
         return vSigner.certs;
     }
 
-    private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
+    /**
+     * Same as above returns the full signer object, containing additional info e.g. digest.
+     */
+    public static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
             throws SignatureNotFoundException, SecurityException, IOException {
         try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
             return verify(apk, verifyIntegrity);
@@ -209,9 +213,11 @@
                     verityDigest, apk.length(), signatureInfo);
         }
 
+        byte[] digest = pickBestDigestForV4(contentDigests);
+
         return new VerifiedSigner(
                 signerCerts.toArray(new X509Certificate[signerCerts.size()][]),
-                verityRootHash);
+                verityRootHash, digest);
     }
 
     private static X509Certificate[] verifySigner(
@@ -426,11 +432,14 @@
      */
     public static class VerifiedSigner {
         public final X509Certificate[][] certs;
-        public final byte[] verityRootHash;
 
-        public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) {
+        public final byte[] verityRootHash;
+        public final byte[] digest;
+
+        public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash, byte[] digest) {
             this.certs = certs;
             this.verityRootHash = verityRootHash;
+            this.digest = digest;
         }
 
     }
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 2437af2..4ab541b 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -16,8 +16,6 @@
 
 package android.util.apk;
 
-import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
-import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
 import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
 import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm;
 import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm;
@@ -26,6 +24,7 @@
 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm;
 import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm;
 import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm;
+import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4;
 import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray;
 
 import android.os.Build;
@@ -213,24 +212,11 @@
                     verityDigest, apk.length(), signatureInfo);
         }
 
-        result.digest = pickBestV3DigestForV4(contentDigests);
+        result.digest = pickBestDigestForV4(contentDigests);
 
         return result;
     }
 
-    // Keep in sync with pickBestV3DigestForV4 in apksigner.V3SchemeVerifier.
-    private static byte[] pickBestV3DigestForV4(Map<Integer, byte[]> contentDigests) {
-        final int[] orderedContentDigestTypes =
-                {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
-                        CONTENT_DIGEST_CHUNKED_SHA256};
-        for (int contentDigestType : orderedContentDigestTypes) {
-            if (contentDigests.containsKey(contentDigestType)) {
-                return contentDigests.get(contentDigestType);
-            }
-        }
-        return null;
-    }
-
     private static VerifiedSigner verifySigner(
             ByteBuffer signerBlock,
             Map<Integer, byte[]> contentDigests,
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
index 8c240d9..d40efce 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java
@@ -145,7 +145,7 @@
                     "Public key mismatch between certificate and signature record");
         }
 
-        return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.v3Digest);
+        return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.apkDigest);
     }
 
     /**
@@ -155,11 +155,11 @@
      */
     public static class VerifiedSigner {
         public final Certificate[] certs;
-        public byte[] v3Digest;
+        public byte[] apkDigest;
 
-        public VerifiedSigner(Certificate[] certs, byte[] v3Digest) {
+        public VerifiedSigner(Certificate[] certs, byte[] apkDigest) {
             this.certs = certs;
-            this.v3Digest = v3Digest;
+            this.apkDigest = apkDigest;
         }
 
     }
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index c1cee48..ab8f80d3 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -184,27 +184,45 @@
             Signature[] signerSigs = convertToSignatures(signerCerts);
 
             if (verifyFull) {
-                // v4 is an add-on and requires v3 signature to validate against its certificates
-                ApkSignatureSchemeV3Verifier.VerifiedSigner nonstreaming =
-                        ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
-                Certificate[][] nonstreamingCerts = new Certificate[][]{nonstreaming.certs};
-                Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts);
+                byte[] nonstreamingDigest = null;
+                Certificate[][] nonstreamingCerts = null;
 
+                try {
+                    // v4 is an add-on and requires v2 or v3 signature to validate against its
+                    // certificate and digest
+                    ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer =
+                            ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
+                    nonstreamingDigest = v3Signer.digest;
+                    nonstreamingCerts = new Certificate[][]{v3Signer.certs};
+                } catch (SignatureNotFoundException e) {
+                    try {
+                        ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer =
+                                ApkSignatureSchemeV2Verifier.verify(apkPath, false);
+                        nonstreamingDigest = v2Signer.digest;
+                        nonstreamingCerts = v2Signer.certs;
+                    } catch (SignatureNotFoundException ee) {
+                        throw new SecurityException(
+                                "V4 verification failed to collect V2/V3 certificates from : "
+                                        + apkPath, ee);
+                    }
+                }
+
+                Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts);
                 if (nonstreamingSigs.length != signerSigs.length) {
                     throw new SecurityException(
-                            "Invalid number of certificates: " + nonstreaming.certs.length);
+                            "Invalid number of certificates: " + nonstreamingSigs.length);
                 }
 
                 for (int i = 0, size = signerSigs.length; i < size; ++i) {
                     if (!nonstreamingSigs[i].equals(signerSigs[i])) {
-                        throw new SecurityException("V4 signature certificate does not match V3");
+                        throw new SecurityException(
+                                "V4 signature certificate does not match V2/V3");
                     }
                 }
 
-                // TODO(b/151240006): add support for v2 digest and make it mandatory.
-                if (!ArrayUtils.isEmpty(vSigner.v3Digest) && !ArrayUtils.equals(vSigner.v3Digest,
-                        nonstreaming.digest, vSigner.v3Digest.length)) {
-                    throw new SecurityException("V3 digest in V4 signature does not match V3");
+                if (!ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest,
+                        vSigner.apkDigest.length)) {
+                    throw new SecurityException("APK digest in V4 signature does not match V2/V3");
                 }
             }
 
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 4fe8515..2a4b65d 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -421,6 +421,10 @@
     static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
     static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3;
 
+    private static final int[] V4_CONTENT_DIGEST_ALGORITHMS =
+            {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
+                    CONTENT_DIGEST_CHUNKED_SHA256};
+
     static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) {
         int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1);
         int digestAlgorithm2 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm2);
@@ -572,6 +576,21 @@
     }
 
     /**
+     * Returns the best digest from the map of available digests.
+     * similarly to compareContentDigestAlgorithm.
+     *
+     * Keep in sync with pickBestDigestForV4 in apksigner's ApkSigningBlockUtils.
+     */
+    static byte[] pickBestDigestForV4(Map<Integer, byte[]> contentDigests) {
+        for (int algo : V4_CONTENT_DIGEST_ALGORITHMS) {
+            if (contentDigests.containsKey(algo)) {
+                return contentDigests.get(algo);
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns new byte buffer whose content is a shared subsequence of this buffer's content
      * between the specified start (inclusive) and end (exclusive) positions. As opposed to
      * {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index dffcafe..0ccb1e0 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -57,8 +57,8 @@
  * <li>The application display area specifies the part of the display that may contain
  * an application window, excluding the system decorations.  The application display area may
  * be smaller than the real display area because the system subtracts the space needed
- * for decor elements such as the status bar.  Use {@link WindowMetrics#getSize()} to query the
- * application window size.</li>
+ * for decor elements such as the status bar.  Use {@link WindowMetrics#getBounds()} to query the
+ * application window bounds.</li>
  * <li>The real display area specifies the part of the display that contains content
  * including the system decorations.  Even so, the real display area may be smaller than the
  * physical size of the display if the window manager is emulating a smaller display
@@ -673,7 +673,7 @@
      *
      * @param outSize A {@link Point} object to receive the size information.
      * @deprecated Use {@link WindowManager#getCurrentWindowMetrics()} to obtain an instance of
-     * {@link WindowMetrics} and use {@link WindowMetrics#getSize()} instead.
+     * {@link WindowMetrics} and use {@link WindowMetrics#getBounds()} instead.
      */
     @Deprecated
     public void getSize(Point outSize) {
@@ -689,7 +689,7 @@
      * Gets the size of the display as a rectangle, in pixels.
      *
      * @param outSize A {@link Rect} object to receive the size information.
-     * @deprecated Use {@link WindowMetrics#getSize()} to get the dimensions of the application
+     * @deprecated Use {@link WindowMetrics#getBounds()} to get the dimensions of the application
      * window area.
      */
     @Deprecated
@@ -755,7 +755,7 @@
     }
 
     /**
-     * @deprecated Use {@link WindowMetrics#getSize()} instead.
+     * @deprecated Use {@link WindowMetrics#getBounds#width()} instead.
      */
     @Deprecated
     public int getWidth() {
@@ -766,7 +766,7 @@
     }
 
     /**
-     * @deprecated Use {@link WindowMetrics#getSize()} instead.
+     * @deprecated Use {@link WindowMetrics#getBounds()#height()} instead.
      */
     @Deprecated
     public int getHeight() {
@@ -1105,7 +1105,7 @@
      * </p>
      *
      * @param outMetrics A {@link DisplayMetrics} object to receive the metrics.
-     * @deprecated Use {@link WindowMetrics#getSize()} to get the dimensions of the application
+     * @deprecated Use {@link WindowMetrics#getBounds()} to get the dimensions of the application
      * window area, and {@link Configuration#densityDpi} to get the current density.
      */
     @Deprecated
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 43f80f1..40a460d 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -16,7 +16,9 @@
 
 package android.view;
 
+import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
+import static android.view.InsetsState.toInternalType;
 import static android.view.InsetsState.toPublicType;
 import static android.view.WindowInsets.Type.all;
 import static android.view.WindowInsets.Type.ime;
@@ -26,8 +28,6 @@
 import android.animation.AnimationHandler;
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
 import android.animation.TypeEvaluator;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
@@ -38,11 +38,9 @@
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.RemoteException;
-import android.renderscript.Sampler.Value;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
-import android.util.Property;
 import android.util.SparseArray;
 import android.view.InsetsSourceConsumer.ShowResult;
 import android.view.InsetsState.InternalInsetsType;
@@ -370,6 +368,7 @@
     private int mLastLegacySystemUiFlags;
     private DisplayCutout mLastDisplayCutout;
     private boolean mStartingAnimation;
+    private int mCaptionInsetsHeight = 0;
 
     private SyncRtSurfaceTransactionApplier mApplier;
 
@@ -463,22 +462,54 @@
 
     @VisibleForTesting
     public boolean onStateChanged(InsetsState state) {
-        boolean localStateChanged = !mState.equals(state);
+        boolean localStateChanged = !mState.equals(state, true /* excludingCaptionInsets */)
+                || !captionInsetsUnchanged();
         if (!localStateChanged && mLastDispachedState.equals(state)) {
             return false;
         }
-        mState.set(state);
+        updateState(state);
         mLastDispachedState.set(state, true /* copySources */);
         applyLocalVisibilityOverride();
         if (localStateChanged) {
             mViewRoot.notifyInsetsChanged();
         }
-        if (!mState.equals(mLastDispachedState)) {
+        if (!mState.equals(mLastDispachedState, true /* excludingCaptionInsets */)) {
             sendStateToWindowManager();
         }
         return true;
     }
 
+    private void updateState(InsetsState newState) {
+        mState.setDisplayFrame(newState.getDisplayFrame());
+        for (int i = newState.getSourcesCount() - 1; i >= 0; i--) {
+            InsetsSource source = newState.sourceAt(i);
+            getSourceConsumer(source.getType()).updateSource(source);
+        }
+        for (int i = mState.getSourcesCount() - 1; i >= 0; i--) {
+            InsetsSource source = mState.sourceAt(i);
+            if (newState.peekSource(source.getType()) == null) {
+                mState.removeSource(source.getType());
+            }
+        }
+        if (mCaptionInsetsHeight != 0) {
+            mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(mFrame.left, mFrame.top,
+                    mFrame.right, mFrame.top + mCaptionInsetsHeight));
+        }
+    }
+
+    private boolean captionInsetsUnchanged() {
+        if (mState.peekSource(ITYPE_CAPTION_BAR) == null
+                && mCaptionInsetsHeight == 0) {
+            return true;
+        }
+        if (mState.peekSource(ITYPE_CAPTION_BAR) != null
+                && mCaptionInsetsHeight
+                == mState.peekSource(ITYPE_CAPTION_BAR).getFrame().height()) {
+            return true;
+        }
+        return false;
+    }
+
     /**
      * @see InsetsState#calculateInsets
      */
@@ -858,8 +889,15 @@
             control.cancel();
         }
         for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
-            if (mRunningAnimations.get(i).runner == control) {
+            RunningAnimation runningAnimation = mRunningAnimations.get(i);
+            if (runningAnimation.runner == control) {
                 mRunningAnimations.remove(i);
+                ArraySet<Integer> types = toInternalType(control.getTypes());
+                for (int j = types.size() - 1; j >= 0; j--) {
+                    if (getSourceConsumer(types.valueAt(j)).notifyAnimationFinished()) {
+                        mViewRoot.notifyInsetsChanged();
+                    }
+                }
                 break;
             }
         }
@@ -946,6 +984,7 @@
         InsetsState tmpState = new InsetsState();
         for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
             final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
+            if (consumer.getType() == ITYPE_CAPTION_BAR) continue;
             if (consumer.getControl() != null) {
                 tmpState.addSource(mState.getSource(consumer.getType()));
             }
@@ -1087,6 +1126,11 @@
     }
 
     @Override
+    public void setCaptionInsetsHeight(int height) {
+        mCaptionInsetsHeight = height;
+    }
+
+    @Override
     public void setSystemBarsBehavior(@Behavior int behavior) {
         mViewRoot.mWindowAttributes.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
         if (mViewRoot.mWindowAttributes.insetsFlags.behavior != behavior) {
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 294faaf..033ccef 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
 
 import android.annotation.NonNull;
@@ -118,6 +119,12 @@
         if (!getIntersection(frame, relativeFrame, mTmpFrame)) {
             return Insets.NONE;
         }
+        // During drag-move and drag-resizing, the caption insets position may not get updated
+        // before the app frame get updated. To layout the app content correctly during drag events,
+        // we always return the insets with the corresponding height covering the top.
+        if (getType() == ITYPE_CAPTION_BAR) {
+            return Insets.of(0, frame.height(), 0, 0);
+        }
 
         // TODO: Currently, non-floating IME always intersects at bottom due to issues with cutout.
         // However, we should let the policy decide from the server.
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 3325734..f36621c 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -16,11 +16,13 @@
 
 package android.view;
 
+import static android.view.InsetsController.ANIMATION_TYPE_NONE;
 import static android.view.InsetsController.AnimationType;
 import static android.view.InsetsState.toPublicType;
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.graphics.Rect;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets.Type.InsetsType;
@@ -64,6 +66,8 @@
     private final Supplier<Transaction> mTransactionSupplier;
     private @Nullable InsetsSourceControl mSourceControl;
     private boolean mHasWindowFocus;
+    private Rect mPendingFrame;
+    private Rect mPendingVisibleFrame;
 
     public InsetsSourceConsumer(@InternalInsetsType int type, InsetsState state,
             Supplier<Transaction> transactionSupplier, InsetsController controller) {
@@ -215,6 +219,38 @@
         // no-op for types that always return ShowResult#SHOW_IMMEDIATELY.
     }
 
+    void updateSource(InsetsSource newSource) {
+        InsetsSource source = mState.peekSource(mType);
+        if (source == null || mController.getAnimationType(mType) == ANIMATION_TYPE_NONE
+                || source.getFrame().equals(newSource.getFrame())) {
+            mState.addSource(newSource);
+            return;
+        }
+
+        // Frame is changing while animating. Keep note of the new frame but keep existing frame
+        // until animaition is finished.
+        newSource = new InsetsSource(newSource);
+        mPendingFrame = new Rect(newSource.getFrame());
+        mPendingVisibleFrame = newSource.getVisibleFrame() != null
+                ? new Rect(newSource.getVisibleFrame())
+                : null;
+        newSource.setFrame(source.getFrame());
+        newSource.setVisibleFrame(source.getVisibleFrame());
+        mState.addSource(newSource);
+    }
+
+    boolean notifyAnimationFinished() {
+        if (mPendingFrame != null) {
+            InsetsSource source = mState.getSource(mType);
+            source.setFrame(mPendingFrame);
+            source.setVisibleFrame(mPendingVisibleFrame);
+            mPendingFrame = null;
+            mPendingVisibleFrame = null;
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Sets requested visibility from the client, regardless of whether we are able to control it at
      * the moment.
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 40e6f57..c515466 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -45,6 +45,8 @@
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -498,6 +500,19 @@
 
     @Override
     public boolean equals(Object o) {
+        return equals(o, false);
+    }
+
+    /**
+     * An equals method can exclude the caption insets. This is useful because we assemble the
+     * caption insets information on the client side, and when we communicate with server, it's
+     * excluded.
+     * @param excludingCaptionInsets {@code true} if we want to compare two InsetsState objects but
+     *                                           ignore the caption insets source value.
+     * @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise.
+     */
+    @VisibleForTesting
+    public boolean equals(Object o, boolean excludingCaptionInsets) {
         if (this == o) { return true; }
         if (o == null || getClass() != o.getClass()) { return false; }
 
@@ -506,11 +521,24 @@
         if (!mDisplayFrame.equals(state.mDisplayFrame)) {
             return false;
         }
-        if (mSources.size() != state.mSources.size()) {
+        int size = mSources.size();
+        int otherSize = state.mSources.size();
+        if (excludingCaptionInsets) {
+            if (mSources.get(ITYPE_CAPTION_BAR) != null) {
+                size--;
+            }
+            if (state.mSources.get(ITYPE_CAPTION_BAR) != null) {
+                otherSize--;
+            }
+        }
+        if (size != otherSize) {
             return false;
         }
         for (int i = mSources.size() - 1; i >= 0; i--) {
             InsetsSource source = mSources.valueAt(i);
+            if (excludingCaptionInsets) {
+                if (source.getType() == ITYPE_CAPTION_BAR) continue;
+            }
             InsetsSource otherSource = state.mSources.get(source.getType());
             if (otherSource == null) {
                 return false;
diff --git a/core/java/android/view/PendingInsetsController.java b/core/java/android/view/PendingInsetsController.java
index 229ee03..a106b2c 100644
--- a/core/java/android/view/PendingInsetsController.java
+++ b/core/java/android/view/PendingInsetsController.java
@@ -42,6 +42,7 @@
     private InsetsController mReplayedInsetsController;
     private ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
             = new ArrayList<>();
+    private int mCaptionInsetsHeight = 0;
 
     @Override
     public void show(int types) {
@@ -80,6 +81,11 @@
     }
 
     @Override
+    public void setCaptionInsetsHeight(int height) {
+        mCaptionInsetsHeight = height;
+    }
+
+    @Override
     public void setSystemBarsBehavior(int behavior) {
         if (mReplayedInsetsController != null) {
             mReplayedInsetsController.setSystemBarsBehavior(behavior);
@@ -134,6 +140,9 @@
         if (mAppearanceMask != 0) {
             controller.setSystemBarsAppearance(mAppearance, mAppearanceMask);
         }
+        if (mCaptionInsetsHeight != 0) {
+            controller.setCaptionInsetsHeight(mCaptionInsetsHeight);
+        }
         int size = mRequests.size();
         for (int i = 0; i < size; i++) {
             mRequests.get(i).replay(controller);
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index cd22ad6..fe70ff7 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -192,6 +192,7 @@
         final WindowManager.LayoutParams lp =
                 new WindowManager.LayoutParams(width, height,
                         WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
+        lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
         setView(view, lp);
     }
 
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 59fc6e9..c89e0c9 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -176,6 +176,7 @@
     boolean mUseAlpha = false;
     float mSurfaceAlpha = 1f;
     boolean mClipSurfaceToBounds;
+    int mBackgroundColor = Color.BLACK;
 
     @UnsupportedAppUsage
     boolean mHaveFrame = false;
@@ -828,6 +829,12 @@
         }
     }
 
+    private Transaction updateBackgroundColor(Transaction t) {
+        final float[] colorComponents = new float[] { Color.red(mBackgroundColor) / 255.f,
+                Color.green(mBackgroundColor) / 255.f, Color.blue(mBackgroundColor) / 255.f };
+        t.setColor(mBackgroundControl, colorComponents);
+        return t;
+    }
 
     private void releaseSurfaces() {
         mSurfaceAlpha = 1f;
@@ -1000,6 +1007,7 @@
                     }
 
                     updateBackgroundVisibility(mTmpTransaction);
+                    updateBackgroundColor(mTmpTransaction);
                     if (mUseAlpha) {
                         mTmpTransaction.setAlpha(mSurfaceControl, alpha);
                         mSurfaceAlpha = alpha;
@@ -1399,10 +1407,8 @@
             return;
         }
 
-        final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f,
-                Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f };
-
-        mTmpTransaction.setColor(mBackgroundControl, colorComponents).apply();
+        mBackgroundColor = bgColor;
+        updateBackgroundColor(mTmpTransaction).apply();
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d69357b..da18608 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3475,7 +3475,7 @@
     /**
      * Flag indicating the field should not have yellow highlight when autofilled.
      */
-    private static final int PFLAG4_AUTOFILL_HIDE_HIGHLIGHT = 0x100;
+    private static final int PFLAG4_AUTOFILL_HIDE_HIGHLIGHT = 0x200;
 
     /* End of masks for mPrivateFlags4 */
 
@@ -28768,6 +28768,11 @@
                 publicAlternatives = "Use {@link WindowInsets#getInsets(int)}")
         final Rect mStableInsets = new Rect();
 
+        /**
+         * Current caption insets to the display coordinate.
+         */
+        final Rect mCaptionInsets = new Rect();
+
         final DisplayCutout.ParcelableWrapper mDisplayCutout =
                 new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
 
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 04260c4..69d37ab 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -16,18 +16,24 @@
 
 package android.view;
 
+import static android.os.StrictMode.vmIncorrectContextUseEnabled;
+
 import android.annotation.FloatRange;
 import android.annotation.TestApi;
+import android.app.Activity;
 import android.app.AppGlobals;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Rect;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.RemoteException;
+import android.os.StrictMode;
 import android.provider.Settings;
 import android.util.DisplayMetrics;
-import android.util.Size;
+import android.util.Log;
 import android.util.SparseArray;
 import android.util.TypedValue;
 
@@ -35,6 +41,8 @@
  * Contains methods to standard constants used in the UI for timeouts, sizes, and distances.
  */
 public class ViewConfiguration {
+    private static final String TAG = "ViewConfiguration";
+
     /**
      * Defines the width of the horizontal scrollbar and the height of the vertical scrollbar in
      * dips
@@ -372,11 +380,13 @@
     }
 
     /**
-     * Creates a new configuration for the specified context. The configuration depends on
-     * various parameters of the context, like the dimension of the display or the density
-     * of the display.
+     * Creates a new configuration for the specified visual {@link Context}. The configuration
+     * depends on various parameters of the {@link Context}, like the dimension of the display or
+     * the density of the display.
      *
-     * @param context The application context used to initialize this view configuration.
+     * @param context A visual {@link Context} used to initialize the view configuration. It must
+     *                be {@link Activity} or other {@link Context} created with
+     *                {@link Context#createWindowContext(int, Bundle)}.
      *
      * @see #get(android.content.Context)
      * @see android.util.DisplayMetrics
@@ -410,8 +420,8 @@
 
         // Size of the screen in bytes, in ARGB_8888 format
         final WindowManager windowManager = context.getSystemService(WindowManager.class);
-        final Size maxWindowSize = windowManager.getMaximumWindowMetrics().getSize();
-        mMaximumDrawingCacheSize = 4 * maxWindowSize.getWidth() * maxWindowSize.getHeight();
+        final Rect maxWindowBounds = windowManager.getMaximumWindowMetrics().getBounds();
+        mMaximumDrawingCacheSize = 4 * maxWindowBounds.width() * maxWindowBounds.height();
 
         mOverscrollDistance = (int) (sizeAndDensity * OVERSCROLL_DISTANCE + 0.5f);
         mOverflingDistance = (int) (sizeAndDensity * OVERFLING_DISTANCE + 0.5f);
@@ -480,13 +490,27 @@
     }
 
     /**
-     * Returns a configuration for the specified context. The configuration depends on
-     * various parameters of the context, like the dimension of the display or the
+     * Returns a configuration for the specified visual {@link Context}. The configuration depends
+     * on various parameters of the {@link Context}, like the dimension of the display or the
      * density of the display.
      *
-     * @param context The application context used to initialize the view configuration.
+     * @param context A visual {@link Context} used to initialize the view configuration. It must
+     *                be {@link Activity} or other {@link Context} created with
+     *                {@link Context#createWindowContext(int, Bundle)}.
      */
     public static ViewConfiguration get(Context context) {
+        if (!context.isUiContext() && vmIncorrectContextUseEnabled()) {
+            final String errorMessage = "Tried to access UI constants from a non-visual Context.";
+            final String message = "UI constants, such as display metrics or window metrics, "
+                    + "must be accessed from Activity or other visual Context. "
+                    + "Use an Activity or a Context created with "
+                    + "Context#createWindowContext(int, Bundle), which are adjusted to the "
+                    + "configuration and visual bounds of an area on screen.";
+            final Exception exception = new IllegalArgumentException(errorMessage);
+            StrictMode.onIncorrectContextUsed(message, exception);
+            Log.e(TAG, errorMessage + message, exception);
+        }
+
         final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
         final int density = (int) (100.0f * metrics.density);
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 50202ae..51304dc 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -158,6 +158,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.os.SomeArgs;
+import com.android.internal.policy.DecorView;
 import com.android.internal.policy.PhoneFallbackEventHandler;
 import com.android.internal.util.Preconditions;
 import com.android.internal.view.BaseSurfaceHolder;
@@ -2221,6 +2222,19 @@
         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
     }
 
+    private boolean updateCaptionInsets() {
+        if (!(mView instanceof DecorView)) return false;
+        final int captionInsetsHeight = ((DecorView) mView).getCaptionInsetsHeight();
+        final Rect captionFrame = new Rect();
+        if (captionInsetsHeight != 0) {
+            captionFrame.set(mWinFrame.left, mWinFrame.top, mWinFrame.right,
+                            mWinFrame.top + captionInsetsHeight);
+        }
+        if (mAttachInfo.mCaptionInsets.equals(captionFrame)) return false;
+        mAttachInfo.mCaptionInsets.set(captionFrame);
+        return true;
+    }
+
     private boolean shouldDispatchCutout() {
         return mWindowAttributes.layoutInDisplayCutoutMode
                         == LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
@@ -2592,6 +2606,9 @@
                     mAttachInfo.mAlwaysConsumeSystemBars = mPendingAlwaysConsumeSystemBars;
                     dispatchApplyInsets = true;
                 }
+                if (updateCaptionInsets()) {
+                    dispatchApplyInsets = true;
+                }
                 if (dispatchApplyInsets || mLastSystemUiVisibility !=
                         mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested) {
                     mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 0c5c183..ae9afaba 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -49,9 +49,6 @@
 import android.transition.TransitionManager;
 import android.util.Pair;
 import android.view.View.OnApplyWindowInsetsListener;
-import android.view.ViewGroup.LayoutParams;
-import android.view.WindowInsets.Side.InsetsSide;
-import android.view.WindowInsets.Type.InsetsType;
 import android.view.accessibility.AccessibilityEvent;
 
 import java.util.Collections;
@@ -323,7 +320,7 @@
     @UnsupportedAppUsage
     private boolean mDestroyed;
 
-    private boolean mOverlayWithDecorCaptionEnabled = false;
+    private boolean mOverlayWithDecorCaptionEnabled = true;
     private boolean mCloseOnSwipeEnabled = false;
 
     // The current window attributes.
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index fde184c..9b2a6cb 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -17,6 +17,7 @@
 
 package android.view;
 
+import static android.view.WindowInsets.Type.CAPTION_BAR;
 import static android.view.WindowInsets.Type.DISPLAY_CUTOUT;
 import static android.view.WindowInsets.Type.FIRST;
 import static android.view.WindowInsets.Type.IME;
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 0282eca..439223c 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -196,6 +196,15 @@
     @Appearance int getSystemBarsAppearance();
 
     /**
+     * Notify the caption insets height change. The information will be used on the client side to,
+     * make sure the InsetsState has the correct caption insets.
+     *
+     * @param height the height of caption bar insets.
+     * @hide
+     */
+    void setCaptionInsetsHeight(int height);
+
+    /**
      * Controls the behavior of system bars.
      *
      * @param behavior Determines how the bars behave when being hidden by the application.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 77ce5c1..cc380f3 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -781,8 +781,6 @@
                         to = "BOOT_PROGRESS"),
                 @ViewDebug.IntToString(from = TYPE_INPUT_CONSUMER,
                         to = "INPUT_CONSUMER"),
-                @ViewDebug.IntToString(from = TYPE_DREAM,
-                        to = "DREAM"),
                 @ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR_PANEL,
                         to = "NAVIGATION_BAR_PANEL"),
                 @ViewDebug.IntToString(from = TYPE_DISPLAY_OVERLAY,
@@ -1105,13 +1103,6 @@
         public static final int TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22;
 
         /**
-         * Window type: Dreams (screen saver) window, just above keyguard.
-         * In multiuser systems shows only on the owning user's window.
-         * @hide
-         */
-        public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;
-
-        /**
          * Window type: Navigation bar panel (when navigation bar is distinct from status bar)
          * In multiuser systems shows on all users' windows.
          * @hide
@@ -1180,8 +1171,9 @@
         public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35;
 
         /**
-         * Window type: shares similar characteristics with {@link #TYPE_DREAM}. The layer is
+         * Window type: shows directly above the keyguard. The layer is
          * reserved for screenshot region selection. These windows must not take input focus.
+         * In multiuser systems shows only on the owning user's window.
          * @hide
          */
         public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36;
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 8bf1ade..316a5f2 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -35,7 +35,6 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.util.Size;
 
 import com.android.internal.os.IResultReceiver;
 
@@ -220,7 +219,7 @@
         final Context context = mParentWindow != null ? mParentWindow.getContext() : mContext;
         final Rect bounds = getCurrentBounds(context);
 
-        return new WindowMetrics(toSize(bounds), computeWindowInsets(bounds));
+        return new WindowMetrics(bounds, computeWindowInsets(bounds));
     }
 
     private static Rect getCurrentBounds(Context context) {
@@ -232,11 +231,7 @@
     @Override
     public WindowMetrics getMaximumWindowMetrics() {
         final Rect maxBounds = getMaximumBounds();
-        return new WindowMetrics(toSize(maxBounds), computeWindowInsets(maxBounds));
-    }
-
-    private Size toSize(Rect frame) {
-        return new Size(frame.width(), frame.height());
+        return new WindowMetrics(maxBounds, computeWindowInsets(maxBounds));
     }
 
     private Rect getMaximumBounds() {
diff --git a/core/java/android/view/WindowMetrics.java b/core/java/android/view/WindowMetrics.java
index ab5a06e..86ef879 100644
--- a/core/java/android/view/WindowMetrics.java
+++ b/core/java/android/view/WindowMetrics.java
@@ -18,10 +18,10 @@
 
 import android.annotation.NonNull;
 import android.graphics.Point;
-import android.util.Size;
+import android.graphics.Rect;
 
 /**
- * Metrics about a Window, consisting of the size and {@link WindowInsets}.
+ * Metrics about a Window, consisting of the bounds and {@link WindowInsets}.
  * <p>
  * This is usually obtained from {@link WindowManager#getCurrentWindowMetrics()} and
  * {@link WindowManager#getMaximumWindowMetrics()}.
@@ -31,21 +31,22 @@
  * @see WindowManager#getMaximumWindowMetrics()
  */
 public final class WindowMetrics {
-    private final @NonNull Size mSize;
+    private final @NonNull Rect mBounds;
     private final @NonNull WindowInsets mWindowInsets;
 
-    public WindowMetrics(@NonNull Size size, @NonNull WindowInsets windowInsets) {
-        mSize = size;
+    public WindowMetrics(@NonNull Rect bounds, @NonNull WindowInsets windowInsets) {
+        mBounds = bounds;
         mWindowInsets = windowInsets;
     }
 
     /**
-     * Returns the size of the window.
+     * Returns the bounds of the area associated with this window or visual context.
      * <p>
-     * <b>Note that this reports a different size than {@link Display#getSize(Point)}.</b>
-     * This method reports the window size including all system bars area, while
-     * {@link Display#getSize(Point)} reports the area excluding navigation bars and display cutout
-     * areas. The value reported by {@link Display#getSize(Point)} can be obtained by using:
+     * <b>Note that the size of the reported bounds can have different size than
+     * {@link Display#getSize(Point)}.</b> This method reports the window size including all system
+     * bar areas, while {@link Display#getSize(Point)} reports the area excluding navigation bars
+     * and display cutout areas. The value reported by {@link Display#getSize(Point)} can be
+     * obtained by using:
      * <pre class="prettyprint">
      * final WindowMetrics metrics = windowManager.getCurrentMetrics();
      * // Gets all excluding insets
@@ -66,16 +67,16 @@
      * </pre>
      * </p>
      *
-     * @return window size in pixel.
+     * @return window bounds in pixels.
      */
-    public @NonNull Size getSize() {
-        return mSize;
+    public @NonNull Rect getBounds() {
+        return mBounds;
     }
 
     /**
-     * Returns the {@link WindowInsets} of the window.
+     * Returns the {@link WindowInsets} of the area associated with this window or visual context.
      *
-     * @return the {@link WindowInsets} of the window.
+     * @return the {@link WindowInsets} of the visual area.
      */
     public @NonNull WindowInsets getWindowInsets() {
         return mWindowInsets;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 39a9ed4..267a5a6 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1242,9 +1242,10 @@
                 if (mLastAutofilledData.containsKey(id)) {
                     value = view.getAutofillValue();
                     valueWasRead = true;
+                    final boolean hideHighlight = mLastAutofilledData.keySet().size() == 1;
 
                     if (Objects.equals(mLastAutofilledData.get(id), value)) {
-                        view.setAutofilled(true, false);
+                        view.setAutofilled(true, hideHighlight);
                     } else {
                         view.setAutofilled(false, false);
                         mLastAutofilledData.remove(id);
diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java
index 8d3dc83..2ead352 100644
--- a/core/java/android/view/autofill/AutofillPopupWindow.java
+++ b/core/java/android/view/autofill/AutofillPopupWindow.java
@@ -25,7 +25,6 @@
 import android.os.RemoteException;
 import android.transition.Transition;
 import android.util.Log;
-import android.util.Size;
 import android.view.View;
 import android.view.View.OnTouchListener;
 import android.view.ViewTreeObserver;
@@ -129,10 +128,10 @@
             // Gravity.BOTTOM because PopupWindow base class does not expose computeGravity().
             final WindowManager windowManager = anchor.getContext()
                     .getSystemService(WindowManager.class);
-            final Size windowSize = windowManager.getCurrentWindowMetrics().getSize();
-            width = windowSize.getWidth();
+            final Rect windowBounds = windowManager.getCurrentWindowMetrics().getBounds();
+            width = windowBounds.width();
             if (height != LayoutParams.MATCH_PARENT) {
-                offsetY = windowSize.getHeight() - height;
+                offsetY = windowBounds.height() - height;
             }
             actualAnchor = anchor;
         } else if (virtualBounds != null) {
diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java
index 1692051..b84cb88 100644
--- a/core/java/android/view/contentcapture/ContentCaptureContext.java
+++ b/core/java/android/view/contentcapture/ContentCaptureContext.java
@@ -15,7 +15,7 @@
  */
 package android.view.contentcapture;
 
-import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID;
+import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index ea34d94..f49b1be 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -16,7 +16,7 @@
 package android.view.contentcapture;
 
 import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString;
-import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID;
+import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index b988927..954b83b 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -235,6 +235,13 @@
     public static final int RESULT_CODE_SECURITY_EXCEPTION = -1;
 
     /**
+     * ID used to indicate that a session does not exist
+     * @hide
+     */
+    @SystemApi
+    public static final int NO_SESSION_ID = 0;
+
+    /**
      * Timeout for calls to system_server.
      */
     private static final int SYNC_CALLS_TIMEOUT_MS = 5000;
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 012f5e6..39c7210 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -17,12 +17,12 @@
 
 import static android.view.contentcapture.ContentCaptureHelper.sDebug;
 import static android.view.contentcapture.ContentCaptureHelper.sVerbose;
+import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID;
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.graphics.Insets;
 import android.util.DebugUtils;
 import android.util.Log;
@@ -53,13 +53,6 @@
     private static final Random sIdGenerator = new Random();
 
     /**
-    *  ID used to indicate that a session does not exist
-    *  @hide
-    */
-    @SystemApi
-    public static final int NO_SESSION_ID = 0;
-
-    /**
      * Initial state, when there is no session.
      *
      * @hide
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4aeea10..62dd192 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -405,6 +405,13 @@
     // The actual zoom value may changes based on this initial zoom value.
     private float mInitialZoom = 1f;
 
+    // For calculating the line change slops while moving cursor/selection.
+    // The slop max/min value include line height and the slop on the upper/lower line.
+    private static final int LINE_CHANGE_SLOP_MAX_DP = 45;
+    private static final int LINE_CHANGE_SLOP_MIN_DP = 12;
+    private int mLineChangeSlopMax;
+    private int mLineChangeSlopMin;
+
     Editor(TextView textView) {
         mTextView = textView;
         // Synchronize the filter list, which places the undo input filter at the end.
@@ -430,6 +437,14 @@
             logCursor("Editor", "New magnifier is %s.",
                     mNewMagnifierEnabled ? "enabled" : "disabled");
         }
+
+        mLineChangeSlopMax = (int) TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP, LINE_CHANGE_SLOP_MAX_DP,
+                mTextView.getContext().getResources().getDisplayMetrics());
+        mLineChangeSlopMin = (int) TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP, LINE_CHANGE_SLOP_MIN_DP,
+                mTextView.getContext().getResources().getDisplayMetrics());
+
     }
 
     @VisibleForTesting
@@ -6018,7 +6033,14 @@
         }
     }
 
-    private int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) {
+    @VisibleForTesting
+    public void setLineChangeSlopMinMaxForTesting(final int min, final int max) {
+        mLineChangeSlopMin = min;
+        mLineChangeSlopMax = max;
+    }
+
+    @VisibleForTesting
+    public int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) {
         final int trueLine = mTextView.getLineAtCoordinate(y);
         if (layout == null || prevLine > layout.getLineCount()
                 || layout.getLineCount() <= 0 || prevLine < 0) {
@@ -6031,28 +6053,21 @@
             return trueLine;
         }
 
+        final int lineHeight = layout.getLineBottom(prevLine) - layout.getLineTop(prevLine);
+        int slop = (int)(LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS
+                * (layout.getLineBottom(trueLine) - layout.getLineTop(trueLine)));
+        slop = Math.max(mLineChangeSlopMin,
+                Math.min(mLineChangeSlopMax, lineHeight + slop)) - lineHeight;
+        slop = Math.max(0, slop);
+
         final float verticalOffset = mTextView.viewportToContentVerticalOffset();
-        final int lineCount = layout.getLineCount();
-        final float slop = mTextView.getLineHeight() * LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS;
-
-        final float firstLineTop = layout.getLineTop(0) + verticalOffset;
-        final float prevLineTop = layout.getLineTop(prevLine) + verticalOffset;
-        final float yTopBound = Math.max(prevLineTop - slop, firstLineTop + slop);
-
-        final float lastLineBottom = layout.getLineBottom(lineCount - 1) + verticalOffset;
-        final float prevLineBottom = layout.getLineBottom(prevLine) + verticalOffset;
-        final float yBottomBound = Math.min(prevLineBottom + slop, lastLineBottom - slop);
-
-        // Determine if we've moved lines based on y position and previous line.
-        int currLine;
-        if (y <= yTopBound) {
-            currLine = Math.max(prevLine - 1, 0);
-        } else if (y >= yBottomBound) {
-            currLine = Math.min(prevLine + 1, lineCount - 1);
-        } else {
-            currLine = prevLine;
+        if (trueLine > prevLine && y >= layout.getLineBottom(prevLine) + slop + verticalOffset) {
+            return trueLine;
         }
-        return currLine;
+        if (trueLine < prevLine && y <= layout.getLineTop(prevLine) - slop + verticalOffset) {
+            return trueLine;
+        }
+        return prevLine;
     }
 
     /**
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 8943da4..4f14539 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -144,9 +144,6 @@
     @Nullable
     private CharSequence mText;
 
-    // TODO(b/144152069): Remove this after assessing impact on dogfood.
-    private boolean mIsCustomToast;
-
     /**
      * Construct an empty Toast object.  You must call {@link #setView} before you
      * can call {@link #show}.
@@ -214,8 +211,7 @@
                     service.enqueueTextToast(pkg, mToken, mText, mDuration, displayId, callback);
                 }
             } else {
-                service.enqueueTextOrCustomToast(pkg, mToken, tn, mDuration, displayId,
-                        mIsCustomToast);
+                service.enqueueToast(pkg, mToken, tn, mDuration, displayId);
             }
         } catch (RemoteException e) {
             // Empty
@@ -253,7 +249,6 @@
      */
     @Deprecated
     public void setView(View view) {
-        mIsCustomToast = true;
         mNextView = view;
     }
 
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 54ea57a..d64b5f1 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -144,9 +144,6 @@
                 Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE),
                 false, co, UserHandle.USER_ALL);
         mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED),
-                false, co, UserHandle.USER_ALL);
-        mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN),
                 false, co, UserHandle.USER_ALL);
         mContext.getContentResolver().registerContentObserver(
@@ -174,8 +171,6 @@
     public void onSettingsChanged() {
         final boolean hasShortcutTarget = hasShortcutTarget();
         final ContentResolver cr = mContext.getContentResolver();
-        final boolean enabled = Settings.Secure.getIntForUser(
-                cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 1, mUserId) == 1;
         // Enable the shortcut from the lockscreen by default if the dialog has been shown
         final int dialogAlreadyShown = Settings.Secure.getIntForUser(
                 cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, DialogStaus.NOT_SHOWN,
@@ -183,7 +178,7 @@
         mEnabledOnLockScreen = Settings.Secure.getIntForUser(
                 cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
                 dialogAlreadyShown, mUserId) == 1;
-        mIsShortcutEnabled = enabled && hasShortcutTarget;
+        mIsShortcutEnabled = hasShortcutTarget;
     }
 
     /**
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index ae9ce65..c535163 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -387,9 +387,6 @@
         resetViewVisibilitiesForWorkProfileEmptyState(emptyStateView);
         emptyStateView.setVisibility(View.VISIBLE);
 
-        ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon);
-        icon.setImageResource(iconRes);
-
         TextView title = emptyStateView.findViewById(R.id.resolver_empty_state_title);
         title.setText(titleRes);
 
@@ -401,9 +398,17 @@
             subtitle.setVisibility(View.GONE);
         }
 
+        ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon);
         Button button = emptyStateView.findViewById(R.id.resolver_empty_state_button);
-        button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
-        button.setOnClickListener(buttonOnClick);
+        if (!getContext().getResources().getBoolean(R.bool.resolver_landscape_phone)) {
+            icon.setVisibility(View.VISIBLE);
+            icon.setImageResource(iconRes);
+            button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
+            button.setOnClickListener(buttonOnClick);
+        } else {
+            icon.setVisibility(View.GONE);
+            button.setVisibility(View.GONE);
+        }
 
         activeListAdapter.markTabLoaded();
     }
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index c408641..51b1334 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -169,13 +169,41 @@
     private static List<AccessibilityButtonTarget> getInstalledServiceTargets(
             @NonNull Context context) {
         final List<AccessibilityButtonTarget> targets = new ArrayList<>();
-        targets.addAll(getAccessibilityServiceTargets(context));
-        targets.addAll(getAccessibilityActivityTargets(context));
+        targets.addAll(getAccessibilityFilteredTargets(context));
         targets.addAll(getWhiteListingServiceTargets(context));
 
         return targets;
     }
 
+    private static List<AccessibilityButtonTarget> getAccessibilityFilteredTargets(
+            @NonNull Context context) {
+        final List<AccessibilityButtonTarget> serviceTargets =
+                getAccessibilityServiceTargets(context);
+        final List<AccessibilityButtonTarget> activityTargets =
+                getAccessibilityActivityTargets(context);
+
+        for (AccessibilityButtonTarget activityTarget : activityTargets) {
+            serviceTargets.removeIf(serviceTarget -> {
+                final ComponentName serviceComponentName =
+                        ComponentName.unflattenFromString(serviceTarget.getId());
+                final ComponentName activityComponentName =
+                        ComponentName.unflattenFromString(activityTarget.getId());
+                final boolean isSamePackageName = activityComponentName.getPackageName().equals(
+                        serviceComponentName.getPackageName());
+                final boolean isSameLabel = activityTarget.getLabel().equals(
+                        serviceTarget.getLabel());
+
+                return isSamePackageName && isSameLabel;
+            });
+        }
+
+        final List<AccessibilityButtonTarget> targets = new ArrayList<>();
+        targets.addAll(serviceTargets);
+        targets.addAll(activityTargets);
+
+        return targets;
+    }
+
     private static List<AccessibilityButtonTarget> getAccessibilityServiceTargets(
             @NonNull Context context) {
         final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index a1a434d..dca682e 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2674,7 +2674,7 @@
      */
     private boolean shouldShowStickyContentPreview() {
         return shouldShowStickyContentPreviewNoOrientationCheck()
-                && getResources().getBoolean(R.bool.sharesheet_show_content_preview);
+                && !getResources().getBoolean(R.bool.resolver_landscape_phone);
     }
 
     private boolean shouldShowStickyContentPreviewNoOrientationCheck() {
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index ff03f1a..c758989 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -205,9 +205,15 @@
     /** List of packages with the same uid, and its app data info: volume uuid and inode. */
     public static final String PKG_DATA_INFO_MAP = "--pkg-data-info-map";
 
+    /** List of whitelisted packages and its app data info: volume uuid and inode. */
+    public static final String WHITELISTED_DATA_INFO_MAP = "--whitelisted-data-info-map";
+
     /** Bind mount app storage dirs to lower fs not via fuse */
     public static final String BIND_MOUNT_APP_STORAGE_DIRS = "--bind-mount-storage-dirs";
 
+    /** Bind mount app storage dirs to lower fs not via fuse */
+    public static final String BIND_MOUNT_APP_DATA_DIRS = "--bind-mount-data-dirs";
+
     /**
      * An extraArg passed when a zygote process is forking a child-zygote, specifying a name
      * in the abstract socket namespace. This socket name is what the new child zygote
@@ -313,6 +319,8 @@
      * @param isTopApp true if the process is for top (high priority) application.
      * @param pkgDataInfoList A list that stores related packages and its app data
      * info: volume uuid and inode.
+     * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for whitelisted apps.
+     * @param bindMountAppDataDirs  True if the zygote needs to mount data dirs.
      * @param bindMountAppStorageDirs  True if the zygote needs to mount storage dirs.
      *
      * @return 0 if this is the child, pid of the child
@@ -321,13 +329,15 @@
     static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
             int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
             int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
-            boolean isTopApp, String[] pkgDataInfoList, boolean bindMountAppStorageDirs) {
+            boolean isTopApp, String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+            boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
         ZygoteHooks.preFork();
 
         int pid = nativeForkAndSpecialize(
                 uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                 fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
-                pkgDataInfoList, bindMountAppStorageDirs);
+                pkgDataInfoList, whitelistedDataInfoList, bindMountAppDataDirs,
+                bindMountAppStorageDirs);
         if (pid == 0) {
             // Note that this event ends at the end of handleChildProc,
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -344,6 +354,7 @@
             int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
             int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
             String appDataDir, boolean isTopApp, String[] pkgDataInfoList,
+            String[] whitelistedDataInfoList, boolean bindMountAppDataDirs,
             boolean bindMountAppStorageDirs);
 
     /**
@@ -371,15 +382,19 @@
      * volume uuid and CE dir inode. For example, pkgDataInfoList = [app_a_pkg_name,
      * app_a_data_volume_uuid, app_a_ce_inode, app_b_pkg_name, app_b_data_volume_uuid,
      * app_b_ce_inode, ...];
+     * @param whitelistedDataInfoList Like pkgDataInfoList, but it's for whitelisted apps.
+     * @param bindMountAppDataDirs  True if the zygote needs to mount data dirs.
      * @param bindMountAppStorageDirs  True if the zygote needs to mount storage dirs.
      */
     private static void specializeAppProcess(int uid, int gid, int[] gids, int runtimeFlags,
             int[][] rlimits, int mountExternal, String seInfo, String niceName,
             boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
-            String[] pkgDataInfoList, boolean bindMountAppStorageDirs) {
+            String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+            boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs) {
         nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
                 niceName, startChildZygote, instructionSet, appDataDir, isTopApp,
-                pkgDataInfoList, bindMountAppStorageDirs);
+                pkgDataInfoList, whitelistedDataInfoList,
+                bindMountAppDataDirs, bindMountAppStorageDirs);
 
         // Note that this event ends at the end of handleChildProc.
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -399,7 +414,8 @@
     private static native void nativeSpecializeAppProcess(int uid, int gid, int[] gids,
             int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
             boolean startChildZygote, String instructionSet, String appDataDir, boolean isTopApp,
-            String[] pkgDataInfoList, boolean bindMountAppStorageDirs);
+            String[] pkgDataInfoList, String[] whitelistedDataInfoList,
+            boolean bindMountAppDataDirs, boolean bindMountAppStorageDirs);
 
     /**
      * Called to do any initialization before starting an application.
@@ -724,7 +740,8 @@
                                  args.mRuntimeFlags, rlimits, args.mMountExternal,
                                  args.mSeInfo, args.mNiceName, args.mStartChildZygote,
                                  args.mInstructionSet, args.mAppDataDir, args.mIsTopApp,
-                                 args.mPkgDataInfoList, args.mBindMountAppStorageDirs);
+                                 args.mPkgDataInfoList, args.mWhitelistedDataInfoList,
+                                 args.mBindMountAppDataDirs, args.mBindMountAppStorageDirs);
 
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index 1a63765..94c1f71 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -227,11 +227,22 @@
     String[] mPkgDataInfoList;
 
     /**
+     * A list that stores all whitelisted app data info: volume uuid and inode.
+     * Null if it does need to do app data isolation.
+     */
+    String[] mWhitelistedDataInfoList;
+
+    /**
      * @see Zygote#BIND_MOUNT_APP_STORAGE_DIRS
      */
     boolean mBindMountAppStorageDirs;
 
     /**
+     * @see Zygote#BIND_MOUNT_APP_DATA_DIRS
+     */
+    boolean mBindMountAppDataDirs;
+
+    /**
      * Constructs instance and parses args
      *
      * @param args zygote command-line args
@@ -452,8 +463,12 @@
                 }
             } else if (arg.startsWith(Zygote.PKG_DATA_INFO_MAP)) {
                 mPkgDataInfoList = getAssignmentList(arg);
+            } else if (arg.startsWith(Zygote.WHITELISTED_DATA_INFO_MAP)) {
+                mWhitelistedDataInfoList = getAssignmentList(arg);
             } else if (arg.equals(Zygote.BIND_MOUNT_APP_STORAGE_DIRS)) {
                 mBindMountAppStorageDirs = true;
+            } else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) {
+                mBindMountAppDataDirs = true;
             } else {
                 break;
             }
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index bc8dfd4..6e880d4 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -258,7 +258,8 @@
                 parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
                 parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
                 parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
-                parsedArgs.mPkgDataInfoList, parsedArgs.mBindMountAppStorageDirs);
+                parsedArgs.mPkgDataInfoList,parsedArgs.mWhitelistedDataInfoList,
+                parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);
 
         try {
             if (pid == 0) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 51b73fc..d2508f36 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -78,7 +78,6 @@
 import android.view.Gravity;
 import android.view.InputQueue;
 import android.view.InsetsState;
-import android.view.InsetsController;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.KeyEvent;
 import android.view.KeyboardShortcutGroup;
@@ -1174,6 +1173,12 @@
                     false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
                     animate && !disallowAnimate,
                     mForceWindowDrawsBarBackgrounds, state);
+
+            if (mHasCaption) {
+                final int captionColor = calculateStatusBarColor();
+                mDecorCaptionView.getCaption().setBackgroundColor(captionColor);
+                updateDecorCaptionShade();
+            }
         }
 
         // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or
@@ -1355,7 +1360,7 @@
                 : state.attributes.isPresent(insetsState, mWindow.getAttributes().flags, force);
         boolean show = state.attributes.isVisible(state.present, color,
                 mWindow.getAttributes().flags, force);
-        boolean showView = show && !isResizing() && size > 0;
+        boolean showView = show && !isResizing() && !mHasCaption && size > 0;
 
         boolean visibilityChanged = false;
         View view = state.view;
@@ -2021,6 +2026,7 @@
             if (getForeground() != null) {
                 drawableChanged();
             }
+            getWindowInsetsController().setCaptionInsetsHeight(getCaptionInsetsHeight());
         }
     }
 
@@ -2094,6 +2100,7 @@
             mDecorCaptionView.onConfigurationChanged(displayWindowDecor);
             enableCaption(displayWindowDecor);
         }
+        getWindowInsetsController().setCaptionInsetsHeight(getCaptionInsetsHeight());
     }
 
     void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
@@ -2182,11 +2189,11 @@
         inflater = inflater.from(context);
         final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
                 null);
-        setDecorCaptionShade(context, view);
+        setDecorCaptionShade(view);
         return view;
     }
 
-    private void setDecorCaptionShade(Context context, DecorCaptionView view) {
+    private void setDecorCaptionShade(DecorCaptionView view) {
         final int shade = mWindow.getDecorCaptionShade();
         switch (shade) {
             case DECOR_CAPTION_SHADE_LIGHT:
@@ -2196,15 +2203,10 @@
                 setDarkDecorCaptionShade(view);
                 break;
             default: {
-                TypedValue value = new TypedValue();
-                context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
-                // We invert the shade depending on brightness of the theme. Dark shade for light
-                // theme and vice versa. Thanks to this the buttons should be visible on the
-                // background.
-                if (Color.luminance(value.data) < 0.5) {
-                    setLightDecorCaptionShade(view);
-                } else {
+                if ((getWindowSystemUiVisibility() & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0) {
                     setDarkDecorCaptionShade(view);
+                } else {
+                    setLightDecorCaptionShade(view);
                 }
                 break;
             }
@@ -2213,7 +2215,7 @@
 
     void updateDecorCaptionShade() {
         if (mDecorCaptionView != null) {
-            setDecorCaptionShade(getContext(), mDecorCaptionView);
+            setDecorCaptionShade(mDecorCaptionView);
         }
     }
 
@@ -2484,6 +2486,15 @@
     }
 
     /**
+     * @hide
+     * @return the height of insets covering the top of window content area.
+     */
+    public int getCaptionInsetsHeight() {
+        if (!mWindow.isOverlayWithDecorCaptionEnabled()) return 0;
+        return getCaptionHeight();
+    }
+
+    /**
      * Converts a DIP measure into physical pixels.
      * @param dip The dip value.
      * @return Returns the number of pixels.
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 0aeaa47..980943e 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -22,14 +22,10 @@
 import android.annotation.StyleRes;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
-import android.graphics.Point;
 import android.graphics.Rect;
-import android.util.Size;
-import android.view.Display;
 import android.view.Gravity;
 import android.view.View;
 import android.view.WindowManager;
-import android.view.WindowMetrics;
 import android.widget.PopupWindow.OnDismissListener;
 
 import com.android.internal.view.menu.MenuPresenter.Callback;
@@ -227,9 +223,9 @@
     @NonNull
     private MenuPopup createPopup() {
         final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
-        final Size maxWindowSize = windowManager.getMaximumWindowMetrics().getSize();
+        final Rect maxWindowBounds = windowManager.getMaximumWindowMetrics().getBounds();
 
-        final int smallestWidth = Math.min(maxWindowSize.getWidth(), maxWindowSize.getHeight());
+        final int smallestWidth = Math.min(maxWindowBounds.width(), maxWindowBounds.height());
         final int minSmallestWidthCascading = mContext.getResources().getDimensionPixelSize(
             com.android.internal.R.dimen.cascading_menus_min_smallest_width);
         final boolean enableCascadingSubmenus = smallestWidth >= minSmallestWidthCascading;
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
index b5d787c2..7a01024 100644
--- a/core/java/com/android/internal/widget/DecorCaptionView.java
+++ b/core/java/com/android/internal/widget/DecorCaptionView.java
@@ -17,7 +17,6 @@
 package com.android.internal.widget;
 
 import android.content.Context;
-import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.util.AttributeSet;
@@ -53,8 +52,7 @@
  * <li>..</li>
  * </ul>
  *
- * Although this ViewGroup has only two direct sub-Views, its behavior is more complex due to
- * overlaying caption on the content and drawing.
+ * Here describe the behavior of overlaying caption on the content and drawing.
  *
  * First, no matter where the content View gets added, it will always be the first child and the
  * caption will be the second. This way the caption will always be drawn on top of the content when
@@ -66,11 +64,9 @@
  * <li>DecorCaptionView.onInterceptTouchEvent() will try intercepting the touch events if the
  * down action is performed on top close or maximize buttons; the reason for that is we want these
  * buttons to always work.</li>
- * <li>The content View will receive the touch event. Mind that content is actually underneath the
- * caption, so we need to introduce our own dispatch ordering. We achieve this by overriding
- * {@link #buildTouchDispatchChildList()}.</li>
- * <li>If the touch event is not consumed by the content View, it will go to the caption View
- * and the dragging logic will be executed.</li>
+ * <li>The caption view will try to consume the event to apply the dragging logic.</li>
+ * <li>If the touch event is not consumed by the caption, the content View will receive the touch
+ * event</li>
  * </ul>
  */
 public class DecorCaptionView extends ViewGroup implements View.OnTouchListener,
@@ -137,11 +133,6 @@
         mOwner = owner;
         mShow = show;
         mOverlayWithAppContent = owner.isOverlayWithDecorCaptionEnabled();
-        if (mOverlayWithAppContent) {
-            // The caption is covering the content, so we make its background transparent to make
-            // the content visible.
-            mCaption.setBackgroundColor(Color.TRANSPARENT);
-        }
         updateCaptionVisibility();
         // By changing the outline provider to BOUNDS, the window can remove its
         // background without removing the shadow.
@@ -236,18 +227,6 @@
     }
 
     @Override
-    public ArrayList<View> buildTouchDispatchChildList() {
-        mTouchDispatchList.ensureCapacity(3);
-        if (mCaption != null) {
-            mTouchDispatchList.add(mCaption);
-        }
-        if (mContent != null) {
-            mTouchDispatchList.add(mContent);
-        }
-        return mTouchDispatchList;
-    }
-
-    @Override
     public boolean shouldDelayChildPressedState() {
         return false;
     }
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index c83cab7..31527e8 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -227,7 +227,7 @@
         }
 
         final int oldCollapsibleHeight = mCollapsibleHeight;
-        mCollapsibleHeight = Math.max(mCollapsibleHeight, getMaxCollapsedHeight());
+        mCollapsibleHeight = Math.min(mCollapsibleHeight, getMaxCollapsedHeight());
 
         if (updateCollapseOffset(oldCollapsibleHeight, !isDragging())) {
             return;
diff --git a/core/jni/android_media_AudioProductStrategies.cpp b/core/jni/android_media_AudioProductStrategies.cpp
index 17a02b2..34be2a5 100644
--- a/core/jni/android_media_AudioProductStrategies.cpp
+++ b/core/jni/android_media_AudioProductStrategies.cpp
@@ -85,10 +85,23 @@
     jStrategyId = static_cast<jint>(strategy.getId());
 
     // Audio Attributes Group array
-    std::map<int, std::vector<AudioAttributes> > groups;
+    int attrGroupIndex = 0;
+    std::map<int /**attributesGroupIndex*/, std::vector<AudioAttributes> > groups;
     for (const auto &attr : strategy.getAudioAttributes()) {
-        int attrGroupId = attr.getGroupId();
-        groups[attrGroupId].push_back(attr);
+        int groupId = attr.getGroupId();
+        int streamType = attr.getStreamType();
+        const auto &iter = std::find_if(begin(groups), end(groups),
+                                        [groupId, streamType](const auto &iter) {
+            const auto &frontAttr = iter.second.front();
+            return frontAttr.getGroupId() == groupId && frontAttr.getStreamType() == streamType;
+        });
+        // Same Volume Group Id and same stream type
+        if (iter != end(groups)) {
+             groups[iter->first].push_back(attr);
+        } else {
+            // Add a new Group of AudioAttributes for this product strategy
+            groups[attrGroupIndex++].push_back(attr);
+        }
     }
     numAttributesGroups = groups.size();
 
@@ -97,7 +110,7 @@
     for (const auto &iter : groups) {
         std::vector<AudioAttributes> audioAttributesGroups = iter.second;
         jint numAttributes = audioAttributesGroups.size();
-        jint jGroupId = iter.first;
+        jint jGroupId = audioAttributesGroups.front().getGroupId();
         jint jLegacyStreamType = audioAttributesGroups.front().getStreamType();
 
         jStatus = JNIAudioAttributeHelper::getJavaArray(env, &jAudioAttributes, numAttributes);
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index cb5a332..bf3fc57 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -214,12 +214,8 @@
 
 // ----------------------------------------------------------------------------
 
-static std::unique_ptr<DynamicLibManager> sDynamicLibManager =
-    std::make_unique<DynamicLibManager>();
-
 // Let the opaque type AAssetManager refer to a guarded AssetManager2 instance.
 struct GuardedAssetManager : public ::AAssetManager {
-  GuardedAssetManager() : guarded_assetmanager(sDynamicLibManager.get()) {}
   Guarded<AssetManager2> guarded_assetmanager;
 };
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index ea3c0fa..aa2d1b5 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -110,7 +110,6 @@
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
 using android::base::GetBoolProperty;
-using android::base::GetProperty;
 
 #define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
                               append(StringPrintf(__VA_ARGS__))
@@ -170,18 +169,6 @@
 
 static constexpr int DEFAULT_DATA_DIR_PERMISSION = 0751;
 
-/**
- * Property to control if app data isolation is enabled.
- */
-static const std::string ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY =
-    "persist.zygote.app_data_isolation";
-
-/**
- * Property to enable app data isolation for sdcard obb or data in vold.
- */
-static const std::string ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
-    "persist.sys.vold_app_data_isolation_enabled";
-
 static constexpr const uint64_t UPPER_HALF_WORD_MASK = 0xFFFF'FFFF'0000'0000;
 static constexpr const uint64_t LOWER_HALF_WORD_MASK = 0x0000'0000'FFFF'FFFF;
 
@@ -1319,20 +1306,13 @@
  * be decrypted after storage is decrypted.
  *
  */
-static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list,
-    uid_t uid, const char* process_name, jstring managed_nice_name,
-    fail_fn_t fail_fn) {
+static void isolateAppData(JNIEnv* env, const std::vector<std::string>& merged_data_info_list,
+    uid_t uid, const char* process_name,
+    jstring managed_nice_name, fail_fn_t fail_fn) {
 
   const userid_t userId = multiuser_get_user_id(uid);
 
-  auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
-
-  int size = (pkg_data_info_list != nullptr) ? env->GetArrayLength(pkg_data_info_list) : 0;
-  // Size should be a multiple of 3, as it contains list of <package_name, volume_uuid, inode>
-  if ((size % 3) != 0) {
-    fail_fn(CREATE_ERROR("Wrong pkg_inode_list size %d", size));
-  }
-  ensureInAppMountNamespace(fail_fn);
+  int size = merged_data_info_list.size();
 
   // Mount tmpfs on all possible data directories, so app no longer see the original apps data.
   char internalCePath[PATH_MAX];
@@ -1377,14 +1357,10 @@
   bool legacySymlinkCreated = false;
 
   for (int i = 0; i < size; i += 3) {
-    jstring package_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i));
-    std::string packageName = extract_fn(package_str).value();
+    std::string const & packageName = merged_data_info_list[i];
+    std::string const & volUuid  = merged_data_info_list[i + 1];
+    std::string const & inode = merged_data_info_list[i + 2];
 
-    jstring vol_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i + 1));
-    std::string volUuid = extract_fn(vol_str).value();
-
-    jstring inode_str = (jstring) (env->GetObjectArrayElement(pkg_data_info_list, i + 2));
-    std::string inode = extract_fn(inode_str).value();
     std::string::size_type sz;
     long long ceDataInode = std::stoll(inode, &sz);
 
@@ -1482,6 +1458,48 @@
   freecon(dataDataContext);
 }
 
+static void insertPackagesToMergedList(JNIEnv* env,
+  std::vector<std::string>& merged_data_info_list,
+  jobjectArray data_info_list, const char* process_name,
+  jstring managed_nice_name, fail_fn_t fail_fn) {
+
+  auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
+
+  int size = (data_info_list != nullptr) ? env->GetArrayLength(data_info_list) : 0;
+  // Size should be a multiple of 3, as it contains list of <package_name, volume_uuid, inode>
+  if ((size % 3) != 0) {
+    fail_fn(CREATE_ERROR("Wrong data_info_list size %d", size));
+  }
+
+  for (int i = 0; i < size; i += 3) {
+    jstring package_str = (jstring) (env->GetObjectArrayElement(data_info_list, i));
+    std::string packageName = extract_fn(package_str).value();
+    merged_data_info_list.push_back(packageName);
+
+    jstring vol_str = (jstring) (env->GetObjectArrayElement(data_info_list, i + 1));
+    std::string volUuid = extract_fn(vol_str).value();
+    merged_data_info_list.push_back(volUuid);
+
+    jstring inode_str = (jstring) (env->GetObjectArrayElement(data_info_list, i + 2));
+    std::string inode = extract_fn(inode_str).value();
+    merged_data_info_list.push_back(inode);
+  }
+}
+
+static void isolateAppData(JNIEnv* env, jobjectArray pkg_data_info_list,
+    jobjectArray whitelisted_data_info_list, uid_t uid, const char* process_name,
+    jstring managed_nice_name, fail_fn_t fail_fn) {
+
+  ensureInAppMountNamespace(fail_fn);
+  std::vector<std::string> merged_data_info_list;
+  insertPackagesToMergedList(env, merged_data_info_list, pkg_data_info_list,
+          process_name, managed_nice_name, fail_fn);
+  insertPackagesToMergedList(env, merged_data_info_list, whitelisted_data_info_list,
+          process_name, managed_nice_name, fail_fn);
+
+  isolateAppData(env, merged_data_info_list, uid, process_name, managed_nice_name, fail_fn);
+}
+
 /**
  * Like isolateAppData(), isolate jit profile directories, so apps don't see what
  * other apps are installed by reading content inside /data/misc/profiles/cur.
@@ -1594,7 +1612,9 @@
                              jstring managed_nice_name, bool is_system_server,
                              bool is_child_zygote, jstring managed_instruction_set,
                              jstring managed_app_data_dir, bool is_top_app,
-                             jobjectArray pkg_data_info_list, bool mount_storage_dirs) {
+                             jobjectArray pkg_data_info_list,
+                             jobjectArray whitelisted_data_info_list,
+                             bool mount_data_dirs, bool mount_storage_dirs) {
   const char* process_name = is_system_server ? "system_server" : "zygote";
   auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
   auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
@@ -1628,9 +1648,9 @@
   // give a null in same_uid_pkgs and private_volumes so they don't need app data isolation.
   // Isolated process / webview / app zygote should be gated by SELinux and file permission
   // so they can't even traverse CE / DE directories.
-  if (pkg_data_info_list != nullptr
-      && GetBoolProperty(ANDROID_APP_DATA_ISOLATION_ENABLED_PROPERTY, true)) {
-    isolateAppData(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
+  if (mount_data_dirs) {
+    isolateAppData(env, pkg_data_info_list, whitelisted_data_info_list,
+            uid, process_name, managed_nice_name, fail_fn);
     isolateJitProfile(env, pkg_data_info_list, uid, process_name, managed_nice_name, fail_fn);
   }
   if ((mount_external != MOUNT_EXTERNAL_INSTALLER) && mount_storage_dirs) {
@@ -2003,7 +2023,8 @@
         jint mount_external, jstring se_info, jstring nice_name,
         jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
         jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
-        jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) {
+        jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list,
+        jboolean mount_data_dirs, jboolean mount_storage_dirs) {
     jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
 
     if (UNLIKELY(managed_fds_to_close == nullptr)) {
@@ -2041,6 +2062,8 @@
                        mount_external, se_info, nice_name, false,
                        is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
                        is_top_app == JNI_TRUE, pkg_data_info_list,
+                       whitelisted_data_info_list,
+                       mount_data_dirs == JNI_TRUE,
                        mount_storage_dirs == JNI_TRUE);
     }
     return pid;
@@ -2076,7 +2099,8 @@
                        permitted_capabilities, effective_capabilities,
                        MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
                        false, nullptr, nullptr, /* is_top_app= */ false,
-                       /* pkg_data_info_list */ nullptr, false);
+                       /* pkg_data_info_list */ nullptr,
+                       /* whitelisted_data_info_list */ nullptr, false, false);
   } else if (pid > 0) {
       // The zygote process checks whether the child process has died or not.
       ALOGI("System server process %d has been created", pid);
@@ -2206,15 +2230,16 @@
     jint runtime_flags, jobjectArray rlimits,
     jint mount_external, jstring se_info, jstring nice_name,
     jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jboolean is_top_app,
-    jobjectArray pkg_data_info_list, jboolean mount_storage_dirs) {
+    jobjectArray pkg_data_info_list, jobjectArray whitelisted_data_info_list,
+    jboolean mount_data_dirs, jboolean mount_storage_dirs) {
   jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
 
   SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
                    capabilities, capabilities,
                    mount_external, se_info, nice_name, false,
                    is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
-                   is_top_app == JNI_TRUE, pkg_data_info_list,
-                   mount_storage_dirs == JNI_TRUE);
+                   is_top_app == JNI_TRUE, pkg_data_info_list, whitelisted_data_info_list,
+                   mount_data_dirs == JNI_TRUE, mount_storage_dirs == JNI_TRUE);
 }
 
 /**
@@ -2408,7 +2433,7 @@
 static const JNINativeMethod gMethods[] = {
         {"nativeForkAndSpecialize",
          "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/"
-         "String;Z[Ljava/lang/String;Z)I",
+         "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)I",
          (void*)com_android_internal_os_Zygote_nativeForkAndSpecialize},
         {"nativeForkSystemServer", "(II[II[[IJJ)I",
          (void*)com_android_internal_os_Zygote_nativeForkSystemServer},
@@ -2421,7 +2446,7 @@
         {"nativeForkUsap", "(II[IZ)I", (void*)com_android_internal_os_Zygote_nativeForkUsap},
         {"nativeSpecializeAppProcess",
          "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/"
-         "String;Z[Ljava/lang/String;Z)V",
+         "String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V",
          (void*)com_android_internal_os_Zygote_nativeSpecializeAppProcess},
         {"nativeInitNativeState", "(Z)V",
          (void*)com_android_internal_os_Zygote_nativeInitNativeState},
diff --git a/core/proto/android/app/enums.proto b/core/proto/android/app/enums.proto
index 42437d5..563ef14 100644
--- a/core/proto/android/app/enums.proto
+++ b/core/proto/android/app/enums.proto
@@ -203,9 +203,7 @@
     APP_OP_INTERACT_ACROSS_PROFILES = 93;
     APP_OP_ACTIVATE_PLATFORM_VPN = 94;
     APP_OP_LOADER_USAGE_STATS = 95;
-    APP_OP_ACCESS_CALL_AUDIO = 96;
+    APP_OP_DEPRECATED_1 = 96 [deprecated = true];
     APP_OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED = 97;
     APP_OP_AUTO_REVOKE_MANAGED_BY_INSTALLER = 98;
 }
-
-
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index ed2c5b2..ab57e3d 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2665,4 +2665,8 @@
     // Open: Settings > Sound > Do Not Disturb > People > Messages
     // OS: R
     DND_MESSAGES = 1839;
+
+    // Open: Settings > Sound > Do Not Disturb > Apps > <Choose App>
+    // OS: R
+    DND_APPS_BYPASSING = 1840;
 }
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index a3313b2..075aa97 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -77,7 +77,7 @@
         optional SettingProto interactive_ui_timeout_ms = 33 [ (android.privacy).dest = DEST_AUTOMATIC ];
         // Settings for magnification mode
         optional SettingProto accessibility_magnification_mode = 34 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        optional SettingProto button_long_press_targets = 35 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto button_targets = 35 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Accessibility accessibility = 2;
 
@@ -180,6 +180,14 @@
     optional SettingProto cmas_additional_broadcast_pkg = 14 [ (android.privacy).dest = DEST_AUTOMATIC ];
     repeated SettingProto completed_categories = 15;
     optional SettingProto connectivity_release_pending_intent_delay_ms = 16 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Controls {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Controls controls = 79;
+
     optional SettingProto device_paired = 17 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto dialer_default_application = 18 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto display_density_forced = 19 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -580,5 +588,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 79;
+    // Next tag = 80;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 84d9ce2..eae6145 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1213,15 +1213,6 @@
                 android:description="@string/permdesc_acceptHandovers"
                 android:protectionLevel="dangerous" />
 
-    <!-- Allows an application assigned to the Dialer role to be granted access to the telephony
-         call audio streams, both TX and RX.
-         <p>Protection level: signature|appop
-    -->
-    <permission android:name="android.permission.ACCESS_CALL_AUDIO"
-                android.label="@string/permlab_accessCallAudio"
-                android:description="@string/permdesc_accessCallAudio"
-                android:protectionLevel="signature|appop" />
-
     <!-- ====================================================================== -->
     <!-- Permissions for accessing the device microphone                        -->
     <!-- ====================================================================== -->
@@ -3654,6 +3645,16 @@
     <permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Allows an application to use the package installer v2 APIs.
+         <p>The package installer v2 APIs are still a work in progress and we're
+         currently validating they work in all scenarios.
+         <p>Not for use by third-party applications.
+         TODO(b/152310230): remove this permission once the APIs are confirmed to be sufficient.
+         @hide
+    -->
+    <permission android:name="com.android.permission.USE_INSTALLER_V2"
+        android:protectionLevel="signature|verifier" />
+
     <!-- @SystemApi @TestApi Allows an application to clear user data.
          <p>Not for use by third-party applications
          @hide
diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml
index 210feaf..c7e1a21 100644
--- a/core/res/res/layout/resolver_empty_states.xml
+++ b/core/res/res/layout/resolver_empty_states.xml
@@ -22,11 +22,11 @@
     android:orientation="vertical"
     android:gravity="center_horizontal"
     android:visibility="gone"
+    android:paddingTop="48dp"
     android:paddingStart="24dp"
     android:paddingEnd="24dp">
     <ImageView
         android:id="@+id/resolver_empty_state_icon"
-        android:layout_marginTop="48dp"
         android:layout_width="24dp"
         android:layout_height="24dp"
         android:layout_centerHorizontal="true" />
diff --git a/core/res/res/values-h480dp/bools.xml b/core/res/res/values-h480dp/bools.xml
index 65e3ae6..7896d9b 100644
--- a/core/res/res/values-h480dp/bools.xml
+++ b/core/res/res/values-h480dp/bools.xml
@@ -16,5 +16,5 @@
   -->
 
 <resources>
-    <bool name="sharesheet_show_content_preview">true</bool>
+    <bool name="resolver_landscape_phone">false</bool>
 </resources>
\ No newline at end of file
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index d6e200a..2a2da6a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1827,19 +1827,14 @@
 
         <attr name="gwpAsanMode" />
 
-        <!-- If {@code true} allow requesting that its permissions don't get automatically
-             revoked when the app is unused for an extended amount of time.
-
-             The default value is {@code false}. -->
-        <attr name="requestAutoRevokePermissionsExemption" format="boolean" />
-
-        <!-- If {@code true} its permissions shouldn't get automatically
-             revoked when the app is unused for an extended amount of time.
-
-             This implies {@code requestDontAutoRevokePermissions=true}
-
-             The default value is {@code false}. -->
+        <!-- @hide no longer used, kept to preserve padding -->
         <attr name="allowAutoRevokePermissionsExemption" format="boolean" />
+
+        <attr name="autoRevokePermissions">
+            <enum name="allowed" value="0" />
+            <enum name="discouraged" value="1" />
+            <enum name="disallowed" value="2" />
+        </attr>
     </declare-styleable>
 
     <!-- An attribution is a logical part of an app and is identified by a tag.
@@ -2121,35 +2116,6 @@
     <declare-styleable name="AndroidManifestQueriesProvider" parent="AndroidManifestQueries" >
         <attr name="authorities" />
     </declare-styleable>
-    <!--
-        Matches an overlayable, its overlays, its actor, and/or its containing target.
-        A target or actor must always be specified, but can be combined for more specificity.
-        Valid combinations and what they match are:
-
-        targetPackage:
-         - All overlays targeting any overlayables inside 'targetPackage'
-
-        targetPackage + targetName:
-         - All overlays targeting the overlayable 'targetName' inside 'targetPackage'
-
-        targetPackage + targetName + actor:
-         - All overlays targeting the overlayable 'targetName' inside 'targetPackage' if the
-           overlayable specifies 'actor'
-
-        targetPackage + actor:
-         - All overlays targeting overlayables inside 'targetPackage' that specify `actor`
-         - The actor itself if the above matches
-
-        actor:
-         - All overlays targeting overlayables that specify `actor`
-         - All targets that contain an overlayable that specifies `actor`
-         - The actor itself
-    -->
-    <declare-styleable name="AndroidManifestQueriesOverlayable">
-        <attr name="targetPackage" />
-        <attr name="targetName"/>
-        <attr name="actor" format="string" />
-    </declare-styleable>
 
 
     <!-- The <code>static-library</code> tag declares that this apk is providing itself
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index c5127dc..fe296c7 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -29,5 +29,5 @@
          <p>The main purpose is for OEMs to customize the rendering of the
          lockscreen, setting this to true should come with customized drawables. -->
     <bool name="use_lock_pattern_drawable">false</bool>
-    <bool name="sharesheet_show_content_preview">false</bool>
+    <bool name="resolver_landscape_phone">true</bool>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e3a7337..acda77f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2779,6 +2779,10 @@
     <string name="config_systemUIServiceComponent" translatable="false"
             >com.android.systemui/com.android.systemui.SystemUIService</string>
 
+    <!-- Package handling Quick controls -->
+    <string name="config_controlsPackage" translatable="false"
+            >com.android.systemui</string>
+
     <!-- Keyguard component -->
     <string name="config_keyguardComponent" translatable="false"
             >com.android.systemui/com.android.systemui.keyguard.KeyguardService</string>
@@ -4424,4 +4428,7 @@
     <!-- Set to true to enable the user switcher on the keyguard. -->
     <bool name="config_keyguardUserSwitcher">false</bool>
 
+    <!-- Set to true to make assistant show in front of the dream/screensaver. -->
+    <bool name="config_assistantOnTopOfDream">false</bool>
+
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index f02d54f..67d20da 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3014,12 +3014,12 @@
       <!-- @hide @SystemApi -->
       <public name="minExtensionVersion" />
       <public name="allowNativeHeapPointerTagging" />
-      <public name="requestAutoRevokePermissionsExemption" />
-      <public name="allowAutoRevokePermissionsExemption" />
+      <!-- @hide no longer used, kept to preserve padding -->
+      <public name="allowAutoRevokePermissionsExemption"/>
+      <public name="autoRevokePermissions" />
       <public name="preserveLegacyExternalStorage" />
       <public name="mimeGroup" />
       <public name="gwpAsanMode" />
-      <public name="actor" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 06f7760..e2e65dd 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1138,17 +1138,17 @@
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_accessFineLocation">access precise location only in the foreground</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_accessFineLocation">This app can get your exact location only when it is in the foreground. Location services must be turned on and available on your device for the app to be able to use them. This may increase battery consumption.</string>
+    <string name="permdesc_accessFineLocation">This app can get your precise location from location services while the app is in use. Location services for your device must be turned on for the app to get location. This may increase battery usage.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_accessCoarseLocation">access approximate location only in the foreground</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_accessCoarseLocation">This app can get your approximate location only when it is in the foreground. Location services must be turned on and available on your device for the app to be able to use them.</string>
+    <string name="permdesc_accessCoarseLocation">This app can get your approximate location from location services while the app is in use. Location services for your device must be turned on for the app to get location.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_accessBackgroundLocation">access location in the background</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_accessBackgroundLocation">This app can access location while running in the background, in addition to foreground location access.</string>
+    <string name="permdesc_accessBackgroundLocation">This app can access location at any time, even while the app is not in use.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_modifyAudioSettings">change your audio settings</string>
@@ -3597,6 +3597,8 @@
     <string name="ext_media_new_notification_title">New <xliff:g id="name" example="SD card">%s</xliff:g></string>
     <!-- Notification body when new external media is detected [CHAR LIMIT=NONE] -->
     <string name="ext_media_new_notification_message">Tap to set up</string>
+    <!-- Automotive specific notification body when new external media is detected. Empty because there is no fix action (b/151671685) [CHAR LIMIT=NONE] -->
+    <string name="ext_media_new_notification_message" product="automotive"></string>
 
     <!-- Notification body when external media is ready for use [CHAR LIMIT=NONE] -->
     <string name="ext_media_ready_notification_message">For transferring photos and media</string>
@@ -3605,8 +3607,10 @@
     <string name="ext_media_unmountable_notification_title">Issue with <xliff:g id="name" example="SD card">%s</xliff:g></string>
     <!-- Notification body when external media is unmountable (corrupt) [CHAR LIMIT=NONE] -->
     <string name="ext_media_unmountable_notification_message">Tap to fix</string>
-    <!-- TV-specifiv notification body when external media is unmountable (corrupt) [CHAR LIMIT=NONE] -->
+    <!-- TV-specific notification body when external media is unmountable (corrupt) [CHAR LIMIT=NONE] -->
     <string name="ext_media_unmountable_notification_message" product="tv"><xliff:g id="name" example="SD card">%s</xliff:g> is corrupt. Select to fix.</string>
+    <!-- Automotive specific notification body when external media is unmountable (corrupt). Empty because there is no fix action (b/151671685) [CHAR LIMIT=NONE] -->
+    <string name="ext_media_unmountable_notification_message" product="automotive"></string>
 
     <!-- Notification title when external media is unsupported [CHAR LIMIT=30] -->
     <string name="ext_media_unsupported_notification_title">Unsupported <xliff:g id="name" example="SD card">%s</xliff:g></string>
@@ -3614,6 +3618,8 @@
     <string name="ext_media_unsupported_notification_message">This device doesn\u2019t support this <xliff:g id="name" example="SD card">%s</xliff:g>. Tap to set up in a supported format.</string>
     <!-- TV-specific notification body when external media is unsupported [CHAR LIMIT=NONE] -->
     <string name="ext_media_unsupported_notification_message" product="tv">This device doesn\u2019t support this <xliff:g id="name" example="SD card">%s</xliff:g>. Select to set up in a supported format.</string>
+    <!-- Automotive specific notification body when external media is unsupported. No action is specified to fix (b/151671685) [CHAR LIMIT=NONE] -->
+    <string name="ext_media_unsupported_notification_message" product="automotive">This device doesn\u2019t support this <xliff:g id="name" example="SD card">%s</xliff:g>.</string>
 
     <!-- Notification title when external media is unsafely removed [CHAR LIMIT=30] -->
     <string name="ext_media_badremoval_notification_title"><xliff:g id="name" example="SD card">%s</xliff:g> unexpectedly removed</string>
@@ -5482,11 +5488,6 @@
     <!-- Error message. This text lets the user know that their current personal apps can't open this specific content. [CHAR LIMIT=NONE] -->
     <string name="resolver_no_personal_apps_available_resolve">No personal apps can open this content</string>
 
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] -->
-    <string name="permlab_accessCallAudio">Record or play audio in telephony calls</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] -->
-    <string name="permdesc_accessCallAudio">Allows this app, when assigned as default dialer application, to record or play audio in telephony calls.</string>
-
     <!-- Icc depersonalization related strings -->
     <!-- Label text for PIN entry widget on SIM Network Depersonalization panel [CHAR LIMIT=none] -->
     <string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY">SIM network unlock PIN</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0adef75..04c6a41 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -365,6 +365,7 @@
   <java-symbol type="bool" name="config_hasRecents" />
   <java-symbol type="string" name="config_recentsComponentName" />
   <java-symbol type="string" name="config_systemUIServiceComponent" />
+  <java-symbol type="string" name="config_controlsPackage" />
   <java-symbol type="string" name="config_screenRecorderComponent" />
   <java-symbol type="string" name="config_somnambulatorComponent" />
   <java-symbol type="string" name="config_screenshotServiceComponent" />
@@ -3929,7 +3930,7 @@
   <java-symbol type="dimen" name="resolver_empty_state_height" />
   <java-symbol type="dimen" name="resolver_empty_state_height_with_tabs" />
   <java-symbol type="dimen" name="resolver_max_collapsed_height_with_tabs" />
-  <java-symbol type="bool" name="sharesheet_show_content_preview" />
+  <java-symbol type="bool" name="resolver_landscape_phone" />
   <java-symbol type="dimen" name="resolver_tab_text_size" />
 
   <!-- Toast message for background started foreground service while-in-use permission restriction feature -->
@@ -3953,4 +3954,7 @@
 
   <!-- Set to true to enable the user switcher on the keyguard. -->
   <java-symbol type="bool" name="config_keyguardUserSwitcher" />
+
+  <!-- Set to true to make assistant show in front of the dream/screensaver. -->
+  <java-symbol type="bool" name="config_assistantOnTopOfDream"/>
 </resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 2ef0c92..88f9fc2 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -702,7 +702,7 @@
     </style>
 
     <style name="Theme.Dream">
-        <item name="windowBackground">@null</item>
+        <item name="windowBackground">@color/black</item>
         <item name="windowDisablePreview">true</item>
     </style>
 
diff --git a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java b/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java
index 79b803a..0cd0643e 100644
--- a/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java
+++ b/core/tests/coretests/BstatsTestApp/src/com/android/coretests/apps/bstatstestapp/TestService.java
@@ -25,13 +25,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Color;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
-import android.util.Size;
 import android.view.Display;
 import android.view.Gravity;
 import android.view.View;
@@ -119,15 +119,15 @@
         @Override
         public void showApplicationOverlay() throws RemoteException {
             final WindowManager wm = mOverlayContext.getSystemService(WindowManager.class);
-            final Size size = wm.getCurrentWindowMetrics().getSize();
+            final Rect bounds = wm.getCurrentWindowMetrics().getBounds();
 
             final WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(
                     TYPE_APPLICATION_OVERLAY,
                     WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                             | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                             | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
-            wmlp.width = size.getWidth() / 2;
-            wmlp.height = size.getHeight() / 2;
+            wmlp.width = bounds.width() / 2;
+            wmlp.height = bounds.height() / 2;
             wmlp.gravity = Gravity.CENTER | Gravity.LEFT;
             wmlp.setTitle(TAG);
 
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 1737bd0..f48e666 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -236,10 +237,28 @@
     }
 
     @Test
+    public void testGetType_providerException() {
+        String type =
+                mResolver.getType(Uri.parse("content://android.content.FakeProviderRemote/error"));
+        assertThat(type).isNull();
+    }
+
+    @Test
     public void testCanonicalize() {
         Uri canonical = mResolver.canonicalize(
                 Uri.parse("content://android.content.FakeProviderRemote/something"));
         assertThat(canonical).isEqualTo(
                 Uri.parse("content://android.content.FakeProviderRemote/canonical"));
     }
+
+    @Test
+    public void testCanonicalize_providerException() {
+        try {
+            mResolver.canonicalize(
+                    Uri.parse("content://android.content.FakeProviderRemote/error"));
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // Expected
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/content/FakeProviderRemote.java b/core/tests/coretests/src/android/content/FakeProviderRemote.java
index 1d7ba5d..8bc5660 100644
--- a/core/tests/coretests/src/android/content/FakeProviderRemote.java
+++ b/core/tests/coretests/src/android/content/FakeProviderRemote.java
@@ -37,6 +37,9 @@
 
     @Override
     public String getType(Uri uri) {
+        if (uri.getPath() != null && uri.getPath().contains("error")) {
+            throw new IllegalArgumentException("Expected exception");
+        }
         return "fake/remote";
     }
 
@@ -57,6 +60,9 @@
 
     @Override
     public Uri canonicalize(Uri uri) {
+        if (uri.getPath() != null && uri.getPath().contains("error")) {
+            throw new IllegalArgumentException("Expected exception");
+        }
         return new Uri.Builder().scheme(uri.getScheme()).authority(uri.getAuthority())
                 .appendPath("canonical").build();
     }
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index df5c9d2..4114b28 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -184,7 +184,7 @@
     @SmallTest
     public void testThemesGetUpdatedWithNewImpl() {
         Binder activity1 = new Binder();
-        Resources resources1 = mResourcesManager.createBaseActivityResources(
+        Resources resources1 = mResourcesManager.createBaseTokenResources(
                 activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources1);
@@ -217,7 +217,7 @@
         // Create a Resources for the Activity.
         Configuration config1 = new Configuration();
         config1.densityDpi = 280;
-        Resources resources1 = mResourcesManager.createBaseActivityResources(
+        Resources resources1 = mResourcesManager.createBaseTokenResources(
                 activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, config1,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources1);
diff --git a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
index 13000e9..f4ebe2f 100644
--- a/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
+++ b/core/tests/coretests/src/android/service/controls/ControlProviderServiceTest.java
@@ -64,8 +64,7 @@
 @RunWith(AndroidJUnit4.class)
 public class ControlProviderServiceTest {
 
-    private static final ComponentName TEST_SYSUI_COMPONENT =
-            ComponentName.unflattenFromString("sysui/.test.cls");
+    private static final String TEST_CONTROLS_PACKAGE = "sysui";
     private static final ComponentName TEST_COMPONENT =
             ComponentName.unflattenFromString("test.pkg/.test.cls");
 
@@ -97,8 +96,8 @@
         when(mSubscriber.asBinder()).thenCallRealMethod();
         when(mSubscriber.queryLocalInterface(any())).thenReturn(mSubscriber);
 
-        when(mResources.getString(com.android.internal.R.string.config_systemUIServiceComponent))
-                .thenReturn(TEST_SYSUI_COMPONENT.flattenToString());
+        when(mResources.getString(com.android.internal.R.string.config_controlsPackage))
+                .thenReturn(TEST_CONTROLS_PACKAGE);
         when(mContext.getResources()).thenReturn(mResources);
 
         Bundle b = new Bundle();
@@ -252,7 +251,7 @@
                 eq(Manifest.permission.BIND_CONTROLS));
         Intent intent = mIntentArgumentCaptor.getValue();
         assertEquals(ControlsProviderService.ACTION_ADD_CONTROL, intent.getAction());
-        assertEquals(TEST_SYSUI_COMPONENT.getPackageName(), intent.getPackage());
+        assertEquals(TEST_CONTROLS_PACKAGE, intent.getPackage());
         assertEquals(TEST_COMPONENT, intent.getParcelableExtra(Intent.EXTRA_COMPONENT_NAME));
         assertTrue(equals(control,
                 intent.getParcelableExtra(ControlsProviderService.EXTRA_CONTROL)));
diff --git a/core/tests/coretests/src/android/util/GridScenario.java b/core/tests/coretests/src/android/util/GridScenario.java
index 4809a21..e7ee1cd 100644
--- a/core/tests/coretests/src/android/util/GridScenario.java
+++ b/core/tests/coretests/src/android/util/GridScenario.java
@@ -233,7 +233,7 @@
         // turn off title bar
         requestWindowFeature(Window.FEATURE_NO_TITLE);
 
-        mScreenHeight = getWindowManager().getCurrentWindowMetrics().getSize().getHeight();
+        mScreenHeight = getWindowManager().getCurrentWindowMetrics().getBounds().height();
 
         final Params params = new Params();
         init(params);
diff --git a/core/tests/coretests/src/android/util/ListScenario.java b/core/tests/coretests/src/android/util/ListScenario.java
index d4e5a43..74dc4b4 100644
--- a/core/tests/coretests/src/android/util/ListScenario.java
+++ b/core/tests/coretests/src/android/util/ListScenario.java
@@ -306,7 +306,7 @@
         requestWindowFeature(Window.FEATURE_NO_TITLE);
 
 
-        mScreenHeight = getWindowManager().getCurrentWindowMetrics().getSize().getHeight();
+        mScreenHeight = getWindowManager().getCurrentWindowMetrics().getBounds().height();
 
         final Params params = createParams();
         init(params);
diff --git a/core/tests/coretests/src/android/util/ScrollViewScenario.java b/core/tests/coretests/src/android/util/ScrollViewScenario.java
index 2c0aa73..ab1a642 100644
--- a/core/tests/coretests/src/android/util/ScrollViewScenario.java
+++ b/core/tests/coretests/src/android/util/ScrollViewScenario.java
@@ -239,7 +239,7 @@
 
         // for test stability, turn off title bar
         requestWindowFeature(Window.FEATURE_NO_TITLE);
-        int screenHeight = getWindowManager().getCurrentWindowMetrics().getSize().getHeight()
+        int screenHeight = getWindowManager().getCurrentWindowMetrics().getBounds().height()
                 - 25;
         mLinearLayout = new LinearLayout(this);
         mLinearLayout.setOrientation(LinearLayout.VERTICAL);
diff --git a/core/tests/coretests/src/android/view/BigCache.java b/core/tests/coretests/src/android/view/BigCache.java
index e465a85..3038e79 100644
--- a/core/tests/coretests/src/android/view/BigCache.java
+++ b/core/tests/coretests/src/android/view/BigCache.java
@@ -17,8 +17,8 @@
 package android.view;
 
 import android.app.Activity;
+import android.graphics.Rect;
 import android.os.Bundle;
-import android.util.Size;
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 
@@ -39,9 +39,9 @@
                 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
 
         final int cacheSize = ViewConfiguration.getMaximumDrawingCacheSize();
-        final Size windowSize = getWindowManager().getCurrentWindowMetrics().getSize();
-        final int screenWidth = windowSize.getWidth();
-        final int screenHeight = windowSize.getHeight();
+        final Rect windowBounds = getWindowManager().getCurrentWindowMetrics().getBounds();
+        final int screenWidth = windowBounds.width();
+        final int screenHeight = windowBounds.height();
 
         final View tiny = new View(this);
         tiny.setId(R.id.a);
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 34a1016..cbb379b 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -21,6 +21,7 @@
 import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
 import static android.view.InsetsSourceConsumer.ShowResult.IME_SHOW_DELAYED;
 import static android.view.InsetsSourceConsumer.ShowResult.SHOW_IMMEDIATELY;
+import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -31,6 +32,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -87,7 +89,6 @@
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class InsetsControllerTest {
-
     private InsetsController mController;
     private SurfaceSession mSession = new SurfaceSession();
     private SurfaceControl mLeash;
@@ -638,6 +639,52 @@
         });
     }
 
+    @Test
+    public void testFrameUpdateDuringAnimation() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+
+            mController.onControlsChanged(createSingletonControl(ITYPE_IME));
+
+            // Pretend IME is calling
+            mController.show(ime(), true /* fromIme */);
+
+            InsetsState copy = new InsetsState(mController.getState(), true /* copySources */);
+            copy.getSource(ITYPE_IME).setFrame(0, 1, 2, 3);
+            copy.getSource(ITYPE_IME).setVisibleFrame(new Rect(4, 5, 6, 7));
+            mController.onStateChanged(copy);
+            assertNotEquals(new Rect(0, 1, 2, 3),
+                    mController.getState().getSource(ITYPE_IME).getFrame());
+            assertNotEquals(new Rect(4, 5, 6, 7),
+                    mController.getState().getSource(ITYPE_IME).getVisibleFrame());
+            mController.cancelExistingAnimation();
+            assertEquals(new Rect(0, 1, 2, 3),
+                    mController.getState().getSource(ITYPE_IME).getFrame());
+            assertEquals(new Rect(4, 5, 6, 7),
+                    mController.getState().getSource(ITYPE_IME).getVisibleFrame());
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    @Test
+    public void testCaptionInsetsStateAssemble() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mController.onFrameChanged(new Rect(0, 0, 100, 300));
+            final InsetsState state = new InsetsState(mController.getState(), true);
+            final Rect captionFrame = new Rect(0, 0, 100, 100);
+            mController.setCaptionInsetsHeight(100);
+            mController.onStateChanged(state);
+            final InsetsState currentState = new InsetsState(mController.getState());
+            // The caption bar source should be synced with the info in mAttachInfo.
+            assertEquals(captionFrame, currentState.peekSource(ITYPE_CAPTION_BAR).getFrame());
+            assertTrue(currentState.equals(state, true /* excludingCaptionInsets*/));
+            mController.setCaptionInsetsHeight(0);
+            mController.onStateChanged(state);
+            // The caption bar source should not be there at all, because we don't add empty
+            // caption to the state from the server.
+            assertNull(mController.getState().peekSource(ITYPE_CAPTION_BAR));
+        });
+    }
+
     private void waitUntilNextFrame() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT,
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 721dc98..2884777 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -153,6 +153,35 @@
         }
     }
 
+
+    @Test
+    public void testCalculateInsets_captionStatusBarOverlap() throws Exception {
+        try (InsetsModeSession session =
+                     new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
+            mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+            mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+            mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(0, 0, 100, 300));
+            mState.getSource(ITYPE_CAPTION_BAR).setVisible(true);
+
+            Rect visibleInsets = mState.calculateVisibleInsets(
+                    new Rect(0, 0, 100, 400), SOFT_INPUT_ADJUST_NOTHING);
+            assertEquals(new Rect(0, 300, 0, 0), visibleInsets);
+        }
+    }
+
+    @Test
+    public void testCalculateInsets_captionBarOffset() throws Exception {
+        try (InsetsModeSession session =
+                     new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
+            mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(0, 0, 100, 300));
+            mState.getSource(ITYPE_CAPTION_BAR).setVisible(true);
+
+            Rect visibleInsets = mState.calculateVisibleInsets(
+                    new Rect(0, 0, 150, 400), SOFT_INPUT_ADJUST_NOTHING);
+            assertEquals(new Rect(0, 300, 0, 0), visibleInsets);
+        }
+    }
+
     @Test
     public void testStripForDispatch() {
         mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
diff --git a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
index 039387c..46e55fa 100644
--- a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
+++ b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
@@ -22,7 +22,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.util.Size;
+import android.graphics.Rect;
 import android.widget.TextView;
 
 import androidx.test.filters.LargeTest;
@@ -55,9 +55,9 @@
 
         // Specify start and end coordinates with respect to the window size.
         final WindowManager wm = mScaleGestureActivity.getSystemService(WindowManager.class);
-        final Size windowSize = wm.getCurrentWindowMetrics().getSize();
-        final int windowWidth = windowSize.getWidth();
-        final int windowHeight = windowSize.getHeight();
+        final Rect windowBounds = wm.getCurrentWindowMetrics().getBounds();
+        final int windowWidth = windowBounds.width();
+        final int windowHeight = windowBounds.height();
 
         // Obtain coordinates to perform pinch and zoom from the center, to 75% of the display.
         final int centerX = windowWidth / 2;
diff --git a/core/tests/coretests/src/android/view/WindowMetricsTest.java b/core/tests/coretests/src/android/view/WindowMetricsTest.java
index fa68860..74524bf 100644
--- a/core/tests/coretests/src/android/view/WindowMetricsTest.java
+++ b/core/tests/coretests/src/android/view/WindowMetricsTest.java
@@ -22,10 +22,10 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.Handler;
 import android.platform.test.annotations.Presubmit;
-import android.util.Size;
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
@@ -87,10 +87,12 @@
 
     private static void verifyMetricsSanity(WindowMetrics currentMetrics,
             WindowMetrics maxMetrics) {
-        Size currentSize = currentMetrics.getSize();
-        Size maxSize = maxMetrics.getSize();
+        Rect currentBounds = currentMetrics.getBounds();
+        Rect maxBounds = maxMetrics.getBounds();
 
-        assertTrue(maxSize.getWidth() >= currentSize.getWidth());
-        assertTrue(maxSize.getHeight() >= currentSize.getHeight());
+        assertTrue(maxBounds.width() >= currentBounds.width());
+        assertTrue(maxBounds.height() >= currentBounds.height());
+        assertTrue(maxBounds.left >= 0);
+        assertTrue(maxBounds.top >= 0);
     }
 }
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
index ba27fac..eae1bbc 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
@@ -15,11 +15,12 @@
  */
 package android.view.contentcapture;
 
+import static org.mockito.Mockito.mock;
 import static org.testng.Assert.assertThrows;
 
+import android.content.ContentCaptureOptions;
 import android.content.Context;
 
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -37,16 +38,20 @@
     @Mock
     private Context mMockContext;
 
-    private ContentCaptureManager mManager;
-
-    @Before
-    public void before() {
-        mManager = new ContentCaptureManager(mMockContext, /* service= */ null,
-                /* options= */ null);
+    @Test
+    public void testConstructor_invalidParametersThrowsException() {
+        assertThrows(NullPointerException.class,
+                () -> new ContentCaptureManager(mMockContext, /* service= */ null, /* options= */
+                        null));
     }
 
     @Test
-    public void testRemoveData_invalid() {
-        assertThrows(NullPointerException.class, () -> mManager.removeData(null));
+    public void testRemoveData_invalidParametersThrowsException() {
+        final IContentCaptureManager mockService = mock(IContentCaptureManager.class);
+        final ContentCaptureOptions options = new ContentCaptureOptions(null);
+        final ContentCaptureManager manager =
+                new ContentCaptureManager(mMockContext, mockService, options);
+
+        assertThrows(NullPointerException.class, () -> manager.removeData(null));
     }
 }
diff --git a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
index d5825e2..4bd9ccd 100644
--- a/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
+++ b/core/tests/coretests/src/android/view/menu/ContextMenuTest.java
@@ -16,9 +16,9 @@
 
 package android.view.menu;
 
+import android.graphics.Rect;
 import android.test.ActivityInstrumentationTestCase;
 import android.util.PollingCheck;
-import android.util.Size;
 import android.view.View;
 import android.view.WindowManager;
 import android.widget.espresso.ContextMenuUtils;
@@ -81,8 +81,8 @@
      */
     private int getMinScreenDimension() {
         final WindowManager windowManager = getActivity().getSystemService(WindowManager.class);
-        final Size maxWindowSize = windowManager.getMaximumWindowMetrics().getSize();
-        return Math.min(maxWindowSize.getWidth(), maxWindowSize.getHeight());
+        final Rect maxWindowBounds = windowManager.getMaximumWindowMetrics().getBounds();
+        return Math.min(maxWindowBounds.width(), maxWindowBounds.height());
     }
 
     /**
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index f81964c..4bfffd7 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -16,6 +16,7 @@
 
 package android.widget;
 
+import static android.text.Spanned.SPAN_INCLUSIVE_EXCLUSIVE;
 import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
 import static android.widget.espresso.TextViewActions.dragOnText;
 import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
@@ -36,7 +37,11 @@
 import android.app.Activity;
 import android.app.Instrumentation;
 import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
 import android.text.Layout;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.AbsoluteSizeSpan;
 import android.util.ArraySet;
 import android.util.Log;
 import android.view.InputDevice;
@@ -44,7 +49,7 @@
 import android.view.View;
 
 import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
+import androidx.test.filters.MediumTest;
 import androidx.test.filters.Suppress;
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.AndroidJUnit4;
@@ -63,7 +68,8 @@
 import java.util.concurrent.atomic.AtomicLong;
 
 @RunWith(AndroidJUnit4.class)
-@SmallTest
+@MediumTest
+@Presubmit
 public class EditorCursorDragTest {
     private static final String LOG_TAG = EditorCursorDragTest.class.getSimpleName();
 
@@ -488,6 +494,39 @@
         simulateDrag(tv, events, true);
     }
 
+    @Suppress // b/152574363
+    @Test
+    public void testLineChangeSlop() throws Throwable {
+        TextView tv = mActivity.findViewById(R.id.textview);
+        Spannable s = new SpannableString("a\nb\nc");
+        s.setSpan(new AbsoluteSizeSpan(10), 2, 4, SPAN_INCLUSIVE_EXCLUSIVE);
+        s.setSpan(new AbsoluteSizeSpan(32), 4, 5, SPAN_INCLUSIVE_EXCLUSIVE);
+        mInstrumentation.runOnMainSync(() -> tv.setText(s));
+
+        Layout layout = tv.getLayout();
+        Editor editor = tv.getEditorForTesting();
+        final float verticalOffset = tv.getExtendedPaddingTop();
+        editor.setLineChangeSlopMinMaxForTesting(30, 65);
+        // Hit top part of upper line, jump to upper line.
+        assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 5 + verticalOffset))
+                .isEqualTo(0);
+        // Hit bottom part of upper line, stay at current line.
+        assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 40 + verticalOffset))
+                .isEqualTo(1);
+        // Hit current line, stay at current line.
+        assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 70 + verticalOffset))
+                .isEqualTo(1);
+        // Hit top part of lower line, stay at current line.
+        assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 85 + verticalOffset))
+                .isEqualTo(1);
+        // Hit bottom part of lower line, jump to lower line.
+        assertThat(editor.getCurrentLineAdjustedForSlop(layout, 1, 110 + verticalOffset))
+                .isEqualTo(2);
+        // Hit lower line of lower line, jump to target line.
+        assertThat(editor.getCurrentLineAdjustedForSlop(layout, 0, 110 + verticalOffset))
+                .isEqualTo(2);
+    }
+
     @Test
     public void testCursorDrag_snapDistance() throws Throwable {
         String text = "line1: This is the 1st line: A\n"
diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
index 3dc001d..ec75e40 100644
--- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
+++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
@@ -22,6 +22,7 @@
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 
+import android.platform.test.annotations.Presubmit;
 import android.view.InputDevice;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
@@ -36,6 +37,7 @@
 
 @RunWith(JUnit4.class)
 @SmallTest
+@Presubmit
 public class EditorTouchStateTest {
 
     private EditorTouchState mTouchState;
diff --git a/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java b/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java
index 8e90a82..d51cc32 100644
--- a/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java
+++ b/core/tests/coretests/src/android/widget/focus/ListOfInternalSelectionViews.java
@@ -111,7 +111,7 @@
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        mScreenHeight = getWindowManager().getCurrentWindowMetrics().getSize().getHeight();
+        mScreenHeight = getWindowManager().getCurrentWindowMetrics().getBounds().height();
 
         Bundle extras = getIntent().getExtras();
         if (extras != null) {
diff --git a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java
index fd1dbfc..5cedd13 100644
--- a/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java
+++ b/core/tests/coretests/src/android/widget/gridview/touch/GridTouchVerticalSpacingStackFromBottomTest.java
@@ -106,8 +106,8 @@
 
         int firstTop = firstChild.getTop();
 
-        int windowHeight = mActivity.getWindowManager().getCurrentWindowMetrics().getSize()
-                .getHeight();
+        int windowHeight = mActivity.getWindowManager().getCurrentWindowMetrics().getBounds()
+                .height();
         int distance = TouchUtils.dragViewBy(this, firstChild, 
                 Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, (int) (windowHeight * 0.75f));
         
diff --git a/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java b/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java
index e6195b1..5cca766 100644
--- a/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java
+++ b/core/tests/coretests/src/android/widget/listview/AdjacentListsWithAdjacentISVsInside.java
@@ -66,7 +66,7 @@
         super.onCreate(savedInstanceState);
 
         final int desiredHeight =
-                (int) (0.8 * getWindowManager().getCurrentWindowMetrics().getSize().getHeight());
+                (int) (0.8 * getWindowManager().getCurrentWindowMetrics().getBounds().height());
 
         mLeftListView = new ListView(this);
         mLeftListView.setAdapter(new AdjacentISVAdapter(desiredHeight));
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 9af0ed0..4a33da6 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -17,7 +17,6 @@
 package com.android.internal.accessibility;
 
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN;
-import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED;
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN;
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
@@ -102,10 +101,8 @@
     private static final long[] VIBRATOR_PATTERN_LONG = {VIBRATOR_PATTERN_1, VIBRATOR_PATTERN_2};
 
     // Convenience values for enabling/disabling to make code more readable
-    private static final int DISABLED = 0;
     private static final int ENABLED_EXCEPT_LOCK_SCREEN = 1;
     private static final int ENABLED_INCLUDING_LOCK_SCREEN = 2;
-    private static final int DISABLED_BUT_LOCK_SCREEN_ON = 3;
 
     private @Mock Context mContext;
     private @Mock FrameworkObjectProvider mFrameworkObjectProvider;
@@ -225,14 +222,6 @@
     }
 
     @Test
-    public void testShortcutAvailable_disabledWithValidServiceWhenCreated_shouldReturnFalse()
-            throws Exception {
-        configureValidShortcutService();
-        configureShortcutEnabled(DISABLED_BUT_LOCK_SCREEN_ON);
-        assertFalse(getController().isAccessibilityShortcutAvailable(false));
-    }
-
-    @Test
     public void testShortcutAvailable_onLockScreenButDisabledThere_shouldReturnFalse()
             throws Exception {
         configureValidShortcutService();
@@ -285,20 +274,8 @@
     }
 
     @Test
-    public void testShortcutAvailable_whenShortcutBecomesDisabled_shouldReturnFalse()
-            throws Exception {
-        configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
-        configureValidShortcutService();
-        AccessibilityShortcutController accessibilityShortcutController = getController();
-        configureShortcutEnabled(DISABLED);
-        accessibilityShortcutController.onSettingsChanged();
-        assertFalse(accessibilityShortcutController.isAccessibilityShortcutAvailable(false));
-    }
-
-    @Test
     public void testShortcutAvailable_whenShortcutBecomesEnabled_shouldReturnTrue()
             throws Exception {
-        configureShortcutEnabled(DISABLED);
         configureValidShortcutService();
         AccessibilityShortcutController accessibilityShortcutController = getController();
         configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
@@ -594,31 +571,19 @@
     }
 
     private void configureShortcutEnabled(int enabledValue) {
-        final boolean enabled;
         final boolean lockscreen;
 
         switch (enabledValue) {
-            case DISABLED:
-                enabled = false;
-                lockscreen = false;
-                break;
-            case DISABLED_BUT_LOCK_SCREEN_ON:
-                enabled = false;
-                lockscreen = true;
-                break;
             case ENABLED_INCLUDING_LOCK_SCREEN:
-                enabled = true;
                 lockscreen = true;
                 break;
             case ENABLED_EXCEPT_LOCK_SCREEN:
-                enabled = true;
                 lockscreen = false;
                 break;
             default:
                 throw new IllegalArgumentException();
         }
 
-        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_ENABLED, enabled ? 1 : 0);
         Settings.Secure.putInt(
                 mContentResolver, ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, lockscreen ? 1 : 0);
     }
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index 5c89da0..98f5824 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -61,7 +61,6 @@
   <hidden-api-whitelisted-app package="com.android.terminal" />
   <hidden-api-whitelisted-app package="com.android.wallpaper" />
   <hidden-api-whitelisted-app package="jp.co.omronsoft.openwnn" />
-  <!-- STOPSHIP: Remove this when fixing all @hide usage for tethering.-->
-  <hidden-api-whitelisted-app package="com.android.networkstack.tethering" />
+  <!-- TODO: Remove NetworkStack whitelisting -->
   <hidden-api-whitelisted-app package="com.android.networkstack" />
 </config>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 1efde86..59bdf3d 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -27,6 +27,10 @@
         <permission name="android.permission.INTERACT_ACROSS_USERS" />
     </privapp-permissions>
 
+    <privapp-permissions package="com.android.angle">
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+    </privapp-permissions>
+
     <privapp-permissions package="com.android.apps.tag">
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
     </privapp-permissions>
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 746378e..31ad81b 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -975,6 +975,7 @@
 
                 mChangingConfigurations = orig.mChangingConfigurations;
                 mChildrenChangingConfigurations = orig.mChildrenChangingConfigurations;
+                mSourceDrawableId = orig.mSourceDrawableId;
 
                 for (int i = 0; i < N_CHILDREN; i++) {
                     final ChildDrawable or = origChildDrawable[i];
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index f87f98a..02c85aa 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -44,7 +44,6 @@
         "AttributeResolution.cpp",
         "ChunkIterator.cpp",
         "ConfigDescription.cpp",
-        "DynamicLibManager.cpp",
         "Idmap.cpp",
         "LoadedArsc.cpp",
         "Locale.cpp",
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index f20e184..eaf452b 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -25,7 +25,6 @@
 
 #include "android-base/logging.h"
 #include "android-base/stringprintf.h"
-#include "androidfw/DynamicLibManager.h"
 #include "androidfw/ResourceUtils.h"
 #include "androidfw/Util.h"
 #include "utils/ByteOrder.h"
@@ -67,12 +66,7 @@
   StringPoolRef entry_string_ref;
 };
 
-AssetManager2::AssetManager2() : dynamic_lib_manager_(std::make_unique<DynamicLibManager>()) {
-  memset(&configuration_, 0, sizeof(configuration_));
-}
-
-AssetManager2::AssetManager2(DynamicLibManager* dynamic_lib_manager)
-    : dynamic_lib_manager_(dynamic_lib_manager) {
+AssetManager2::AssetManager2() {
   memset(&configuration_, 0, sizeof(configuration_));
 }
 
@@ -91,6 +85,9 @@
   package_groups_.clear();
   package_ids_.fill(0xff);
 
+  // A mapping from apk assets path to the runtime package id of its first loaded package.
+  std::unordered_map<std::string, uint8_t> apk_assets_package_ids;
+
   // Overlay resources are not directly referenced by an application so their resource ids
   // can change throughout the application's lifetime. Assign overlay package ids last.
   std::vector<const ApkAssets*> sorted_apk_assets(apk_assets_);
@@ -98,37 +95,25 @@
     return !a->IsOverlay();
   });
 
-  std::unordered_map<std::string, uint8_t> apk_assets_package_ids;
-  std::unordered_map<std::string, uint8_t> package_name_package_ids;
-
-  // Assign stable package ids to application packages.
-  uint8_t next_available_package_id = 0U;
-  for (const auto& apk_assets : sorted_apk_assets) {
-    for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) {
-      uint8_t package_id = package->GetPackageId();
-      if (package->IsOverlay()) {
-        package_id = GetDynamicLibManager()->FindUnassignedId(next_available_package_id);
-        next_available_package_id = package_id + 1;
-      } else if (package->IsDynamic()) {
-        package_id = GetDynamicLibManager()->GetAssignedId(package->GetPackageName());
-      }
-
-      // Map the path of the apk assets to the package id of its first loaded package.
-      apk_assets_package_ids[apk_assets->GetPath()] = package_id;
-
-      // Map the package name of the package to the first loaded package with that package id.
-      package_name_package_ids[package->GetPackageName()] = package_id;
-    }
+  // The assets cookie must map to the position of the apk assets in the unsorted apk assets list.
+  std::unordered_map<const ApkAssets*, ApkAssetsCookie> apk_assets_cookies;
+  apk_assets_cookies.reserve(apk_assets_.size());
+  for (size_t i = 0, n = apk_assets_.size(); i < n; i++) {
+    apk_assets_cookies[apk_assets_[i]] = static_cast<ApkAssetsCookie>(i);
   }
 
-  const int apk_assets_count = apk_assets_.size();
-  for (int i = 0; i < apk_assets_count; i++) {
-    const auto& apk_assets = apk_assets_[i];
-    for (const auto& package : apk_assets->GetLoadedArsc()->GetPackages()) {
-      const auto package_id_entry = package_name_package_ids.find(package->GetPackageName());
-      CHECK(package_id_entry != package_name_package_ids.end())
-          << "no package id assgined to package " << package->GetPackageName();
-      const uint8_t package_id = package_id_entry->second;
+  // 0x01 is reserved for the android package.
+  int next_package_id = 0x02;
+  for (const ApkAssets* apk_assets : sorted_apk_assets) {
+    const LoadedArsc* loaded_arsc = apk_assets->GetLoadedArsc();
+    for (const std::unique_ptr<const LoadedPackage>& package : loaded_arsc->GetPackages()) {
+      // Get the package ID or assign one if a shared library.
+      int package_id;
+      if (package->IsDynamic()) {
+        package_id = next_package_id++;
+      } else {
+        package_id = package->GetPackageId();
+      }
 
       // Add the mapping for package ID to index if not present.
       uint8_t idx = package_ids_[package_id];
@@ -162,7 +147,7 @@
             target_package_group.overlays_.push_back(
                 ConfiguredOverlay{loaded_idmap->GetTargetResourcesMap(target_package_id,
                                                                       overlay_table.get()),
-                                  static_cast<ApkAssetsCookie>(i)});
+                                  apk_assets_cookies[apk_assets]});
           }
         }
 
@@ -174,7 +159,7 @@
 
       // Add the package and to the set of packages with the same ID.
       package_group->packages_.push_back(ConfiguredPackage{package.get(), {}});
-      package_group->cookies_.push_back(static_cast<ApkAssetsCookie>(i));
+      package_group->cookies_.push_back(apk_assets_cookies[apk_assets]);
 
       // Add the package name -> build time ID mappings.
       for (const DynamicPackageEntry& entry : package->GetDynamicPackageMap()) {
@@ -182,6 +167,8 @@
         package_group->dynamic_ref_table->mEntries.replaceValueFor(
             package_name, static_cast<uint8_t>(entry.package_id));
       }
+
+      apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id));
     }
   }
 
@@ -1329,16 +1316,6 @@
   return 0;
 }
 
-DynamicLibManager* AssetManager2::GetDynamicLibManager() const {
-  auto dynamic_lib_manager =
-      std::get_if<std::unique_ptr<DynamicLibManager>>(&dynamic_lib_manager_);
-  if (dynamic_lib_manager) {
-    return (*dynamic_lib_manager).get();
-  } else {
-    return *std::get_if<DynamicLibManager*>(&dynamic_lib_manager_);
-  }
-}
-
 std::unique_ptr<Theme> AssetManager2::NewTheme() {
   return std::unique_ptr<Theme>(new Theme(this));
 }
diff --git a/libs/androidfw/DynamicLibManager.cpp b/libs/androidfw/DynamicLibManager.cpp
deleted file mode 100644
index 895b769..0000000
--- a/libs/androidfw/DynamicLibManager.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#include "androidfw/DynamicLibManager.h"
-
-namespace android {
-
-uint8_t DynamicLibManager::GetAssignedId(const std::string& library_package_name) {
-  auto lib_entry = shared_lib_package_ids_.find(library_package_name);
-  if (lib_entry != shared_lib_package_ids_.end()) {
-    return lib_entry->second;
-  }
-
-  return shared_lib_package_ids_[library_package_name] = next_package_id_++;
-}
-
-uint8_t DynamicLibManager::FindUnassignedId(uint8_t start_package_id) {
-  return (start_package_id < next_package_id_) ? next_package_id_ : start_package_id;
-}
-
-} // namespace android
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index b2cec2a..e21abad 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -27,7 +27,6 @@
 #include "androidfw/ApkAssets.h"
 #include "androidfw/Asset.h"
 #include "androidfw/AssetManager.h"
-#include "androidfw/DynamicLibManager.h"
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/Util.h"
 
@@ -95,7 +94,6 @@
   };
 
   AssetManager2();
-  explicit AssetManager2(DynamicLibManager* dynamic_lib_manager);
 
   // Sets/resets the underlying ApkAssets for this AssetManager. The ApkAssets
   // are not owned by the AssetManager, and must have a longer lifetime.
@@ -126,6 +124,9 @@
   // This may be nullptr if the APK represented by `cookie` has no resource table.
   std::shared_ptr<const DynamicRefTable> GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const;
 
+  // Retrieve the assigned package id of the package if loaded into this AssetManager
+  uint8_t GetAssignedPackageId(const LoadedPackage* package) const;
+
   // Returns a string representation of the overlayable API of a package.
   bool GetOverlayablesToString(const android::StringPiece& package_name,
                                std::string* out) const;
@@ -370,11 +371,6 @@
   // been seen while traversing bag parents.
   const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids);
 
-  // Retrieve the assigned package id of the package if loaded into this AssetManager
-  uint8_t GetAssignedPackageId(const LoadedPackage* package) const;
-
-  DynamicLibManager* GetDynamicLibManager() const;
-
   // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must
   // have a longer lifetime.
   std::vector<const ApkAssets*> apk_assets_;
@@ -393,9 +389,6 @@
   // may need to be purged.
   ResTable_config configuration_;
 
-  // Component responsible for assigning package ids to shared libraries.
-  std::variant<std::unique_ptr<DynamicLibManager>, DynamicLibManager*> dynamic_lib_manager_;
-
   // Cached set of bags. These are cached because they can inherit keys from parent bags,
   // which involves some calculation.
   std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_;
diff --git a/libs/androidfw/include/androidfw/DynamicLibManager.h b/libs/androidfw/include/androidfw/DynamicLibManager.h
deleted file mode 100644
index 1ff7079..0000000
--- a/libs/androidfw/include/androidfw/DynamicLibManager.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#ifndef ANDROIDFW_DYNAMICLIBMANAGER_H
-#define ANDROIDFW_DYNAMICLIBMANAGER_H
-
-#include <string>
-#include <unordered_map>
-
-#include "android-base/macros.h"
-
-namespace android {
-
-// Manages assigning resource ids for dynamic resources.
-class DynamicLibManager {
- public:
-  DynamicLibManager() = default;
-
-  // Retrieves the assigned package id for the library.
-  uint8_t GetAssignedId(const std::string& library_package_name);
-
-  // Queries in ascending order for the first available package id that is not currently assigned to
-  // a library.
-  uint8_t FindUnassignedId(uint8_t start_package_id);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DynamicLibManager);
-
-  uint8_t next_package_id_ = 0x02;
-  std::unordered_map<std::string, uint8_t> shared_lib_package_ids_;
-};
-
-} // namespace android
-
-#endif //ANDROIDFW_DYNAMICLIBMANAGER_H
diff --git a/libs/androidfw/include/androidfw/MutexGuard.h b/libs/androidfw/include/androidfw/MutexGuard.h
index 8891512..64924f4 100644
--- a/libs/androidfw/include/androidfw/MutexGuard.h
+++ b/libs/androidfw/include/androidfw/MutexGuard.h
@@ -47,8 +47,7 @@
   static_assert(!std::is_pointer<T>::value, "T must not be a raw pointer");
 
  public:
-  template <typename ...Args>
-  explicit Guarded(Args&& ...args) : guarded_(std::forward<Args>(args)...) {
+  explicit Guarded() : guarded_() {
   }
 
   template <typename U = T>
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index ac32699..8c255d1 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -17,9 +17,9 @@
 #include "androidfw/AssetManager2.h"
 #include "androidfw/AssetManager.h"
 
-#include "android-base/logging.h"
-
 #include "TestHelpers.h"
+#include "android-base/file.h"
+#include "android-base/logging.h"
 #include "androidfw/ResourceUtils.h"
 #include "data/appaslib/R.h"
 #include "data/basic/R.h"
@@ -45,37 +45,43 @@
 class AssetManager2Test : public ::testing::Test {
  public:
   void SetUp() override {
-    basic_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
+    // Move to the test data directory so the idmap can locate the overlay APK.
+    std::string original_path = base::GetExecutableDirectory();
+    chdir(GetTestDataPath().c_str());
+
+    basic_assets_ = ApkAssets::Load("basic/basic.apk");
     ASSERT_NE(nullptr, basic_assets_);
 
-    basic_de_fr_assets_ = ApkAssets::Load(GetTestDataPath() + "/basic/basic_de_fr.apk");
+    basic_de_fr_assets_ = ApkAssets::Load("basic/basic_de_fr.apk");
     ASSERT_NE(nullptr, basic_de_fr_assets_);
 
-    style_assets_ = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+    style_assets_ = ApkAssets::Load("styles/styles.apk");
     ASSERT_NE(nullptr, style_assets_);
 
-    lib_one_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_one/lib_one.apk");
+    lib_one_assets_ = ApkAssets::Load("lib_one/lib_one.apk");
     ASSERT_NE(nullptr, lib_one_assets_);
 
-    lib_two_assets_ = ApkAssets::Load(GetTestDataPath() + "/lib_two/lib_two.apk");
+    lib_two_assets_ = ApkAssets::Load("lib_two/lib_two.apk");
     ASSERT_NE(nullptr, lib_two_assets_);
 
-    libclient_assets_ = ApkAssets::Load(GetTestDataPath() + "/libclient/libclient.apk");
+    libclient_assets_ = ApkAssets::Load("libclient/libclient.apk");
     ASSERT_NE(nullptr, libclient_assets_);
 
-    appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk",
-                                       PROPERTY_DYNAMIC);
+    appaslib_assets_ = ApkAssets::Load("appaslib/appaslib.apk", PROPERTY_DYNAMIC);
     ASSERT_NE(nullptr, appaslib_assets_);
 
-    system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk",
-                                     PROPERTY_SYSTEM);
+    system_assets_ = ApkAssets::Load("system/system.apk", PROPERTY_SYSTEM);
     ASSERT_NE(nullptr, system_assets_);
 
-    app_assets_ = ApkAssets::Load(GetTestDataPath() + "/app/app.apk");
+    app_assets_ = ApkAssets::Load("app/app.apk");
     ASSERT_THAT(app_assets_, NotNull());
 
-    overlayable_assets_ = ApkAssets::Load(GetTestDataPath() + "/overlayable/overlayable.apk");
+    overlay_assets_ = ApkAssets::LoadOverlay("overlay/overlay.idmap");
+    ASSERT_NE(nullptr, overlay_assets_);
+
+    overlayable_assets_ = ApkAssets::Load("overlayable/overlayable.apk");
     ASSERT_THAT(overlayable_assets_, NotNull());
+    chdir(original_path.c_str());
   }
 
  protected:
@@ -88,6 +94,7 @@
   std::unique_ptr<const ApkAssets> appaslib_assets_;
   std::unique_ptr<const ApkAssets> system_assets_;
   std::unique_ptr<const ApkAssets> app_assets_;
+  std::unique_ptr<const ApkAssets> overlay_assets_;
   std::unique_ptr<const ApkAssets> overlayable_assets_;
 };
 
@@ -216,23 +223,24 @@
   EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data);
 }
 
-TEST_F(AssetManager2Test, AssignsUnchangingPackageIdToSharedLibrary) {
-  DynamicLibManager lib_manager;
-  AssetManager2 assetmanager(&lib_manager);
+TEST_F(AssetManager2Test, AssignsOverlayPackageIdLast) {
+  AssetManager2 assetmanager;
   assetmanager.SetApkAssets(
-      {lib_one_assets_.get(), lib_two_assets_.get(), libclient_assets_.get()});
+      {overlayable_assets_.get(), overlay_assets_.get(), lib_one_assets_.get()});
 
-  AssetManager2 assetmanager2(&lib_manager);
-  assetmanager2.SetApkAssets(
-      {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
+  auto apk_assets = assetmanager.GetApkAssets();
+  ASSERT_EQ(3, apk_assets.size());
+  ASSERT_EQ(overlayable_assets_.get(), apk_assets[0]);
+  ASSERT_EQ(overlay_assets_.get(), apk_assets[1]);
+  ASSERT_EQ(lib_one_assets_.get(), apk_assets[2]);
 
-  uint32_t res_id = assetmanager.GetResourceId("com.android.lib_one:string/foo");
-  ASSERT_NE(0U, res_id);
+  auto get_first_package_id = [&assetmanager](const ApkAssets* apkAssets) -> uint8_t {
+    return assetmanager.GetAssignedPackageId(apkAssets->GetLoadedArsc()->GetPackages()[0].get());
+  };
 
-  uint32_t res_id_2 = assetmanager2.GetResourceId("com.android.lib_one:string/foo");
-  ASSERT_NE(0U, res_id_2);
-
-  ASSERT_EQ(res_id, res_id_2);
+  ASSERT_EQ(get_first_package_id(overlayable_assets_.get()), 0x7f);
+  ASSERT_EQ(get_first_package_id(overlay_assets_.get()), 0x03);
+  ASSERT_EQ(get_first_package_id(lib_one_assets_.get()), 0x02);
 }
 
 TEST_F(AssetManager2Test, GetSharedLibraryResourceName) {
@@ -770,7 +778,6 @@
   ASSERT_EQ(api.find("not_overlayable"), std::string::npos);
   ASSERT_NE(api.find("resource='com.android.overlayable:string/overlayable2' overlayable='OverlayableResources1' actor='overlay://theme' policy='0x0000000a'\n"),
             std::string::npos);
-
 }
 
 }  // namespace android
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 9950f05..db2a1e8 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -432,7 +432,7 @@
      * @return An array of supported encapsulation modes for the device.  This
      *     may be an empty array if no encapsulation modes are supported.
      */
-    public @NonNull int[] getEncapsulationModes() {
+    public @NonNull @AudioTrack.EncapsulationMode int[] getEncapsulationModes() {
         // Implement a getter in r-dev or r-tv-dev as needed.
         return new int[0];  // be careful of returning a copy of any internal data.
     }
@@ -451,7 +451,7 @@
      * @return An array of supported encapsulation metadata types for the device.  This
      *     may be an empty array if no metadata types are supported.
      */
-    public @NonNull int[] getEncapsulationMetadataTypes() {
+    public @NonNull @AudioTrack.EncapsulationMetadataType int[] getEncapsulationMetadataTypes() {
         // Implement a getter in r-dev or r-tv-dev as needed.
         return new int[0];  // be careful of returning a copy of any internal data.
     }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 383202b..ba8f7e6 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4579,6 +4579,7 @@
      * {@hide}
      */
     @UnsupportedAppUsage
+    @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public void setWiredDeviceConnectionState(int type, int state, String address, String name) {
         final IAudioService service = getService();
         try {
@@ -4768,7 +4769,7 @@
      * opened on that device.
      *
      * @param device an instance of {@link AudioDeviceInfo} returned from {@link getDevices()}.
-     * @param delayMs delay in milliseconds desired.  This should be in range of {@code 0}
+     * @param delayMillis delay in milliseconds desired.  This should be in range of {@code 0}
      *     to the value returned by {@link #getMaxAdditionalOutputDeviceDelay()}.
      * @return true if successful, false if the device does not support output device delay
      *     or the delay is not in range of {@link #getMaxAdditionalOutputDeviceDelay()}.
@@ -4776,7 +4777,7 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public boolean setAdditionalOutputDeviceDelay(
-            @NonNull AudioDeviceInfo device, @IntRange(from = 0) int delayMs) {
+            @NonNull AudioDeviceInfo device, @IntRange(from = 0) long delayMillis) {
         Objects.requireNonNull(device);
         // Implement the setter in r-dev or r-tv-dev as needed.
         return false;
@@ -4792,7 +4793,7 @@
      */
     @SystemApi
     @IntRange(from = 0)
-    public int getAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) {
+    public long getAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) {
         Objects.requireNonNull(device);
         // Implement the getter in r-dev or r-tv-dev as needed.
         return 0;
@@ -4810,7 +4811,7 @@
      */
     @SystemApi
     @IntRange(from = 0)
-    public int getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) {
+    public long getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceInfo device) {
         Objects.requireNonNull(device);
         // Implement the getter in r-dev or r-tv-dev as needed.
         return 0;
diff --git a/media/java/android/media/AudioMetadata.java b/media/java/android/media/AudioMetadata.java
index 1a9517c..c91ff0d 100644
--- a/media/java/android/media/AudioMetadata.java
+++ b/media/java/android/media/AudioMetadata.java
@@ -79,96 +79,11 @@
     }
 
     /**
-     * A read only {@code Map} interface of {@link Key} value pairs.
-     *
-     * <p>Using a {@link Key} interface, the map looks up the corresponding value.</p>
-     */
-    public interface ReadMap {
-        /**
-         * Returns true if the key exists in the map.
-         *
-         * @param key interface for requesting the value.
-         * @param <T> type of value.
-         * @return true if key exists in the Map.
-         */
-        <T> boolean containsKey(@NonNull Key<T> key);
-
-        /**
-         * Returns a copy of the map.
-         *
-         * This is intended for safe conversion between a {@link ReadMap}
-         * interface and a {@link Map} interface.
-         * Currently only simple objects are used for key values which
-         * means a shallow copy is sufficient.
-         *
-         * @return a Map copied from the existing map.
-         */
-        @NonNull
-        Map dup(); // lint checker doesn't like clone().
-
-        /**
-         * Returns the value associated with the key.
-         *
-         * @param key interface for requesting the value.
-         * @param <T> type of value.
-         * @return returns the value of associated with key or null if it doesn't exist.
-         */
-        @Nullable
-        <T> T get(@NonNull Key<T> key);
-
-        /**
-         * Returns a {@code Set} of keys associated with the map.
-         * @hide
-         */
-        @NonNull
-        Set<Key<?>> keySet();
-
-        /**
-         * Returns the number of elements in the map.
-         */
-        int size();
-    }
-
-    /**
-     * A writeable {@link Map} interface of {@link Key} value pairs.
-     * This interface is not guaranteed to be thread-safe
-     * unless the supplier for the {@code Map} states it as thread safe.
-     */
-    // TODO: Create a wrapper like java.util.Collections.synchronizedMap?
-    public interface Map extends ReadMap {
-        /**
-         * Removes the value associated with the key.
-         * @param key interface for storing the value.
-         * @param <T> type of value.
-         * @return the value of the key, null if it doesn't exist.
-         */
-        @Nullable
-        <T> T remove(@NonNull Key<T> key);
-
-        /**
-         * Sets a value for the key.
-         *
-         * @param key interface for storing the value.
-         * @param <T> type of value.
-         * @param value a non-null value of type T.
-         * @return the previous value associated with key or null if it doesn't exist.
-         */
-        // See automatic Kotlin overloading for Java interoperability.
-        // https://kotlinlang.org/docs/reference/java-interop.html#operators
-        // See also Kotlin set for overloaded operator indexing.
-        // https://kotlinlang.org/docs/reference/operator-overloading.html#indexed
-        // Also the Kotlin mutable-list set.
-        // https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-list/set.html
-        @Nullable
-        <T> T set(@NonNull Key<T> key, @NonNull T value);
-    }
-
-    /**
-     * Creates a {@link Map} suitable for adding keys.
-     * @return an empty {@link Map} instance.
+     * Creates a {@link AudioMetadataMap} suitable for adding keys.
+     * @return an empty {@link AudioMetadataMap} instance.
      */
     @NonNull
-    public static Map createMap() {
+    public static AudioMetadataMap createMap() {
         return new BaseMap();
     }
 
@@ -339,7 +254,7 @@
      * It is possible to require the keys to be of a certain class
      * before allowing a set or get operation.
      */
-    public static class BaseMap implements Map {
+    public static class BaseMap implements AudioMetadataMap {
         @Override
         public <T> boolean containsKey(@NonNull Key<T> key) {
             Pair<Key<?>, Object> valuePair = mHashMap.get(pairFromKey(key));
@@ -348,7 +263,7 @@
 
         @Override
         @NonNull
-        public Map dup() {
+        public AudioMetadataMap dup() {
             BaseMap map = new BaseMap();
             map.mHashMap.putAll(this.mHashMap);
             return map;
diff --git a/media/java/android/media/AudioMetadataMap.java b/media/java/android/media/AudioMetadataMap.java
new file mode 100644
index 0000000..1961931
--- /dev/null
+++ b/media/java/android/media/AudioMetadataMap.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 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.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * AudioMetadataMap is a writeable {@code Map}-style
+ * interface of {@link AudioMetadata.Key} value pairs.
+ * This interface is not guaranteed to be thread-safe
+ * unless the underlying implementation for the {@code AudioMetadataMap}
+ * states it as thread safe.
+ *
+ * {@see AudioMetadataReadMap}
+ */
+// TODO: Create a wrapper like java.util.Collections.synchronizedMap?
+
+public interface AudioMetadataMap extends AudioMetadataReadMap {
+    /**
+     * Removes the value associated with the key.
+     * @param key interface for storing the value.
+     * @param <T> type of value.
+     * @return the value of the key, null if it doesn't exist.
+     */
+    @Nullable
+    <T> T remove(@NonNull AudioMetadata.Key<T> key);
+
+    /**
+     * Sets a value for the key.
+     *
+     * @param key interface for storing the value.
+     * @param <T> type of value.
+     * @param value a non-null value of type T.
+     * @return the previous value associated with key or null if it doesn't exist.
+     */
+    // See automatic Kotlin overloading for Java interoperability.
+    // https://kotlinlang.org/docs/reference/java-interop.html#operators
+    // See also Kotlin set for overloaded operator indexing.
+    // https://kotlinlang.org/docs/reference/operator-overloading.html#indexed
+    // Also the Kotlin mutable-list set.
+    // https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-list/set.html
+    @Nullable
+    <T> T set(@NonNull AudioMetadata.Key<T> key, @NonNull T value);
+}
diff --git a/media/java/android/media/AudioMetadataReadMap.java b/media/java/android/media/AudioMetadataReadMap.java
new file mode 100644
index 0000000..e74242a
--- /dev/null
+++ b/media/java/android/media/AudioMetadataReadMap.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 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.media;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.Set;
+
+/**
+ * A read only {@code Map}-style interface of {@link AudioMetadata.Key} value pairs used
+ * for {@link AudioMetadata}.
+ *
+ * <p>Using a {@link AudioMetadata.Key} interface,
+ * this map looks up the corresponding value.
+ * Read-only maps are thread-safe for lookup, but the underlying object
+ * values may need their own thread protection if mutable.</p>
+ *
+ * {@see AudioMetadataMap}
+ */
+public interface AudioMetadataReadMap {
+    /**
+     * Returns true if the key exists in the map.
+     *
+     * @param key interface for requesting the value.
+     * @param <T> type of value.
+     * @return true if key exists in the Map.
+     */
+    <T> boolean containsKey(@NonNull AudioMetadata.Key<T> key);
+
+    /**
+     * Returns a copy of the map.
+     *
+     * This is intended for safe conversion between a {@link AudioMetadataReadMap}
+     * interface and a {@link AudioMetadataMap} interface.
+     * Currently only simple objects are used for key values which
+     * means a shallow copy is sufficient.
+     *
+     * @return a Map copied from the existing map.
+     */
+    @NonNull
+    AudioMetadataMap dup(); // lint checker doesn't like clone().
+
+    /**
+     * Returns the value associated with the key.
+     *
+     * @param key interface for requesting the value.
+     * @param <T> type of value.
+     * @return returns the value of associated with key or null if it doesn't exist.
+     */
+    @Nullable
+    <T> T get(@NonNull AudioMetadata.Key<T> key);
+
+    /**
+     * Returns a {@code Set} of keys associated with the map.
+     * @hide
+     */
+    @NonNull
+    Set<AudioMetadata.Key<?>> keySet();
+
+    /**
+     * Returns the number of elements in the map.
+     */
+    @IntRange(from = 0)
+    int size();
+}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 8b973a1..0a56acc 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -708,6 +708,8 @@
         DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_HEADSET);
     }
 
+    public static final String LEGACY_REMOTE_SUBMIX_ADDRESS = "0";
+
     // device states, must match AudioSystem::device_connection_state
     @UnsupportedAppUsage
     public static final int DEVICE_STATE_UNAVAILABLE = 0;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index d17e429..1d229b80 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -918,7 +918,29 @@
         private final int mContentId;
         private final int mSyncId;
 
-        private TunerConfiguration(int contentId, int syncId) {
+        /**
+         * Constructs a TunerConfiguration instance for use in {@link AudioTrack.Builder}
+         *
+         * @param contentId selects the audio stream to use.
+         *     The contentId may be obtained from
+         *     {@link android.media.tv.tuner.filter.Filter#getId()}.
+         *     This is always a positive number.
+         * @param syncId selects the clock to use for synchronization
+         *     of audio with other streams such as video.
+         *     The syncId may be obtained from
+         *     {@link android.media.tv.tuner.Tuner#getAvSyncHwId()}.
+         *     This is always a positive number.
+         */
+        @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+        public TunerConfiguration(
+                @IntRange(from = 1) int contentId, @IntRange(from = 1)int syncId) {
+            if (contentId < 1) {
+                throw new IllegalArgumentException(
+                        "contentId " + contentId + " must be positive");
+            }
+            if (syncId < 1) {
+                throw new IllegalArgumentException("syncId " + syncId + " must be positive");
+            }
             mContentId = contentId;
             mSyncId = syncId;
         }
@@ -938,73 +960,6 @@
         public @IntRange(from = 1) int getSyncId() {
             return mSyncId;  // The Builder ensures this is > 0.
         }
-
-        /**
-         * Builder class for {@link AudioTrack.TunerConfiguration} objects.
-         */
-        public static class Builder {
-            private int mContentId;
-            private int mSyncId;
-
-            /**
-             * Sets the contentId from the Tuner filter.
-             *
-             * @param contentId selects the audio stream to use.
-             *     The contentId may be obtained from
-             *     {@link android.media.tv.tuner.filter.Filter#getId()}.
-             *     This is always a positive number.
-             *
-             * @return the same Builder instance.
-             */
-            @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
-            public @NonNull Builder setContentId(@IntRange(from = 1) int contentId) {
-                if (contentId < 1) {
-                    throw new IllegalArgumentException(
-                            "contentId " + contentId + " must be positive");
-                }
-                mContentId = contentId;
-                return this;
-            }
-
-            /**
-             * Sets the syncId from the Tuner filter.
-             *
-             * @param syncId selects the clock to use for synchronization
-             *     of audio with other streams such as video.
-             *     The syncId may be obtained from
-             *     {@link android.media.tv.tuner.Tuner#getAvSyncHwId()}.
-             *     This is always a positive number.
-             *
-             * @return the same Builder instance.
-             */
-            @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
-            public @NonNull Builder setSyncId(@IntRange(from = 1) int syncId) {
-                if (syncId < 1) {
-                    throw new IllegalArgumentException("syncId " + syncId + " must be positive");
-                }
-                mSyncId = syncId;
-                return this;
-            }
-
-            /**
-             * Builds a {@link AudioTrack.TunerConfiguration} instance initialized with
-             * the parameters set on this {@code Builder}.
-             *
-             * @return a new successfully initialized {@link AudioTrack.TunerConfiguration}.
-             * @throws UnsupportedOperationException if the parameters set on the
-             *     {@code Builder} are incompatible.
-             */
-            @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
-            public @NonNull TunerConfiguration build() {
-                if (mContentId < 1 || mSyncId < 1) {
-                    throw new UnsupportedOperationException(
-                            "mContentId " + mContentId
-                            + " mSyncId " + mSyncId
-                            + " must be set");
-                }
-                return new TunerConfiguration(mContentId, mSyncId);
-            }
-        }
     }
 
     /**
@@ -3673,7 +3628,7 @@
     // OnCodecFormatChangedListener notifications uses an instance
     // of ListenerList to manage its listeners.
 
-    private final Utils.ListenerList<AudioMetadata.ReadMap> mCodecFormatChangedListeners =
+    private final Utils.ListenerList<AudioMetadataReadMap> mCodecFormatChangedListeners =
             new Utils.ListenerList();
 
     /**
@@ -3684,13 +3639,13 @@
          * Called when the compressed codec format changes.
          *
          * @param audioTrack is the {@code AudioTrack} instance associated with the codec.
-         * @param info is a {@link AudioMetadata.ReadMap} of values which contains decoded format
+         * @param info is a {@link AudioMetadataReadMap} of values which contains decoded format
          *     changes reported by the codec.  Not all hardware
          *     codecs indicate codec format changes. Acceptable keys are taken from
          *     {@code AudioMetadata.Format.KEY_*} range, with the associated value type.
          */
         void onCodecFormatChanged(
-                @NonNull AudioTrack audioTrack, @Nullable AudioMetadata.ReadMap info);
+                @NonNull AudioTrack audioTrack, @Nullable AudioMetadataReadMap info);
     }
 
     /**
@@ -3708,7 +3663,7 @@
         mCodecFormatChangedListeners.add(
                 listener, /* key for removal */
                 executor,
-                (int eventCode, AudioMetadata.ReadMap readMap) -> {
+                (int eventCode, AudioMetadataReadMap readMap) -> {
                     // eventCode is unused by this implementation.
                     listener.onCodecFormatChanged(this, readMap);
                 }
@@ -4067,7 +4022,7 @@
             ByteBuffer buffer = (ByteBuffer) obj;
             buffer.order(ByteOrder.nativeOrder());
             buffer.rewind();
-            AudioMetadata.ReadMap audioMetaData = AudioMetadata.fromByteBuffer(buffer);
+            AudioMetadataReadMap audioMetaData = AudioMetadata.fromByteBuffer(buffer);
             if (audioMetaData == null) {
                 Log.e(TAG, "Unable to get audio metadata from byte buffer");
                 return;
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index c0461bc..1d70a0d 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -46,6 +46,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
@@ -3048,16 +3049,47 @@
          * @param block The linear block object
          * @param offset The byte offset into the input buffer at which the data starts.
          * @param size The number of bytes of valid input data.
-         * @param cryptoInfo Metadata describing the structure of the encrypted input sample.
-         *                   may be null for non-encrypted content.
          * @return this object
          * @throws IllegalStateException if a buffer is already set
          */
         public @NonNull QueueRequest setLinearBlock(
                 @NonNull LinearBlock block,
                 int offset,
+                int size) {
+            if (!isAccessible()) {
+                throw new IllegalStateException("The request is stale");
+            }
+            if (mLinearBlock != null || mHardwareBuffer != null) {
+                throw new IllegalStateException("Cannot set block twice");
+            }
+            mLinearBlock = block;
+            mOffset = offset;
+            mSize = size;
+            mCryptoInfo = null;
+            return this;
+        }
+
+        /**
+         * Set an encrypted linear block to this queue request. Exactly one buffer must be
+         * set for a queue request before calling {@link #queue}. It is possible
+         * to use the same {@link LinearBlock} object for multiple queue
+         * requests. The behavior is undefined if the range of the buffer
+         * overlaps for multiple requests, or the application writes into the
+         * region being processed by the codec.
+         *
+         * @param block The linear block object
+         * @param offset The byte offset into the input buffer at which the data starts.
+         * @param size The number of bytes of valid input data.
+         * @param cryptoInfo Metadata describing the structure of the encrypted input sample.
+         * @return this object
+         * @throws IllegalStateException if a buffer is already set
+         */
+        public @NonNull QueueRequest setEncryptedLinearBlock(
+                @NonNull LinearBlock block,
+                int offset,
                 int size,
-                @Nullable MediaCodec.CryptoInfo cryptoInfo) {
+                @NonNull MediaCodec.CryptoInfo cryptoInfo) {
+            Objects.requireNonNull(cryptoInfo);
             if (!isAccessible()) {
                 throw new IllegalStateException("The request is stale");
             }
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 9985613..e5ad569 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -663,8 +663,8 @@
         }
 
         /**
-         * Constructor for builder to create {@link MediaRoute2Info} with
-         * existing {@link MediaRoute2Info} instance.
+         * Constructor for builder to create {@link MediaRoute2Info} with existing
+         * {@link MediaRoute2Info} instance.
          *
          * @param routeInfo the existing instance to copy data from.
          */
@@ -690,6 +690,38 @@
         }
 
         /**
+         * Constructor for builder to create {@link MediaRoute2Info} with existing
+         * {@link MediaRoute2Info} instance and replace ID with the given {@code id}.
+         *
+         * @param id The ID of the new route. Must not be empty.
+         * @param routeInfo the existing instance to copy data from.
+         * @hide
+         */
+        public Builder(@NonNull String id, @NonNull MediaRoute2Info routeInfo) {
+            if (TextUtils.isEmpty(id)) {
+                throw new IllegalArgumentException("id must not be empty");
+            }
+            Objects.requireNonNull(routeInfo, "routeInfo must not be null");
+
+            mId = id;
+            mName = routeInfo.mName;
+            mFeatures = new ArrayList<>(routeInfo.mFeatures);
+            mType = routeInfo.mType;
+            mIsSystem = routeInfo.mIsSystem;
+            mIconUri = routeInfo.mIconUri;
+            mDescription = routeInfo.mDescription;
+            mConnectionState = routeInfo.mConnectionState;
+            mClientPackageName = routeInfo.mClientPackageName;
+            mVolumeHandling = routeInfo.mVolumeHandling;
+            mVolumeMax = routeInfo.mVolumeMax;
+            mVolume = routeInfo.mVolume;
+            if (routeInfo.mExtras != null) {
+                mExtras = new Bundle(routeInfo.mExtras);
+            }
+            mProviderId = routeInfo.mProviderId;
+        }
+
+        /**
          * Adds a feature for the route.
          * @param feature a feature that the route has. May be one of predefined features
          *                such as {@link #FEATURE_LIVE_AUDIO}, {@link #FEATURE_LIVE_VIDEO} or
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index f9dbc50..090f78e 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -374,8 +374,8 @@
         if (refAttr.equals(sDefaultAttributes)) {
             return false;
         }
-        return ((refAttr.getUsage() == AudioAttributes.USAGE_UNKNOWN)
-                || (attr.getUsage() == refAttr.getUsage()))
+        return ((refAttr.getSystemUsage() == AudioAttributes.USAGE_UNKNOWN)
+                || (attr.getSystemUsage() == refAttr.getSystemUsage()))
             && ((refAttr.getContentType() == AudioAttributes.CONTENT_TYPE_UNKNOWN)
                 || (attr.getContentType() == refAttr.getContentType()))
             && ((refAttr.getAllFlags() == 0)
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index 6a8483c..7d51b10 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -211,7 +211,7 @@
          */
         @NonNull
         public UUID getModelUuid() {
-            return mGenericSoundModel.uuid;
+            return mGenericSoundModel.getUuid();
         }
 
         /**
@@ -221,7 +221,7 @@
          */
         @NonNull
         public UUID getVendorUuid() {
-            return mGenericSoundModel.vendorUuid;
+            return mGenericSoundModel.getVendorUuid();
         }
 
         /**
@@ -230,7 +230,7 @@
          * @return Version associated with the model
          */
         public int getVersion() {
-            return mGenericSoundModel.version;
+            return mGenericSoundModel.getVersion();
         }
 
         /**
@@ -240,7 +240,7 @@
          */
         @Nullable
         public byte[] getModelData() {
-            return mGenericSoundModel.data;
+            return mGenericSoundModel.getData();
         }
 
         /**
@@ -307,7 +307,7 @@
         }
 
         try {
-            switch (soundModel.type) {
+            switch (soundModel.getType()) {
                 case SoundModel.TYPE_GENERIC_SOUND:
                     return mSoundTriggerService.loadGenericSoundModel(
                             (GenericSoundModel) soundModel);
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 29dfd73..9310d38 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -40,6 +40,7 @@
 using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterSettings;
 using ::android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
 using ::android::hardware::tv::tuner::V1_0::DemuxAlpLengthType;
+using ::android::hardware::tv::tuner::V1_0::DemuxCapabilities;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadEvent;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterDownloadSettings;
@@ -1382,6 +1383,42 @@
     return dvrObj;
 }
 
+jobject JTuner::getDemuxCaps() {
+    DemuxCapabilities caps;
+    Result res;
+    mTuner->getDemuxCaps([&](Result r, const DemuxCapabilities& demuxCaps) {
+        caps = demuxCaps;
+        res = r;
+    });
+    if (res != Result::SUCCESS) {
+        return NULL;
+    }
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jclass clazz = env->FindClass("android/media/tv/tuner/DemuxCapabilities");
+    jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIIIIIIJI[IZ)V");
+
+    jint numDemux = caps.numDemux;
+    jint numRecord = caps.numRecord;
+    jint numPlayback = caps.numPlayback;
+    jint numTsFilter = caps.numTsFilter;
+    jint numSectionFilter = caps.numSectionFilter;
+    jint numAudioFilter = caps.numAudioFilter;
+    jint numVideoFilter = caps.numVideoFilter;
+    jint numPesFilter = caps.numPesFilter;
+    jint numPcrFilter = caps.numPcrFilter;
+    jlong numBytesInSectionFilter = caps.numBytesInSectionFilter;
+    jint filterCaps = static_cast<jint>(caps.filterCaps);
+    jboolean bTimeFilter = caps.bTimeFilter;
+
+    jintArray linkCaps = env->NewIntArray(caps.linkCaps.size());
+    env->SetIntArrayRegion(
+            linkCaps, 0, caps.linkCaps.size(), reinterpret_cast<jint*>(&caps.linkCaps[0]));
+
+    return env->NewObject(clazz, capsInit, numDemux, numRecord, numPlayback, numTsFilter,
+            numSectionFilter, numAudioFilter, numVideoFilter, numPesFilter, numPcrFilter,
+            numBytesInSectionFilter, filterCaps, linkCaps, bTimeFilter);
+}
+
 }  // namespace android
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -2739,8 +2776,9 @@
     return tuner->openDvr(DvrType::PLAYBACK, bufferSize);
 }
 
-static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv*, jobject) {
-    return NULL;
+static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv* env, jobject thiz) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->getDemuxCaps();
 }
 
 static int android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 5d2bba6..18aac28 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -185,6 +185,7 @@
     jobject openTimeFilter();
     jobject openDescrambler();
     jobject openDvr(DvrType type, jlong bufferSize);
+    jobject getDemuxCaps();
 
 protected:
     Result openDemux();
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
index 2522095..daeb731 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
@@ -2210,14 +2210,14 @@
     }
 
     public static Size getPreviewSizeBound(WindowManager windowManager, Size bound) {
-        Size windowSize = windowManager.getCurrentWindowMetrics().getSize();
+        Rect windowBounds = windowManager.getCurrentWindowMetrics().getBounds();
 
-        int width = windowSize.getWidth();
-        int height = windowSize.getHeight();
+        int width = windowBounds.width();
+        int height = windowBounds.height();
 
         if (height > width) {
             height = width;
-            width = windowSize.getHeight();
+            width = windowBounds.height();
         }
 
         if (bound.getWidth() <= width &&
diff --git a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java
index d4eb2a9..a2da23e 100644
--- a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java
+++ b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeApp.java
@@ -28,11 +28,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
-import android.util.Size;
 import android.util.Slog;
 import android.view.Display;
 import android.view.ViewGroup;
@@ -134,8 +134,8 @@
         }
         lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
         lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
-        Size maxWindowSize = wm.getMaximumWindowMetrics().getSize();
-        int maxSize = Math.max(maxWindowSize.getWidth(), maxWindowSize.getHeight());
+        Rect maxWindowBounds = wm.getMaximumWindowMetrics().getBounds();
+        int maxSize = Math.max(maxWindowBounds.width(), maxWindowBounds.height());
         maxSize *= 2;
         lp.x = maxSize;
         lp.y = maxSize;
diff --git a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeBackgroundService.java b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeBackgroundService.java
index df00eee..e2120f8 100644
--- a/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeBackgroundService.java
+++ b/packages/FakeOemFeatures/src/com/android/fakeoemfeatures/FakeBackgroundService.java
@@ -19,22 +19,22 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
-import java.util.ArrayList;
-import java.util.Random;
-
 import android.app.Dialog;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
-import android.util.Size;
 import android.view.Display;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 
+import java.util.ArrayList;
+import java.util.Random;
+
 public class FakeBackgroundService extends Service {
     final ArrayList<int[]> mAllocs = new ArrayList<int[]>();
 
@@ -99,8 +99,8 @@
         // Create an instance of WindowManager that is adjusted to the area of the display dedicated
         // for windows with type TYPE_APPLICATION_OVERLAY.
         final WindowManager wm = windowContext.getSystemService(WindowManager.class);
-        Size maxWindowSize = wm.getMaximumWindowMetrics().getSize();
-        int maxSize = Math.max(maxWindowSize.getWidth(), maxWindowSize.getHeight());
+        Rect maxWindowBounds = wm.getMaximumWindowMetrics().getBounds();
+        int maxSize = Math.max(maxWindowBounds.width(), maxWindowBounds.height());
         maxSize *= 2;
         lp.x = maxSize;
         lp.y = maxSize;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index a2fa461..a95677d 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -406,6 +406,10 @@
 
         mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
         mOk.setEnabled(false);
+
+        if (!mOk.isInTouchMode()) {
+            mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).requestFocus();
+        }
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
index 2b84196..59735f4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
@@ -189,19 +189,6 @@
         return context.getString(R.string.config_defaultAccessibilityService);
     }
 
-    /**
-     * Check if the accessibility shortcut is enabled for a user
-     *
-     * @param context A valid context
-     * @param userId  The user of interest
-     * @return {@code true} if the shortcut is enabled for the user. {@code false} otherwise.
-     * Note that the shortcut may be enabled, but no action associated with it.
-     */
-    public static boolean isShortcutEnabled(Context context, int userId) {
-        return Settings.Secure.getIntForUser(context.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 1, userId) == 1;
-    }
-
     private static Set<ComponentName> getInstalledServices(Context context) {
         final Set<ComponentName> installedServices = new HashSet<>();
         installedServices.clear();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index d17f242..a1fba4a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -174,7 +174,7 @@
 
     @Override
     public boolean isEnabled(BluetoothDevice device) {
-        if (mService == null) {
+        if (mService == null || device == null) {
             return false;
         }
         return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN;
@@ -182,7 +182,7 @@
 
     @Override
     public int getConnectionPolicy(BluetoothDevice device) {
-        if (mService == null) {
+        if (mService == null || device == null) {
             return CONNECTION_POLICY_FORBIDDEN;
         }
         return mService.getConnectionPolicy(device);
@@ -191,7 +191,7 @@
     @Override
     public boolean setEnabled(BluetoothDevice device, boolean enabled) {
         boolean isEnabled = false;
-        if (mService == null) {
+        if (mService == null || device == null) {
             return false;
         }
         if (enabled) {
@@ -213,7 +213,7 @@
     }
 
     public long getHiSyncId(BluetoothDevice device) {
-        if (mService == null) {
+        if (mService == null || device == null) {
             return BluetoothHearingAid.HI_SYNC_ID_INVALID;
         }
         return mService.getHiSyncId(device);
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
index c34c365..7ef0801 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
@@ -169,6 +169,22 @@
                 sourceMetricsCategory);
     }
 
+    /**
+     * Logs an event when the intent is started by Profile select dialog.
+     *
+     * @return true if the intent is loggable, otherwise false
+     */
+    public boolean logStartedIntentWithProfile(Intent intent, int sourceMetricsCategory,
+            boolean isWorkProfile) {
+        if (intent == null) {
+            return false;
+        }
+        final ComponentName cn = intent.getComponent();
+        final String key = cn != null ? cn.flattenToString() : intent.getAction();
+        return logSettingsTileClick(key + (isWorkProfile ? "/work" : "/personal"),
+                sourceMetricsCategory);
+    }
+
     private boolean logSettingsTileClick(String logKey, int sourceMetricsCategory) {
         if (TextUtils.isEmpty(logKey)) {
             // Not loggable
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index b1300a9..df0de68 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -66,11 +66,12 @@
     private LocalBluetoothManager mLocalBluetoothManager;
     private InfoMediaManager mInfoMediaManager;
     private String mPackageName;
+    private MediaDevice mOnTransferBluetoothDevice;
 
     @VisibleForTesting
-    List<MediaDevice> mMediaDevices = new ArrayList<>();
+    List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
     @VisibleForTesting
-    List<MediaDevice> mDisconnectedMediaDevices = new ArrayList<>();
+    List<MediaDevice> mDisconnectedMediaDevices = new CopyOnWriteArrayList<>();
     @VisibleForTesting
     MediaDevice mPhoneDevice;
     @VisibleForTesting
@@ -143,7 +144,7 @@
             final CachedBluetoothDevice cachedDevice =
                     ((BluetoothMediaDevice) device).getCachedDevice();
             if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) {
-                device.setState(MediaDeviceState.STATE_CONNECTING);
+                mOnTransferBluetoothDevice = connectDevice;
                 cachedDevice.connect();
                 return;
             }
@@ -206,6 +207,7 @@
     public void stopScan() {
         mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
         mInfoMediaManager.stopScan();
+        unRegisterDeviceAttributeChangeCallback();
     }
 
     /**
@@ -389,35 +391,41 @@
             mCurrentConnectedDevice = infoMediaDevice != null
                     ? infoMediaDevice : updateCurrentConnectedDevice();
             dispatchDeviceListUpdate();
+            if (mOnTransferBluetoothDevice != null && mOnTransferBluetoothDevice.isConnected()) {
+                connectDevice(mOnTransferBluetoothDevice);
+                mOnTransferBluetoothDevice = null;
+            }
         }
 
         private List<MediaDevice> buildDisconnectedBluetoothDevice() {
-            for (MediaDevice device : mDisconnectedMediaDevices) {
-                ((BluetoothMediaDevice) device).getCachedDevice()
-                        .unregisterCallback(mDeviceAttributeChangeCallback);
-            }
-            mDisconnectedMediaDevices.clear();
             final List<BluetoothDevice> bluetoothDevices =
                     mBluetoothAdapter.getMostRecentlyConnectedDevices();
             final CachedBluetoothDeviceManager cachedDeviceManager =
                     mLocalBluetoothManager.getCachedDeviceManager();
 
+            final List<CachedBluetoothDevice> cachedBluetoothDeviceList = new ArrayList<>();
             for (BluetoothDevice device : bluetoothDevices) {
                 final CachedBluetoothDevice cachedDevice =
                         cachedDeviceManager.findDevice(device);
                 if (cachedDevice != null) {
                     if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED
                             && !cachedDevice.isConnected()) {
-                        final MediaDevice mediaDevice = new BluetoothMediaDevice(mContext,
-                                cachedDevice,
-                                null, null, mPackageName);
-                        if (!mMediaDevices.contains(mediaDevice)) {
-                            cachedDevice.registerCallback(mDeviceAttributeChangeCallback);
-                            mDisconnectedMediaDevices.add(mediaDevice);
-                        }
+                        cachedBluetoothDeviceList.add(cachedDevice);
                     }
                 }
             }
+
+            unRegisterDeviceAttributeChangeCallback();
+            mDisconnectedMediaDevices.clear();
+            for (CachedBluetoothDevice cachedDevice : cachedBluetoothDeviceList) {
+                final MediaDevice mediaDevice = new BluetoothMediaDevice(mContext,
+                        cachedDevice,
+                        null, null, mPackageName);
+                if (!mMediaDevices.contains(mediaDevice)) {
+                    cachedDevice.registerCallback(mDeviceAttributeChangeCallback);
+                    mDisconnectedMediaDevices.add(mediaDevice);
+                }
+            }
             return new ArrayList<>(mDisconnectedMediaDevices);
         }
 
@@ -468,6 +476,12 @@
         }
     }
 
+    private void unRegisterDeviceAttributeChangeCallback() {
+        for (MediaDevice device : mDisconnectedMediaDevices) {
+            ((BluetoothMediaDevice) device).getCachedDevice()
+                    .unregisterCallback(mDeviceAttributeChangeCallback);
+        }
+    }
 
     /**
      * Callback for notifying device information updating
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 8bf48e59..c713d78 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -17,6 +17,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
@@ -36,7 +37,10 @@
 
 import java.util.List;
 
-public class WifiStatusTracker extends ConnectivityManager.NetworkCallback {
+/**
+ * Track status of Wi-Fi for the Sys UI.
+ */
+public class WifiStatusTracker {
     private final Context mContext;
     private final WifiNetworkScoreCache mWifiNetworkScoreCache;
     private final WifiManager mWifiManager;
@@ -55,8 +59,9 @@
             .clearCapabilities()
             .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
             .addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
-    private final ConnectivityManager.NetworkCallback mNetworkCallback = new ConnectivityManager
-            .NetworkCallback() {
+    private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+        // Note: onCapabilitiesChanged is guaranteed to be called "immediately" after onAvailable
+        // and onLinkPropertiesChanged.
         @Override
         public void onCapabilitiesChanged(
                 Network network, NetworkCapabilities networkCapabilities) {
@@ -64,11 +69,35 @@
             mCallback.run();
         }
     };
+    private final NetworkCallback mDefaultNetworkCallback = new NetworkCallback() {
+                @Override
+                public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+                    // network is now the default network, and its capabilities are nc.
+                    // This method will always be called immediately after the network becomes the
+                    // default, in addition to any time the capabilities change while the network is
+                    // the default.
+                    mDefaultNetwork = network;
+                    mDefaultNetworkCapabilities = nc;
+                    updateStatusLabel();
+                    mCallback.run();
+                }
+                @Override
+                public void onLost(Network network) {
+                    // The system no longer has a default network.
+                    mDefaultNetwork = null;
+                    mDefaultNetworkCapabilities = null;
+                    updateStatusLabel();
+                    mCallback.run();
+                }
+            };
+    private Network mDefaultNetwork = null;
+    private NetworkCapabilities mDefaultNetworkCapabilities = null;
     private final Runnable mCallback;
 
     private WifiInfo mWifiInfo;
     public boolean enabled;
     public boolean isCaptivePortal;
+    public boolean isDefaultNetwork;
     public int state;
     public boolean connected;
     public String ssid;
@@ -94,11 +123,13 @@
             mWifiNetworkScoreCache.registerListener(mCacheListener);
             mConnectivityManager.registerNetworkCallback(
                     mNetworkRequest, mNetworkCallback, mHandler);
+            mConnectivityManager.registerDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
         } else {
             mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI,
                     mWifiNetworkScoreCache);
             mWifiNetworkScoreCache.unregisterListener();
             mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+            mConnectivityManager.unregisterNetworkCallback(mDefaultNetworkCallback);
         }
     }
 
@@ -154,8 +185,17 @@
     }
 
     private void updateStatusLabel() {
-        final NetworkCapabilities networkCapabilities
-                = mConnectivityManager.getNetworkCapabilities(mWifiManager.getCurrentNetwork());
+        NetworkCapabilities networkCapabilities;
+        final Network currentWifiNetwork = mWifiManager.getCurrentNetwork();
+        if (currentWifiNetwork != null && currentWifiNetwork.equals(mDefaultNetwork)) {
+            // Wifi is connected and the default network.
+            isDefaultNetwork = true;
+            networkCapabilities = mDefaultNetworkCapabilities;
+        } else {
+            isDefaultNetwork = false;
+            networkCapabilities = mConnectivityManager.getNetworkCapabilities(
+                    mWifiManager.getCurrentNetwork());
+        }
         isCaptivePortal = false;
         if (networkCapabilities != null) {
             if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
index ed0857c..204a933 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
@@ -164,6 +164,38 @@
     }
 
     @Test
+    public void logStartedIntentWithProfile_isPersonalProfile_shouldTagPersonal() {
+        final Intent intent = new Intent().setComponent(new ComponentName("pkg", "cls"));
+
+        final boolean loggable = mProvider.logStartedIntentWithProfile(intent,
+                MetricsEvent.SETTINGS_GESTURES, false);
+
+        assertThat(loggable).isTrue();
+        verify(mLogWriter).action(
+                MetricsEvent.SETTINGS_GESTURES,
+                MetricsEvent.ACTION_SETTINGS_TILE_CLICK,
+                SettingsEnums.PAGE_UNKNOWN,
+                "pkg/cls/personal",
+                0);
+    }
+
+    @Test
+    public void logStartedIntentWithProfile_isWorkProfile_shouldTagWork() {
+        final Intent intent = new Intent().setComponent(new ComponentName("pkg", "cls"));
+
+        final boolean loggable = mProvider.logStartedIntentWithProfile(intent,
+                MetricsEvent.SETTINGS_GESTURES, true);
+
+        assertThat(loggable).isTrue();
+        verify(mLogWriter).action(
+                MetricsEvent.SETTINGS_GESTURES,
+                MetricsEvent.ACTION_SETTINGS_TILE_CLICK,
+                SettingsEnums.PAGE_UNKNOWN,
+                "pkg/cls/work",
+                0);
+    }
+
+    @Test
     public void getAttribution_noActivity_shouldReturnUnknown() {
         assertThat(mProvider.getAttribution(null /* activity */))
                 .isEqualTo(SettingsEnums.PAGE_UNKNOWN);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 6b3a97f..4c61ef5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -107,8 +108,8 @@
         when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
         when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
 
-        mInfoMediaDevice1 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1,
-                TEST_PACKAGE_NAME);
+        mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1,
+                TEST_PACKAGE_NAME));
         mInfoMediaDevice2 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo2,
                 TEST_PACKAGE_NAME);
         mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
@@ -565,6 +566,34 @@
     }
 
     @Test
+    public void onDeviceListAdded_transferToDisconnectedBluetooth_verifyConnectDevice() {
+        final List<MediaDevice> devices = new ArrayList<>();
+        final MediaDevice currentDevice = mock(MediaDevice.class);
+        final MediaDevice device = mock(BluetoothMediaDevice.class);
+        final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
+        mLocalMediaManager.mMediaDevices.add(device);
+        mLocalMediaManager.mMediaDevices.add(currentDevice);
+
+        when(device.getId()).thenReturn(TEST_DEVICE_ID_1);
+        when(currentDevice.getId()).thenReturn(TEST_CURRENT_DEVICE_ID);
+        when(((BluetoothMediaDevice) device).getCachedDevice()).thenReturn(cachedDevice);
+        when(cachedDevice.isConnected()).thenReturn(false);
+        when(cachedDevice.isBusy()).thenReturn(false);
+
+        mLocalMediaManager.registerCallback(mCallback);
+        mLocalMediaManager.connectDevice(device);
+
+        verify(cachedDevice).connect();
+        when(device.isConnected()).thenReturn(true);
+        mLocalMediaManager.mCurrentConnectedDevice = currentDevice;
+        devices.add(mInfoMediaDevice1);
+        devices.add(currentDevice);
+        mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
+
+        verify(mInfoMediaDevice1).connect();
+    }
+
+    @Test
     public void onRequestFailed_shouldDispatchOnRequestFailed() {
         mLocalMediaManager.registerCallback(mCallback);
 
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index d350d9d..736e995 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -48,7 +48,6 @@
         Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
         Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
         Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
-        Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED,
         Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
         Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
         Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET,
@@ -97,6 +96,7 @@
         Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
         Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
         Settings.Secure.QS_TILES,
+        Settings.Secure.CONTROLS_ENABLED,
         Settings.Secure.DOZE_ENABLED,
         Settings.Secure.DOZE_ALWAYS_ON,
         Settings.Secure.DOZE_PICK_UP_GESTURE,
@@ -164,6 +164,6 @@
         Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT,
         Settings.Secure.PEOPLE_STRIP,
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
-        Settings.Secure.ACCESSIBILITY_BUTTON_LONG_PRESS_TARGETS,
+        Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 4d33b62..b413e8e 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -25,10 +25,10 @@
 import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.JSON_OBJECT_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.LOCALE_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
-import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.TILE_LIST_VALIDATOR;
 import static android.provider.settings.validators.SettingsValidators.TTS_LIST_VALIDATOR;
 
@@ -78,11 +78,8 @@
                 ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
         // technically either ComponentName or class name, but there's proper value
         // validation at callsites, so allow any non-null string
-        VALIDATORS.put(
-                Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
-                ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
+        VALIDATORS.put(Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, value -> value != null);
         VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(
@@ -144,6 +141,7 @@
         VALIDATORS.put(Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.QS_TILES, TILE_LIST_VALIDATOR);
+        VALIDATORS.put(Secure.CONTROLS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.DOZE_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.DOZE_ALWAYS_ON, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.DOZE_PICK_UP_GESTURE, BOOLEAN_VALIDATOR);
@@ -249,7 +247,7 @@
                         Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
                         Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
         VALIDATORS.put(
-                Secure.ACCESSIBILITY_BUTTON_LONG_PRESS_TARGETS,
+                Secure.ACCESSIBILITY_BUTTON_TARGETS,
                 ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index af74121..8a7b913 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1781,9 +1781,6 @@
                 Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
                 SecureSettingsProto.Accessibility.LARGE_POINTER_ICON);
         dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED,
-                SecureSettingsProto.Accessibility.SHORTCUT_ENABLED);
-        dumpSetting(s, p,
                 Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
                 SecureSettingsProto.Accessibility.SHORTCUT_ON_LOCK_SCREEN);
         dumpSetting(s, p,
@@ -1814,8 +1811,8 @@
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
                 SecureSettingsProto.Accessibility.ACCESSIBILITY_MAGNIFICATION_MODE);
         dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_BUTTON_LONG_PRESS_TARGETS,
-                SecureSettingsProto.Accessibility.BUTTON_LONG_PRESS_TARGETS);
+                Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
+                SecureSettingsProto.Accessibility.BUTTON_TARGETS);
         p.end(accessibilityToken);
 
         final long adaptiveSleepToken = p.start(SecureSettingsProto.ADAPTIVE_SLEEP);
@@ -1975,6 +1972,13 @@
         dumpSetting(s, p,
                 Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS,
                 SecureSettingsProto.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS);
+
+        final long controlsToken = p.start(SecureSettingsProto.CONTROLS);
+        dumpSetting(s, p,
+                Settings.Secure.CONTROLS_ENABLED,
+                SecureSettingsProto.Controls.ENABLED);
+        p.end(controlsToken);
+
         dumpSetting(s, p,
                 Settings.Secure.DEVICE_PAIRED,
                 SecureSettingsProto.DEVICE_PAIRED);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 5a9d749..2fde87c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3436,7 +3436,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 188;
+            private static final int SETTINGS_VERSION = 189;
 
             private final int mUserId;
 
@@ -4759,6 +4759,23 @@
                     currentVersion = 188;
                 }
 
+                if (currentVersion == 188) {
+                    // Deprecate ACCESSIBILITY_SHORTCUT_ENABLED, and migrate it
+                    // to ACCESSIBILITY_SHORTCUT_TARGET_SERVICE.
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    final Setting shortcutEnabled = secureSettings.getSettingLocked(
+                            "accessibility_shortcut_enabled");
+                    if ("0".equals(shortcutEnabled.getValue())) {
+                        // Clear shortcut key targets list setting.
+                        secureSettings.insertSettingLocked(
+                                Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+                                "", null /* tag */, false /* makeDefault */,
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+                    secureSettings.deleteSettingLocked("accessibility_shortcut_enabled");
+                    currentVersion = 189;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index c7fb00a..4771c41 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -81,6 +81,8 @@
     <uses-permission android:name="android.permission.READ_INPUT_STATE" />
     <uses-permission android:name="android.permission.SET_ORIENTATION" />
     <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+    <!--  TODO(b/152310230): remove once APIs are confirmed to be sufficient -->
+    <uses-permission android:name="com.android.permission.USE_INSTALLER_V2" />
     <uses-permission android:name="android.permission.MOVE_PACKAGE" />
     <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
     <uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 64e5237..a1376c3 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -1019,8 +1019,11 @@
      * Wraps up bugreport generation and triggers a notification to share the bugreport.
      */
     private void onBugreportFinished(BugreportInfo info) {
+        if (!TextUtils.isEmpty(info.shareTitle)) {
+            info.setTitle(info.shareTitle);
+        }
         Log.d(TAG, "Bugreport finished with title: " + info.getTitle()
-                + " and shareDescription:  " + info.shareDescription);
+                + " and shareDescription: " + info.shareDescription);
         info.finished = new AtomicBoolean(true);
 
         synchronized (mLock) {
@@ -1795,7 +1798,9 @@
 
         /**
          * User-provided, detailed description of the bugreport; when set, will be added to the body
-         * of the {@link Intent#ACTION_SEND_MULTIPLE} intent.
+         * of the {@link Intent#ACTION_SEND_MULTIPLE} intent. This is shown in the app where the
+         * bugreport is being shared as an attachment. This is not related/dependant on
+         * {@code shareDescription}.
          */
         private String description;
 
@@ -1803,13 +1808,13 @@
          * Current value of progress (in percentage) of the bugreport generation as
          * displayed by the UI.
          */
-        AtomicInteger progress;
+        AtomicInteger progress = new AtomicInteger(0);
 
         /**
          * Last value of progress (in percentage) of the bugreport generation for which
          * system notification was updated.
          */
-        AtomicInteger lastProgress;
+        AtomicInteger lastProgress = new AtomicInteger(0);
 
         /**
          * Time of the last progress update.
@@ -2130,7 +2135,6 @@
                 return new BugreportInfo[size];
             }
         };
-
     }
 
     @GuardedBy("mLock")
diff --git a/packages/SystemUI/res/drawable/control_background_ripple.xml b/packages/SystemUI/res/drawable/control_background_ripple.xml
new file mode 100644
index 0000000..37914e2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/control_background_ripple.xml
@@ -0,0 +1,23 @@
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="?android:attr/colorControlHighlight">
+    <item android:id="@android:id/mask">
+        <color android:color="@android:color/white" />
+    </item>
+    <item android:drawable="@drawable/control_background" />
+</ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_device_air_freshener_off.xml b/packages/SystemUI/res/drawable/ic_device_air_freshener_off.xml
new file mode 100644
index 0000000..c343020
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_air_freshener_off.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12,5C12.5523,5 13,4.5523 13,4C13,3.4477 12.5523,3 12,3C11.4477,3 11,3.4477 11,4C11,4.5523 11.4477,5 12,5Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M12,2C12.5523,2 13,1.5523 13,1C13,0.4477 12.5523,0 12,0C11.4477,0 11,0.4477 11,1C11,1.5523 11.4477,2 12,2Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M9.707,4.707C9.8469,4.5671 9.9421,4.389 9.9808,4.195C10.0194,4.001 9.9996,3.7999 9.9239,3.6172C9.8482,3.4344 9.72,3.2782 9.5556,3.1683C9.3911,3.0584 9.1978,2.9998 9,2.9998C8.8022,2.9998 8.6088,3.0584 8.4444,3.1683C8.2799,3.2782 8.1518,3.4344 8.0761,3.6172C8.0004,3.7999 7.9806,4.001 8.0192,4.195C8.0578,4.389 8.1531,4.5671 8.293,4.707C8.4805,4.8944 8.7348,4.9998 9,4.9998C9.2652,4.9998 9.5195,4.8944 9.707,4.707Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M7.707,2.707C7.8469,2.5671 7.9422,2.389 7.9808,2.195C8.0194,2.001 7.9996,1.7999 7.9239,1.6172C7.8482,1.4344 7.7201,1.2782 7.5556,1.1683C7.3911,1.0584 7.1978,0.9998 7,0.9998C6.8022,0.9998 6.6089,1.0584 6.4444,1.1683C6.2799,1.2782 6.1518,1.4344 6.0761,1.6172C6.0004,1.7999 5.9806,2.001 6.0192,2.195C6.0578,2.389 6.1531,2.5671 6.293,2.707C6.4805,2.8944 6.7348,2.9998 7,2.9998C7.2652,2.9998 7.5195,2.8944 7.707,2.707Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M15.707,4.707C15.8469,4.5671 15.9421,4.389 15.9808,4.195C16.0194,4.001 15.9996,3.7999 15.9239,3.6172C15.8482,3.4344 15.7201,3.2782 15.5556,3.1683C15.3911,3.0584 15.1978,2.9998 15,2.9998C14.8022,2.9998 14.6088,3.0584 14.4444,3.1683C14.2799,3.2782 14.1518,3.4344 14.0761,3.6172C14.0004,3.7999 13.9806,4.001 14.0192,4.195C14.0578,4.389 14.1531,4.5671 14.293,4.707C14.4805,4.8944 14.7348,4.9998 15,4.9998C15.2652,4.9998 15.5195,4.8944 15.707,4.707Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M17.707,2.707C17.8469,2.5671 17.9421,2.389 17.9808,2.195C18.0194,2.001 17.9996,1.7999 17.9239,1.6172C17.8482,1.4344 17.7201,1.2782 17.5556,1.1683C17.3911,1.0584 17.1978,0.9998 17,0.9998C16.8022,0.9998 16.6088,1.0584 16.4444,1.1683C16.2799,1.2782 16.1518,1.4344 16.0761,1.6172C16.0004,1.7999 15.9806,2.001 16.0192,2.195C16.0578,2.389 16.1531,2.5671 16.293,2.707C16.4805,2.8944 16.7348,2.9998 17,2.9998C17.2652,2.9998 17.5195,2.8944 17.707,2.707Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M15,9.683V7C15,6.7348 14.8946,6.4804 14.7071,6.2929C14.5196,6.1054 14.2652,6 14,6H10C9.7348,6 9.4804,6.1054 9.2929,6.2929C9.1053,6.4804 9,6.7348 9,7V9.683C7.855,10.2245 6.8787,11.067 6.1756,12.1205C5.4725,13.174 5.0689,14.3988 5.0081,15.6639C4.9473,16.929 5.2315,18.1868 5.8304,19.3029C6.4292,20.4189 7.3202,21.3512 8.408,22H15.592C16.6798,21.3512 17.5707,20.4189 18.1696,19.3029C18.7685,18.1868 19.0527,16.929 18.9919,15.6639C18.9311,14.3988 18.5275,13.174 17.8244,12.1205C17.1212,11.067 16.145,10.2245 15,9.683ZM14.989,20H9.011C8.3852,19.5381 7.877,18.9352 7.5276,18.2402C7.1783,17.5453 6.9975,16.7778 7,16C7.0041,15.0553 7.2746,14.131 7.7803,13.3331C8.286,12.5351 9.0065,11.896 9.859,11.489L11,10.946V8H13V10.946L14.141,11.489C14.9935,11.896 15.714,12.5351 16.2197,13.3331C16.7254,14.131 16.9959,15.0553 17,16C17.0025,16.7778 16.8217,17.5453 16.4723,18.2402C16.123,18.9352 15.6148,19.5381 14.989,20Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_air_freshener_on.xml b/packages/SystemUI/res/drawable/ic_device_air_freshener_on.xml
new file mode 100644
index 0000000..4f3434d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_air_freshener_on.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12,5C12.5523,5 13,4.5523 13,4C13,3.4477 12.5523,3 12,3C11.4477,3 11,3.4477 11,4C11,4.5523 11.4477,5 12,5Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M12,2C12.5523,2 13,1.5523 13,1C13,0.4477 12.5523,0 12,0C11.4477,0 11,0.4477 11,1C11,1.5523 11.4477,2 12,2Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M9.707,4.707C9.8469,4.5671 9.9422,4.389 9.9808,4.195C10.0194,4.001 9.9996,3.7999 9.9239,3.6172C9.8482,3.4344 9.7201,3.2782 9.5556,3.1683C9.3912,3.0584 9.1978,2.9998 9,2.9998C8.8022,2.9998 8.6088,3.0584 8.4444,3.1683C8.2799,3.2782 8.1518,3.4344 8.0761,3.6172C8.0004,3.7999 7.9806,4.001 8.0192,4.195C8.0578,4.389 8.1531,4.5671 8.293,4.707C8.4805,4.8944 8.7348,4.9998 9,4.9998C9.2652,4.9998 9.5195,4.8944 9.707,4.707Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M7.707,2.707C7.8469,2.5671 7.9422,2.389 7.9808,2.195C8.0194,2.001 7.9996,1.7999 7.9239,1.6172C7.8482,1.4344 7.7201,1.2782 7.5556,1.1683C7.3911,1.0584 7.1978,0.9998 7,0.9998C6.8022,0.9998 6.6089,1.0584 6.4444,1.1683C6.2799,1.2782 6.1518,1.4344 6.0761,1.6172C6.0004,1.7999 5.9806,2.001 6.0192,2.195C6.0578,2.389 6.1531,2.5671 6.293,2.707C6.4805,2.8944 6.7348,2.9998 7,2.9998C7.2652,2.9998 7.5195,2.8944 7.707,2.707Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M15.707,4.707C15.8469,4.5671 15.9422,4.389 15.9808,4.195C16.0194,4.001 15.9996,3.7999 15.9239,3.6172C15.8482,3.4344 15.7201,3.2782 15.5556,3.1683C15.3911,3.0584 15.1978,2.9998 15,2.9998C14.8022,2.9998 14.6089,3.0584 14.4444,3.1683C14.2799,3.2782 14.1518,3.4344 14.0761,3.6172C14.0004,3.7999 13.9806,4.001 14.0192,4.195C14.0578,4.389 14.1531,4.5671 14.293,4.707C14.4805,4.8944 14.7348,4.9998 15,4.9998C15.2652,4.9998 15.5195,4.8944 15.707,4.707Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M17.707,2.707C17.8469,2.5671 17.9422,2.389 17.9808,2.195C18.0194,2.001 17.9996,1.7999 17.9239,1.6172C17.8482,1.4344 17.7201,1.2782 17.5556,1.1683C17.3911,1.0584 17.1978,0.9998 17,0.9998C16.8022,0.9998 16.6089,1.0584 16.4444,1.1683C16.2799,1.2782 16.1518,1.4344 16.0761,1.6172C16.0004,1.7999 15.9806,2.001 16.0192,2.195C16.0578,2.389 16.1531,2.5671 16.293,2.707C16.4805,2.8944 16.7348,2.9998 17,2.9998C17.2652,2.9998 17.5195,2.8944 17.707,2.707Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M15,9.683V7C15,6.7348 14.8946,6.4804 14.7071,6.2929C14.5196,6.1054 14.2652,6 14,6H10C9.7348,6 9.4804,6.1054 9.2929,6.2929C9.1053,6.4804 9,6.7348 9,7V9.683C7.855,10.2245 6.8787,11.067 6.1756,12.1205C5.4725,13.174 5.0689,14.3988 5.0081,15.6639C4.9473,16.929 5.2315,18.1868 5.8304,19.3029C6.4292,20.4189 7.3202,21.3512 8.408,22H15.592C16.6798,21.3512 17.5707,20.4189 18.1696,19.3029C18.7685,18.1868 19.0527,16.929 18.9919,15.6639C18.9311,14.3988 18.5275,13.174 17.8244,12.1205C17.1213,11.067 16.145,10.2245 15,9.683Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_air_purifier_off.xml b/packages/SystemUI/res/drawable/ic_device_air_purifier_off.xml
new file mode 100644
index 0000000..b18c3e7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_air_purifier_off.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M16,19H5V7C5.0016,6.47 5.2128,5.9623 5.5875,5.5875C5.9623,5.2128 6.47,5.0016 7,5H14C14.5299,5.0016 15.0377,5.2128 15.4125,5.5875C15.7872,5.9623 15.9984,6.47 16,7V8H18V7C18,5.9391 17.5786,4.9217 16.8284,4.1716C16.0783,3.4214 15.0609,3 14,3H7C5.9391,3 4.9217,3.4214 4.1716,4.1716C3.4214,4.9217 3,5.9391 3,7V21H18V17H16V19Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M19,13C18.6049,13.378 18.1309,13.6638 17.6122,13.8367C17.0935,14.0096 16.5429,14.0653 16,14V16C16.5429,16.0653 17.0935,16.0096 17.6122,15.8367C18.1309,15.6638 18.6049,15.378 19,15C19.93,14.02 20,14 21,14V12C20,12 19.93,12.02 19,13Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M19,9C18.6049,9.378 18.1309,9.6638 17.6122,9.8367C17.0935,10.0096 16.5429,10.0653 16,10V12C16.5429,12.0653 17.0935,12.0096 17.6122,11.8367C18.1309,11.6638 18.6049,11.378 19,11C19.93,10.02 20,10 21,10V8C20,8 19.93,8.02 19,9Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M10.5,8C9.7089,8 8.9355,8.2346 8.2777,8.6741C7.6199,9.1137 7.1072,9.7383 6.8045,10.4692C6.5017,11.2001 6.4225,12.0044 6.5769,12.7803C6.7312,13.5563 7.1122,14.269 7.6716,14.8284C8.231,15.3878 8.9437,15.7688 9.7196,15.9232C10.4956,16.0775 11.2998,15.9982 12.0307,15.6955C12.7616,15.3927 13.3864,14.8801 13.8259,14.2223C14.2654,13.5645 14.5,12.7911 14.5,12C14.5,10.9391 14.0786,9.9217 13.3284,9.1716C12.5783,8.4214 11.5609,8 10.5,8ZM10.5,14C10.1044,14 9.7178,13.8827 9.3889,13.663C9.06,13.4432 8.8036,13.1308 8.6522,12.7654C8.5009,12.3999 8.4613,11.9978 8.5384,11.6098C8.6156,11.2218 8.8061,10.8655 9.0858,10.5858C9.3655,10.3061 9.7219,10.1156 10.1098,10.0385C10.4978,9.9613 10.8999,10.0008 11.2654,10.1522C11.6308,10.3036 11.9432,10.56 12.1629,10.8889C12.3827,11.2178 12.5,11.6044 12.5,12C12.5,12.5304 12.2893,13.0391 11.9142,13.4142C11.5391,13.7893 11.0304,14 10.5,14Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_air_purifier_on.xml b/packages/SystemUI/res/drawable/ic_device_air_purifier_on.xml
new file mode 100644
index 0000000..b18c3e7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_air_purifier_on.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M16,19H5V7C5.0016,6.47 5.2128,5.9623 5.5875,5.5875C5.9623,5.2128 6.47,5.0016 7,5H14C14.5299,5.0016 15.0377,5.2128 15.4125,5.5875C15.7872,5.9623 15.9984,6.47 16,7V8H18V7C18,5.9391 17.5786,4.9217 16.8284,4.1716C16.0783,3.4214 15.0609,3 14,3H7C5.9391,3 4.9217,3.4214 4.1716,4.1716C3.4214,4.9217 3,5.9391 3,7V21H18V17H16V19Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M19,13C18.6049,13.378 18.1309,13.6638 17.6122,13.8367C17.0935,14.0096 16.5429,14.0653 16,14V16C16.5429,16.0653 17.0935,16.0096 17.6122,15.8367C18.1309,15.6638 18.6049,15.378 19,15C19.93,14.02 20,14 21,14V12C20,12 19.93,12.02 19,13Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M19,9C18.6049,9.378 18.1309,9.6638 17.6122,9.8367C17.0935,10.0096 16.5429,10.0653 16,10V12C16.5429,12.0653 17.0935,12.0096 17.6122,11.8367C18.1309,11.6638 18.6049,11.378 19,11C19.93,10.02 20,10 21,10V8C20,8 19.93,8.02 19,9Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M10.5,8C9.7089,8 8.9355,8.2346 8.2777,8.6741C7.6199,9.1137 7.1072,9.7383 6.8045,10.4692C6.5017,11.2001 6.4225,12.0044 6.5769,12.7803C6.7312,13.5563 7.1122,14.269 7.6716,14.8284C8.231,15.3878 8.9437,15.7688 9.7196,15.9232C10.4956,16.0775 11.2998,15.9982 12.0307,15.6955C12.7616,15.3927 13.3864,14.8801 13.8259,14.2223C14.2654,13.5645 14.5,12.7911 14.5,12C14.5,10.9391 14.0786,9.9217 13.3284,9.1716C12.5783,8.4214 11.5609,8 10.5,8ZM10.5,14C10.1044,14 9.7178,13.8827 9.3889,13.663C9.06,13.4432 8.8036,13.1308 8.6522,12.7654C8.5009,12.3999 8.4613,11.9978 8.5384,11.6098C8.6156,11.2218 8.8061,10.8655 9.0858,10.5858C9.3655,10.3061 9.7219,10.1156 10.1098,10.0385C10.4978,9.9613 10.8999,10.0008 11.2654,10.1522C11.6308,10.3036 11.9432,10.56 12.1629,10.8889C12.3827,11.2178 12.5,11.6044 12.5,12C12.5,12.5304 12.2893,13.0391 11.9142,13.4142C11.5391,13.7893 11.0304,14 10.5,14Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_blinds_off.xml b/packages/SystemUI/res/drawable/ic_device_blinds_off.xml
new file mode 100644
index 0000000..a511ad2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_blinds_off.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20,18V4H4V18H2V20H22V18H20ZM18,6V9H6V6H18ZM6,18V11H11V13.2771C10.6187,13.4972 10.3207,13.8369 10.1522,14.2437C9.9838,14.6504 9.9542,15.1013 10.0681,15.5266C10.1821,15.9519 10.4332,16.3277 10.7825,16.5957C11.1318,16.8637 11.5597,17.009 12,17.009C12.4403,17.009 12.8682,16.8637 13.2175,16.5957C13.5668,16.3277 13.8179,15.9519 13.9319,15.5266C14.0458,15.1013 14.0162,14.6504 13.8478,14.2437C13.6793,13.8369 13.3813,13.4972 13,13.2771V11H18V18H6Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_blinds_on.xml b/packages/SystemUI/res/drawable/ic_device_blinds_on.xml
new file mode 100644
index 0000000..8166274
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_blinds_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20,18V4H4V18H2V20H22V18H20ZM6,18V11H11V13.2771C10.6187,13.4972 10.3207,13.8369 10.1522,14.2437C9.9838,14.6504 9.9542,15.1013 10.0681,15.5266C10.1821,15.9519 10.4332,16.3277 10.7825,16.5957C11.1318,16.8637 11.5597,17.009 12,17.009C12.4403,17.009 12.8682,16.8637 13.2175,16.5957C13.5668,16.3277 13.8179,15.9519 13.9319,15.5266C14.0458,15.1013 14.0162,14.6504 13.8478,14.2437C13.6793,13.8369 13.3813,13.4972 13,13.2771V11H18V18H6Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_camera_off.xml b/packages/SystemUI/res/drawable/ic_device_camera_off.xml
new file mode 100644
index 0000000..32cad14
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_camera_off.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M18,10.48V6C17.9984,5.47 17.7872,4.9624 17.4125,4.5876C17.0377,4.2129 16.5299,4.0016 16,4H4C3.4701,4.0016 2.9623,4.2129 2.5875,4.5876C2.2128,4.9624 2.0016,5.47 2,6V18C2.0016,18.5299 2.2128,19.0376 2.5875,19.4124C2.9623,19.7871 3.4701,19.9984 4,20H16C16.5299,19.9984 17.0377,19.7871 17.4125,19.4124C17.7872,19.0376 17.9984,18.5299 18,18V13.52L22,17.5V6.5L18,10.48ZM16,9.6899V18H4V6H16V9.6899Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_camera_on.xml b/packages/SystemUI/res/drawable/ic_device_camera_on.xml
new file mode 100644
index 0000000..93c50f0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_camera_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M18,10.48V6C17.9984,5.47 17.7872,4.9624 17.4125,4.5876C17.0377,4.2129 16.5299,4.0016 16,4H4C3.4701,4.0016 2.9623,4.2129 2.5875,4.5876C2.2128,4.9624 2.0016,5.47 2,6V18C2.0016,18.5299 2.2128,19.0376 2.5875,19.4124C2.9623,19.7871 3.4701,19.9984 4,20H16C16.5299,19.9984 17.0377,19.7871 17.4125,19.4124C17.7872,19.0376 17.9984,18.5299 18,18V13.52L22,17.5V6.5L18,10.48Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_dishwasher_off.xml b/packages/SystemUI/res/drawable/ic_device_dishwasher_off.xml
new file mode 100644
index 0000000..16ad90b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_dishwasher_off.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M18,2.01L6,2C5.7371,1.9991 5.4766,2.0502 5.2335,2.1504C4.9905,2.2506 4.7696,2.3979 4.5837,2.5838C4.3978,2.7696 4.2505,2.9905 4.1504,3.2335C4.0502,3.4766 3.9991,3.7371 4,4V20C3.9991,20.2629 4.0502,20.5234 4.1504,20.7665C4.2505,21.0096 4.3978,21.2304 4.5837,21.4163C4.7696,21.6022 4.9905,21.7494 5.2335,21.8496C5.4766,21.9498 5.7371,22.0009 6,22H18C18.2629,22.0009 18.5234,21.9498 18.7665,21.8496C19.0095,21.7494 19.2304,21.6022 19.4163,21.4163C19.6022,21.2304 19.7495,21.0096 19.8496,20.7665C19.9498,20.5234 20.0009,20.2629 20,20V4C20.0007,3.7376 19.9493,3.4778 19.8489,3.2354C19.7485,2.993 19.6011,2.7728 19.4151,2.5878C19.2291,2.4027 19.0083,2.2564 18.7654,2.1572C18.5225,2.0581 18.2624,2.008 18,2.01ZM18,20H6L5.993,4H18V20Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M11,7C11.5523,7 12,6.5523 12,6C12,5.4477 11.5523,5 11,5C10.4477,5 10,5.4477 10,6C10,6.5523 10.4477,7 11,7Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M8,7C8.5523,7 9,6.5523 9,6C9,5.4477 8.5523,5 8,5C7.4477,5 7,5.4477 7,6C7,6.5523 7.4477,7 8,7Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M8.211,14.272C8.2337,15.2618 8.6429,16.2035 9.351,16.8955C10.0591,17.5875 11.0099,17.975 12,17.975C12.9901,17.975 13.9409,17.5875 14.649,16.8955C15.3571,16.2035 15.7663,15.2618 15.789,14.272C15.7891,13.7805 15.6908,13.2941 15.5,12.8412C15.3092,12.3883 15.0297,11.9782 14.678,11.635L12,9L9.322,11.635C8.9703,11.9782 8.6908,12.3883 8.5,12.8412C8.3092,13.2941 8.2109,13.7805 8.211,14.272ZM10.724,13.061L12,11.806L13.276,13.061C13.4381,13.2184 13.567,13.4065 13.6551,13.6145C13.7432,13.8225 13.7888,14.0461 13.789,14.272C13.7735,14.7361 13.5782,15.1761 13.2444,15.4989C12.9106,15.8217 12.4644,16.0022 12,16.0022C11.5356,16.0022 11.0894,15.8217 10.7556,15.4989C10.4218,15.1761 10.2265,14.7361 10.211,14.272C10.2111,14.0461 10.2566,13.8225 10.3447,13.6145C10.4328,13.4065 10.5618,13.2183 10.724,13.061Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_dishwasher_on.xml b/packages/SystemUI/res/drawable/ic_device_dishwasher_on.xml
new file mode 100644
index 0000000..63f99ce
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_dishwasher_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M18,2.01L6,2C5.7371,1.9991 5.4766,2.0502 5.2335,2.1504C4.9905,2.2506 4.7696,2.3979 4.5837,2.5838C4.3978,2.7696 4.2505,2.9905 4.1504,3.2335C4.0502,3.4766 3.9991,3.7371 4,4V20C3.9991,20.2629 4.0502,20.5234 4.1504,20.7665C4.2505,21.0096 4.3978,21.2304 4.5837,21.4163C4.7696,21.6022 4.9905,21.7494 5.2335,21.8496C5.4766,21.9498 5.7371,22.0009 6,22H18C18.2629,22.0009 18.5234,21.9498 18.7665,21.8496C19.0095,21.7494 19.2304,21.6022 19.4163,21.4163C19.6022,21.2304 19.7495,21.0096 19.8496,20.7665C19.9498,20.5234 20.0009,20.2629 20,20V4C20.0007,3.7376 19.9493,3.4778 19.8489,3.2354C19.7485,2.993 19.6011,2.7728 19.4151,2.5878C19.2291,2.4027 19.0083,2.2564 18.7654,2.1572C18.5225,2.0581 18.2624,2.008 18,2.01ZM11,5C11.1978,5 11.3911,5.0587 11.5556,5.1686C11.72,5.2785 11.8482,5.4346 11.9239,5.6173C11.9996,5.8 12.0194,6.0011 11.9808,6.1951C11.9422,6.3891 11.847,6.5673 11.7071,6.7072C11.5673,6.847 11.3891,6.9423 11.1951,6.9809C11.0011,7.0194 10.8,6.9995 10.6173,6.9238C10.4346,6.8481 10.2784,6.72 10.1685,6.5556C10.0586,6.3911 10,6.1978 10,6C10,5.7348 10.1054,5.4804 10.2929,5.2929C10.4804,5.1053 10.7348,5 11,5ZM7,6C7,5.8022 7.0587,5.6089 7.1685,5.4445C7.2784,5.28 7.4346,5.1519 7.6173,5.0762C7.8,5.0005 8.0011,4.9806 8.1951,5.0192C8.3891,5.0578 8.5673,5.153 8.7071,5.2929C8.847,5.4327 8.9422,5.611 8.9808,5.8049C9.0194,5.9989 8.9996,6.2 8.9239,6.3827C8.8482,6.5654 8.72,6.7215 8.5556,6.8314C8.3911,6.9413 8.1978,7 8,7C7.7348,7 7.4804,6.8947 7.2929,6.7072C7.1054,6.5196 7,6.2652 7,6ZM12,18C11.0032,18.008 10.0441,17.6198 9.3335,16.9207C8.623,16.2216 8.2192,15.2688 8.211,14.272C8.2109,13.7806 8.3092,13.2941 8.5,12.8412C8.6908,12.3883 8.9703,11.9782 9.322,11.635L12,9L14.678,11.635C15.0297,11.9782 15.3092,12.3883 15.5,12.8412C15.6908,13.2941 15.7891,13.7806 15.789,14.272C15.7808,15.2688 15.377,16.2216 14.6665,16.9207C13.9559,17.6198 12.9968,18.008 12,18Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_doorbell_off.xml b/packages/SystemUI/res/drawable/ic_device_doorbell_off.xml
new file mode 100644
index 0000000..6c03a4b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_doorbell_off.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M17,2H7C6.4696,2 5.9609,2.2106 5.5858,2.5857C5.2107,2.9608 5,3.4696 5,4V20C5,20.5304 5.2107,21.0392 5.5858,21.4143C5.9609,21.7894 6.4696,22 7,22H17C17.5304,22 18.0391,21.7894 18.4142,21.4143C18.7893,21.0392 19,20.5304 19,20V4C19,3.4696 18.7893,2.9608 18.4142,2.5857C18.0391,2.2106 17.5304,2 17,2ZM17,20H7V4H17V20Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M12,15C11.6044,15 11.2177,15.1174 10.8889,15.3372C10.56,15.5569 10.3036,15.8692 10.1522,16.2346C10.0009,16.6001 9.9613,17.0022 10.0384,17.3901C10.1156,17.7781 10.3061,18.1346 10.5858,18.4143C10.8655,18.694 11.2219,18.8845 11.6098,18.9617C11.9978,19.0388 12.3999,18.999 12.7654,18.8477C13.1308,18.6963 13.4432,18.44 13.6629,18.1111C13.8827,17.7822 14,17.3956 14,17C14,16.4696 13.7893,15.9608 13.4142,15.5857C13.0391,15.2106 12.5304,15 12,15ZM12,18C11.8022,18 11.6089,17.9414 11.4444,17.8315C11.28,17.7217 11.1518,17.5653 11.0761,17.3826C11.0004,17.1998 10.9806,16.9989 11.0192,16.8049C11.0578,16.611 11.153,16.4328 11.2929,16.293C11.4327,16.1531 11.6109,16.0579 11.8049,16.0193C11.9989,15.9807 12.2,16.0005 12.3827,16.0762C12.5654,16.1519 12.7216,16.2799 12.8315,16.4443C12.9413,16.6088 13,16.8022 13,17C13,17.2652 12.8946,17.5195 12.7071,17.707C12.5196,17.8946 12.2652,18 12,18Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M13,12.5H11C11,12.7652 11.1054,13.0195 11.2929,13.207C11.4804,13.3946 11.7348,13.5 12,13.5C12.2652,13.5 12.5196,13.3946 12.7071,13.207C12.8946,13.0195 13,12.7652 13,12.5Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M16,11H15V8.6599C15.0532,7.9533 14.8572,7.2506 14.4462,6.6733C14.0352,6.0961 13.4351,5.6809 12.75,5.5V5.25C12.75,5.0511 12.671,4.8604 12.5303,4.7197C12.3897,4.5791 12.1989,4.5 12,4.5C11.8011,4.5 11.6103,4.5791 11.4697,4.7197C11.329,4.8604 11.25,5.0511 11.25,5.25V5.5C10.5659,5.6827 9.9669,6.0982 9.5562,6.675C9.1455,7.2519 8.9488,7.9537 9,8.6599V11H8V12H16V11Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_doorbell_on.xml b/packages/SystemUI/res/drawable/ic_device_doorbell_on.xml
new file mode 100644
index 0000000..d08393a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_doorbell_on.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12,18C12.5523,18 13,17.5523 13,17C13,16.4477 12.5523,16 12,16C11.4477,16 11,16.4477 11,17C11,17.5523 11.4477,18 12,18Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M17,2H7C6.4696,2 5.9609,2.2106 5.5858,2.5857C5.2107,2.9608 5,3.4696 5,4V20C5,20.5304 5.2107,21.0392 5.5858,21.4143C5.9609,21.7894 6.4696,22 7,22H17C17.5304,22 18.0391,21.7894 18.4142,21.4143C18.7893,21.0392 19,20.5304 19,20V4C19,3.4696 18.7893,2.9608 18.4142,2.5857C18.0391,2.2106 17.5304,2 17,2ZM12,19C11.6044,19 11.2178,18.8826 10.8889,18.6628C10.56,18.4431 10.3036,18.1308 10.1522,17.7654C10.0009,17.3999 9.9613,16.9978 10.0384,16.6099C10.1156,16.2219 10.3061,15.8654 10.5858,15.5857C10.8655,15.306 11.2219,15.1155 11.6098,15.0383C11.9978,14.9612 12.3999,15.001 12.7654,15.1523C13.1308,15.3037 13.4432,15.56 13.6629,15.8889C13.8827,16.2178 14,16.6044 14,17C14,17.5304 13.7893,18.0392 13.4142,18.4143C13.0391,18.7894 12.5304,19 12,19ZM11,12.5H13C13,12.7652 12.8946,13.0195 12.7071,13.207C12.5196,13.3946 12.2652,13.5 12,13.5C11.7348,13.5 11.4804,13.3946 11.2929,13.207C11.1054,13.0195 11,12.7652 11,12.5ZM16,12H8V11H9V8.6599C8.9488,7.9537 9.1455,7.2519 9.5562,6.675C9.9669,6.0982 10.5659,5.6827 11.25,5.5V5.25C11.25,5.0511 11.329,4.8604 11.4697,4.7197C11.6103,4.5791 11.8011,4.5 12,4.5C12.1989,4.5 12.3897,4.5791 12.5303,4.7197C12.671,4.8604 12.75,5.0511 12.75,5.25V5.5C13.4351,5.6809 14.0352,6.0961 14.4462,6.6733C14.8572,7.2506 15.0532,7.9533 15,8.6599V11H16V12Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_drawer_off.xml b/packages/SystemUI/res/drawable/ic_device_drawer_off.xml
new file mode 100644
index 0000000..bcab534
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_drawer_off.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M18,3H6C5.4696,3 4.9609,3.2109 4.5858,3.5859C4.2107,3.961 4,4.4696 4,5V21H6V19H18V21H20V5C20,4.4696 19.7893,3.961 19.4142,3.5859C19.0391,3.2109 18.5304,3 18,3ZM18,11H13V9H18V11ZM18,7H13V5H18V7ZM11,5V11H6V5H11ZM6,17V13H18V17H6Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M14,14H10V16H14V14Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_drawer_on.xml b/packages/SystemUI/res/drawable/ic_device_drawer_on.xml
new file mode 100644
index 0000000..800e9f1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_drawer_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M18,3H6C5.4696,3 4.9609,3.2109 4.5858,3.5859C4.2107,3.961 4,4.4696 4,5V21H6V19H18V21H20V5C20,4.4696 19.7893,3.961 19.4142,3.5859C19.0391,3.2109 18.5304,3 18,3ZM6,5H11V11H6V5ZM14,16H10V14H14V16ZM18,11H13V9H18V11ZM18,7H13V5H18V7Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_fan_off.xml b/packages/SystemUI/res/drawable/ic_device_fan_off.xml
new file mode 100644
index 0000000..c90d574
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_fan_off.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M16.345,8.3611L14.055,9.1791C13.8731,9.0458 13.6785,8.9309 13.474,8.8361C13.6398,7.9956 14.1302,7.2543 14.839,6.7731C15.3079,6.465 15.6646,6.0136 15.8558,5.4861C16.047,4.9587 16.0625,4.3836 15.8999,3.8466C15.7374,3.3097 15.4055,2.8397 14.9538,2.5069C14.5022,2.1741 13.955,1.9963 13.394,2.0001C8.994,2.0001 7.157,5.0071 8.361,7.6551L9.179,9.9451C9.0458,10.1269 8.9309,10.3215 8.836,10.5261C7.9954,10.3606 7.254,9.8701 6.773,9.1611C6.465,8.6922 6.0135,8.3355 5.4861,8.1443C4.9587,7.9531 4.3835,7.9375 3.8466,8.1001C3.3096,8.2627 2.8396,8.5946 2.5068,9.0462C2.174,9.4979 1.9962,10.0451 2,10.6061C2,15.0061 5.007,16.843 7.655,15.639L9.945,14.821C10.1267,14.9541 10.3209,15.069 10.525,15.1641C10.3598,16.0048 9.8692,16.7463 9.16,17.227C8.691,17.5351 8.3343,17.9867 8.1431,18.5142C7.9519,19.0418 7.9365,19.617 8.0992,20.154C8.2619,20.691 8.5939,21.161 9.0457,21.4937C9.4976,21.8264 10.0449,22.004 10.606,22.0001C15.006,22.0001 16.843,18.993 15.639,16.345L14.821,14.0551C14.954,13.8734 15.0689,13.6791 15.164,13.475C16.0048,13.6402 16.7462,14.1309 17.227,14.8401C17.5351,15.3091 17.9866,15.6657 18.5141,15.8569C19.0417,16.0481 19.6169,16.0636 20.1539,15.9009C20.6909,15.7382 21.1609,15.4061 21.4936,14.9543C21.8264,14.5025 22.004,13.9551 22,13.394C22,9 18.993,7.1571 16.345,8.3611ZM12,13.5001C11.7033,13.5001 11.4133,13.4121 11.1666,13.2473C10.92,13.0824 10.7277,12.8482 10.6142,12.5741C10.5006,12.3 10.4709,11.9984 10.5288,11.7074C10.5867,11.4164 10.7296,11.1492 10.9393,10.9394C11.1491,10.7296 11.4164,10.5867 11.7074,10.5289C11.9983,10.471 12.2999,10.5007 12.574,10.6143C12.8481,10.7278 13.0824,10.92 13.2472,11.1667C13.412,11.4134 13.5,11.7034 13.5,12.0001C13.5,12.3979 13.342,12.7794 13.0607,13.0607C12.7793,13.342 12.3978,13.5001 12,13.5001ZM10.245,5.2161C10.6327,4.7742 11.1217,4.4328 11.6701,4.2211C12.2184,4.0093 12.8099,3.9335 13.394,4.0001C13.5259,3.9959 13.6555,4.0354 13.7627,4.1124C13.8699,4.1893 13.9487,4.2995 13.987,4.4258C14.0253,4.5521 14.0208,4.6875 13.9744,4.811C13.9279,4.9346 13.842,5.0393 13.73,5.1091C13.1418,5.5017 12.6392,6.0092 12.2521,6.601C11.8651,7.1929 11.6018,7.8568 11.478,8.553C11.2666,8.5847 11.0587,8.6362 10.857,8.707L10.181,6.8271C10.0515,6.576 9.9893,6.2955 10.0005,6.0131C10.0117,5.7308 10.0959,5.4561 10.245,5.2161ZM6.827,13.816C6.576,13.9458 6.2956,14.0083 6.0132,13.9973C5.7308,13.9862 5.4561,13.902 5.216,13.753C4.7745,13.3655 4.4332,12.8769 4.2215,12.3289C4.0098,11.7809 3.9338,11.1898 4,10.6061C3.9959,10.4742 4.0353,10.3446 4.1123,10.2374C4.1893,10.1302 4.2994,10.0513 4.4257,10.0131C4.552,9.9748 4.6874,9.9792 4.8109,10.0257C4.9345,10.0722 5.0392,10.1581 5.109,10.2701C5.5015,10.8581 6.0088,11.3607 6.6005,11.7477C7.1922,12.1347 7.8559,12.3981 8.552,12.522C8.5844,12.7334 8.6363,12.9413 8.707,13.1431L6.827,13.816ZM13.755,18.782C13.3675,19.2242 12.8786,19.566 12.3302,19.7781C11.7818,19.9902 11.1902,20.0664 10.606,20.0001C10.4741,20.0042 10.3445,19.9647 10.2373,19.8878C10.1301,19.8108 10.0512,19.7006 10.013,19.5743C9.9747,19.448 9.9791,19.3126 10.0256,19.1891C10.0721,19.0656 10.158,18.9608 10.27,18.8911C10.8581,18.4987 11.3606,17.9914 11.7475,17.3997C12.1343,16.808 12.3974,16.1441 12.521,15.4481C12.7327,15.4156 12.9409,15.3638 13.143,15.2931L13.818,17.173C13.9477,17.4241 14.0101,17.7045 13.999,17.9869C13.988,18.2692 13.9039,18.5439 13.755,18.7841V18.782ZM18.891,13.7281C18.4985,13.1399 17.991,12.6373 17.3992,12.2504C16.8073,11.8636 16.1432,11.6005 15.447,11.477C15.4154,11.2653 15.3639,11.057 15.293,10.855L17.173,10.1801C17.424,10.0503 17.7044,9.9879 17.9868,9.9989C18.2692,10.0099 18.5439,10.094 18.784,10.243C19.2261,10.631 19.5677,11.1202 19.7795,11.669C19.9912,12.2178 20.0669,12.8097 20,13.394C20.0041,13.5259 19.9647,13.6555 19.8877,13.7628C19.8107,13.87 19.7006,13.9488 19.5743,13.9871C19.448,14.0253 19.3126,14.0209 19.1891,13.9744C19.0655,13.928 18.9608,13.8421 18.891,13.73V13.7281Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_fan_on.xml b/packages/SystemUI/res/drawable/ic_device_fan_on.xml
new file mode 100644
index 0000000..79950ea
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_fan_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M16.345,8.3611L14.055,9.1791C13.8731,9.0458 13.6785,8.9309 13.474,8.8361C13.6398,7.9956 14.1302,7.2543 14.839,6.7731C15.3079,6.465 15.6646,6.0136 15.8558,5.4861C16.047,4.9587 16.0625,4.3836 15.8999,3.8466C15.7374,3.3097 15.4055,2.8397 14.9538,2.5069C14.5022,2.1741 13.955,1.9963 13.394,2.0001C8.994,2.0001 7.157,5.0071 8.361,7.6551L9.179,9.9451C9.0457,10.1269 8.9309,10.3215 8.836,10.5261C7.9954,10.3606 7.254,9.8701 6.773,9.1611C6.465,8.6922 6.0135,8.3355 5.4861,8.1443C4.9587,7.9531 4.3835,7.9375 3.8466,8.1001C3.3096,8.2627 2.8396,8.5946 2.5068,9.0462C2.174,9.4979 1.9962,10.0451 2,10.6061C2,15.0061 5.007,16.843 7.655,15.639L9.945,14.821C10.1267,14.9541 10.3209,15.069 10.525,15.1641C10.3598,16.0048 9.8692,16.7463 9.16,17.227C8.691,17.5351 8.3343,17.9867 8.1431,18.5142C7.9519,19.0418 7.9365,19.617 8.0992,20.154C8.2619,20.691 8.5939,21.161 9.0457,21.4937C9.4976,21.8264 10.0449,22.004 10.606,22.0001C15.006,22.0001 16.843,18.993 15.639,16.345L14.821,14.0551C14.954,13.8734 15.0689,13.6791 15.164,13.475C16.0048,13.6402 16.7462,14.1309 17.227,14.8401C17.5351,15.3091 17.9866,15.6657 18.5141,15.8569C19.0417,16.0481 19.6169,16.0636 20.1539,15.9009C20.6909,15.7382 21.1609,15.4061 21.4936,14.9543C21.8264,14.5025 22.004,13.9551 22,13.394C22,9 18.993,7.1571 16.345,8.3611ZM12,13.5001C11.7033,13.5001 11.4133,13.4121 11.1666,13.2473C10.92,13.0824 10.7277,12.8482 10.6142,12.5741C10.5006,12.3 10.4709,11.9984 10.5288,11.7074C10.5867,11.4164 10.7296,11.1492 10.9393,10.9394C11.1491,10.7296 11.4164,10.5867 11.7074,10.5289C11.9983,10.471 12.2999,10.5007 12.574,10.6143C12.8481,10.7278 13.0824,10.92 13.2472,11.1667C13.412,11.4134 13.5,11.7034 13.5,12.0001C13.5,12.3979 13.342,12.7794 13.0607,13.0607C12.7794,13.342 12.3978,13.5001 12,13.5001Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_garage_off.xml b/packages/SystemUI/res/drawable/ic_device_garage_off.xml
new file mode 100644
index 0000000..8865983
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_garage_off.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20,9L12,3L4,9V21H6V10L12,5.5L18,10V21H20V9Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M7,11V21H17V11H7ZM15,13V15H9V13H15ZM9,19V17H15V19H9Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_garage_on.xml b/packages/SystemUI/res/drawable/ic_device_garage_on.xml
new file mode 100644
index 0000000..8865983
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_garage_on.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20,9L12,3L4,9V21H6V10L12,5.5L18,10V21H20V9Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M7,11V21H17V11H7ZM15,13V15H9V13H15ZM9,19V17H15V19H9Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_gate_off.xml b/packages/SystemUI/res/drawable/ic_device_gate_off.xml
new file mode 100644
index 0000000..9f7d9ed
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_gate_off.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M4,7H2V17H4V7Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M15,5H9C7.9391,5 6.9217,5.4215 6.1716,6.1716C5.4214,6.9218 5,7.9391 5,9V19H19V9C19,7.9391 18.5786,6.9218 17.8284,6.1716C17.0783,5.4215 16.0609,5 15,5ZM7,9C7,8.4696 7.2107,7.9608 7.5858,7.5857C7.9609,7.2106 8.4696,7 9,7H11V11H9V13H11V17H7V9ZM17,17H13V13H15V11H13V7H15C15.5304,7 16.0391,7.2106 16.4142,7.5857C16.7893,7.9608 17,8.4696 17,9V17Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M22,7H20V17H22V7Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_gate_on.xml b/packages/SystemUI/res/drawable/ic_device_gate_on.xml
new file mode 100644
index 0000000..1a005ac
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_gate_on.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M4,7H2V17H4V7Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M22,7H20V17H22V7Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M5,9V19H11V13H9V11H11V5H9C7.9391,5 6.9217,5.4215 6.1716,6.1716C5.4214,6.9218 5,7.9391 5,9Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M15,5H13V11H15V13H13V19H19V9C19,7.9391 18.5786,6.9218 17.8284,6.1716C17.0783,5.4215 16.0609,5 15,5Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_hood_off.xml b/packages/SystemUI/res/drawable/ic_device_hood_off.xml
new file mode 100644
index 0000000..71d5f24
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_hood_off.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M21.414,12.414L17,8V3H7V8L2.586,12.414C2.2109,12.789 2.0001,13.2976 2,13.828V18C2,18.5304 2.2107,19.0391 2.5858,19.4142C2.9609,19.7893 3.4696,20 4,20H20C20.5304,20 21.0391,19.7893 21.4142,19.4142C21.7893,19.0391 22,18.5304 22,18V13.828C21.9999,13.2976 21.7891,12.789 21.414,12.414ZM9,8.828V5H15V8.828L18.172,12H5.828L9,8.828ZM20,18H4V14H20V18Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M14,15.25H10V16.75H14V15.25Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_hood_on.xml b/packages/SystemUI/res/drawable/ic_device_hood_on.xml
new file mode 100644
index 0000000..721e68b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_hood_on.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M21,12L17,8V3H7V8L3,12H21Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M2,14V18C2,18.5304 2.2107,19.0391 2.5858,19.4142C2.9609,19.7893 3.4696,20 4,20H20C20.5304,20 21.0391,19.7893 21.4142,19.4142C21.7893,19.0391 22,18.5304 22,18V14H2ZM14,16.75H10V15.25H14V16.75Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_kettle_off.xml b/packages/SystemUI/res/drawable/ic_device_kettle_off.xml
new file mode 100644
index 0000000..94ad254
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_kettle_off.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20,5H18V2H3L6,6V19H18V14H20C20.5304,14 21.0391,13.7893 21.4142,13.4142C21.7893,13.0391 22,12.5304 22,12V7C22,6.4696 21.7893,5.9609 21.4142,5.5858C21.0391,5.2107 20.5304,5 20,5ZM16,17H8V5.333L7,4H16V17ZM20,12H18V7H20V12Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M15,5H12V16H15V5Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M21,20H3V22H21V20Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_kettle_on.xml b/packages/SystemUI/res/drawable/ic_device_kettle_on.xml
new file mode 100644
index 0000000..11081e8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_kettle_on.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20,5H18V2H3L6,6V19H18V14H20C20.5304,14 21.0391,13.7893 21.4142,13.4142C21.7893,13.0391 22,12.5304 22,12V7C22,6.4696 21.7893,5.9609 21.4142,5.5858C21.0391,5.2107 20.5304,5 20,5ZM15,16H12V5H15V16ZM20,12H18V7H20V12Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M21,20H3V22H21V20Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_light_off.xml b/packages/SystemUI/res/drawable/ic_device_light_off.xml
new file mode 100644
index 0000000..62fa631
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_light_off.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12,22C12.5304,22 13.0391,21.7894 13.4142,21.4143C13.7893,21.0392 14,20.5304 14,20H10C10,20.5304 10.2107,21.0392 10.5858,21.4143C10.9609,21.7894 11.4696,22 12,22Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M16,17H8V19H16V17Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M12,2C10.3491,2.0023 8.7451,2.5493 7.4367,3.5561C6.1283,4.563 5.1887,5.9734 4.7636,7.5686C4.3385,9.1638 4.4516,10.8547 5.0854,12.3792C5.7191,13.9036 6.8382,15.1764 8.269,16H15.731C17.1618,15.1764 18.2809,13.9036 18.9147,12.3792C19.5485,10.8547 19.6616,9.1638 19.2364,7.5686C18.8113,5.9734 17.8717,4.563 16.5633,3.5561C15.2549,2.5493 13.6509,2.0023 12,2ZM15.148,14H8.848C8.1191,13.4982 7.5239,12.826 7.114,12.0417C6.7041,11.2575 6.4919,10.3849 6.496,9.5C6.496,8.0413 7.0755,6.6423 8.1069,5.6108C9.1384,4.5794 10.5373,4 11.996,4C13.4547,4 14.8536,4.5794 15.8851,5.6108C16.9165,6.6423 17.496,8.0413 17.496,9.5C17.5005,10.3846 17.289,11.2568 16.8798,12.041C16.4706,12.8252 15.8761,13.4977 15.148,14Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_light_on.xml b/packages/SystemUI/res/drawable/ic_device_light_on.xml
new file mode 100644
index 0000000..08f05fc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_light_on.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12,22C12.5304,22 13.0391,21.7894 13.4142,21.4143C13.7893,21.0392 14,20.5304 14,20H10C10,20.5304 10.2107,21.0392 10.5858,21.4143C10.9609,21.7894 11.4696,22 12,22Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M16,17H8V19H16V17Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M12,2C10.3491,2.0023 8.7451,2.5493 7.4367,3.5561C6.1283,4.563 5.1887,5.9734 4.7636,7.5686C4.3385,9.1638 4.4516,10.8547 5.0854,12.3792C5.7191,13.9036 6.8382,15.1764 8.269,16H15.731C17.1618,15.1764 18.2809,13.9036 18.9147,12.3792C19.5485,10.8547 19.6616,9.1638 19.2364,7.5686C18.8113,5.9734 17.8717,4.563 16.5633,3.5561C15.2549,2.5493 13.6509,2.0023 12,2Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_lock_off.xml b/packages/SystemUI/res/drawable/ic_device_lock_off.xml
new file mode 100644
index 0000000..a2662ff
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_lock_off.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M18,8H17V6C17,4.6739 16.4732,3.402 15.5355,2.4644C14.5979,1.5267 13.3261,1 12,1C10.6739,1 9.4021,1.5267 8.4645,2.4644C7.5268,3.402 7,4.6739 7,6V8H6C5.47,8.0016 4.9623,8.2127 4.5875,8.5874C4.2128,8.9621 4.0016,9.47 4,10V20C4.0016,20.5299 4.2128,21.0379 4.5875,21.4126C4.9623,21.7873 5.47,21.9984 6,22H18C18.5299,21.9984 19.0377,21.7873 19.4125,21.4126C19.7872,21.0379 19.9984,20.5299 20,20V10C19.9984,9.47 19.7872,8.9621 19.4125,8.5874C19.0377,8.2127 18.5299,8.0016 18,8ZM9,6C9,5.2043 9.3161,4.4415 9.8787,3.8789C10.4413,3.3163 11.2044,3 12,3C12.7956,3 13.5587,3.3163 14.1213,3.8789C14.6839,4.4415 15,5.2043 15,6V8H9V6ZM18,20H6V10H18V20Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M12,17C13.1046,17 14,16.1046 14,15C14,13.8954 13.1046,13 12,13C10.8954,13 10,13.8954 10,15C10,16.1046 10.8954,17 12,17Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_lock_on.xml b/packages/SystemUI/res/drawable/ic_device_lock_on.xml
new file mode 100644
index 0000000..5ae7090
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_lock_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M18,8H17V6C17,4.6739 16.4732,3.402 15.5355,2.4644C14.5979,1.5267 13.3261,1 12,1C10.6739,1 9.4022,1.5267 8.4645,2.4644C7.5268,3.402 7,4.6739 7,6V8H6C5.47,8.0016 4.9623,8.2127 4.5875,8.5874C4.2128,8.9621 4.0016,9.47 4,10V20C4.0016,20.5299 4.2128,21.0379 4.5875,21.4126C4.9623,21.7873 5.47,21.9984 6,22H18C18.5299,21.9984 19.0377,21.7873 19.4125,21.4126C19.7872,21.0379 19.9984,20.5299 20,20V10C19.9984,9.47 19.7872,8.9621 19.4125,8.5874C19.0377,8.2127 18.5299,8.0016 18,8ZM12,17C11.6044,17 11.2178,16.8828 10.8889,16.6631C10.56,16.4433 10.3036,16.1306 10.1522,15.7651C10.0009,15.3997 9.9613,14.9978 10.0384,14.6099C10.1156,14.2219 10.3061,13.8656 10.5858,13.5859C10.8655,13.3062 11.2219,13.1157 11.6098,13.0386C11.9978,12.9614 12.3999,13.001 12.7654,13.1523C13.1308,13.3037 13.4432,13.5598 13.6629,13.8887C13.8827,14.2176 14,14.6044 14,15C13.9984,15.5299 13.7872,16.0379 13.4125,16.4126C13.0377,16.7873 12.5299,16.9984 12,17ZM15,8H9V6C9,5.2043 9.3161,4.4415 9.8787,3.8789C10.4413,3.3163 11.2044,3 12,3C12.7956,3 13.5587,3.3163 14.1213,3.8789C14.6839,4.4415 15,5.2043 15,6V8Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_microwave_off.xml b/packages/SystemUI/res/drawable/ic_device_microwave_off.xml
new file mode 100644
index 0000000..771afbb
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_microwave_off.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20,4H4C3.4696,4 2.9609,4.2107 2.5858,4.5858C2.2107,4.9609 2,5.4696 2,6V18C2,18.5304 2.2107,19.0391 2.5858,19.4142C2.9609,19.7893 3.4696,20 4,20H20C20.5304,20 21.0391,19.7893 21.4142,19.4142C21.7893,19.0391 22,18.5304 22,18V6C22,5.4696 21.7893,4.9609 21.4142,4.5858C21.0391,4.2107 20.5304,4 20,4ZM20,18H4V6H20V18Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M18,9C18.5523,9 19,8.5523 19,8C19,7.4477 18.5523,7 18,7C17.4477,7 17,7.4477 17,8C17,8.5523 17.4477,9 18,9Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M18,13C18.5523,13 19,12.5523 19,12C19,11.4477 18.5523,11 18,11C17.4477,11 17,11.4477 17,12C17,12.5523 17.4477,13 18,13Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M18,17C18.5523,17 19,16.5523 19,16C19,15.4477 18.5523,15 18,15C17.4477,15 17,15.4477 17,16C17,16.5523 17.4477,17 18,17Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M15,7H5V17H15V7ZM13,15H7V9H13V15Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_microwave_on.xml b/packages/SystemUI/res/drawable/ic_device_microwave_on.xml
new file mode 100644
index 0000000..b05f681
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_microwave_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20,4H4C3.4696,4 2.9609,4.2107 2.5858,4.5858C2.2107,4.9609 2,5.4696 2,6V18C2,18.5304 2.2107,19.0391 2.5858,19.4142C2.9609,19.7893 3.4696,20 4,20H20C20.5304,20 21.0391,19.7893 21.4142,19.4142C21.7893,19.0391 22,18.5304 22,18V6C22,5.4696 21.7893,4.9609 21.4142,4.5858C21.0391,4.2107 20.5304,4 20,4ZM15,17H5V7H15V17ZM18,17C17.8022,17 17.6089,16.9413 17.4444,16.8314C17.28,16.7215 17.1518,16.5654 17.0761,16.3827C17.0004,16.2 16.9806,15.9989 17.0192,15.8049C17.0578,15.611 17.153,15.4327 17.2929,15.2928C17.4327,15.153 17.6109,15.0578 17.8049,15.0192C17.9989,14.9806 18.2,15.0005 18.3827,15.0762C18.5654,15.1519 18.7216,15.28 18.8315,15.4445C18.9414,15.6089 19,15.8022 19,16C19,16.2652 18.8946,16.5196 18.7071,16.7072C18.5196,16.8947 18.2652,17 18,17ZM18,13C17.8022,13 17.6089,12.9413 17.4444,12.8314C17.28,12.7215 17.1518,12.5654 17.0761,12.3827C17.0004,12.2 16.9806,11.9989 17.0192,11.8049C17.0578,11.611 17.153,11.4327 17.2929,11.2928C17.4327,11.153 17.6109,11.0578 17.8049,11.0192C17.9989,10.9806 18.2,11.0005 18.3827,11.0762C18.5654,11.1519 18.7216,11.28 18.8315,11.4445C18.9414,11.6089 19,11.8022 19,12C19,12.2652 18.8946,12.5196 18.7071,12.7072C18.5196,12.8947 18.2652,13 18,13ZM18,9C17.8022,9 17.6089,8.9413 17.4444,8.8314C17.28,8.7215 17.1518,8.5654 17.0761,8.3827C17.0004,8.2 16.9806,7.9989 17.0192,7.8049C17.0578,7.6109 17.153,7.4327 17.2929,7.2929C17.4327,7.153 17.6109,7.0578 17.8049,7.0192C17.9989,6.9806 18.2,7.0005 18.3827,7.0762C18.5654,7.1519 18.7216,7.28 18.8315,7.4445C18.9414,7.6089 19,7.8022 19,8C19,8.2652 18.8946,8.5196 18.7071,8.7072C18.5196,8.8947 18.2652,9 18,9Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_mop_off.xml b/packages/SystemUI/res/drawable/ic_device_mop_off.xml
new file mode 100644
index 0000000..7fdaaea
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_mop_off.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M22.379,20.515L21,15V13C21,12.4696 20.7893,11.9609 20.4142,11.5858C20.0391,11.2107 19.5304,11 19,11H15V4C15,3.2043 14.6839,2.4413 14.1213,1.8787C13.5587,1.3161 12.7956,1 12,1C11.2043,1 10.4413,1.3161 9.8786,1.8787C9.316,2.4413 9,3.2043 9,4V11H5C4.4695,11 3.9608,11.2107 3.5858,11.5858C3.2107,11.9609 3,12.4696 3,13V15L1.621,20.515C1.5473,20.8099 1.5417,21.1178 1.6048,21.4151C1.6679,21.7125 1.798,21.9916 1.9851,22.2311C2.1722,22.4707 2.4115,22.6645 2.6847,22.7977C2.958,22.9309 3.258,23.0001 3.562,23H20.438C20.742,23.0001 21.042,22.9309 21.3152,22.7977C21.5884,22.6645 21.8277,22.4707 22.0149,22.2311C22.202,21.9916 22.332,21.7125 22.3951,21.4151C22.4582,21.1178 22.4527,20.8099 22.379,20.515ZM11,4C11,3.7348 11.1053,3.4804 11.2929,3.2929C11.4804,3.1054 11.7348,3 12,3C12.2652,3 12.5195,3.1054 12.7071,3.2929C12.8946,3.4804 13,3.7348 13,4V11H11V4ZM5,13H19V15H5V13ZM18,21V19C18,18.7348 17.8946,18.4804 17.7071,18.2929C17.5195,18.1054 17.2652,18 17,18C16.7348,18 16.4804,18.1054 16.2929,18.2929C16.1053,18.4804 16,18.7348 16,19V21H13V19C13,18.7348 12.8946,18.4804 12.7071,18.2929C12.5195,18.1054 12.2652,18 12,18C11.7348,18 11.4804,18.1054 11.2929,18.2929C11.1053,18.4804 11,18.7348 11,19V21H8V19C8,18.7348 7.8946,18.4804 7.7071,18.2929C7.5195,18.1054 7.2652,18 7,18C6.7348,18 6.4804,18.1054 6.2929,18.2929C6.1053,18.4804 6,18.7348 6,19V21H3.562L4.562,17H19.438L20.438,21H18Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_mop_on.xml b/packages/SystemUI/res/drawable/ic_device_mop_on.xml
new file mode 100644
index 0000000..8350ca1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_mop_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M22.379,20.515L21,15V13C21,12.4696 20.7893,11.9609 20.4142,11.5858C20.0391,11.2107 19.5304,11 19,11H15V4C15,3.2043 14.6839,2.4413 14.1213,1.8787C13.5587,1.3161 12.7956,1 12,1C11.2043,1 10.4413,1.3161 9.8787,1.8787C9.316,2.4413 9,3.2043 9,4V11H5C4.4695,11 3.9608,11.2107 3.5858,11.5858C3.2107,11.9609 3,12.4696 3,13V15L1.621,20.515C1.5473,20.8099 1.5417,21.1178 1.6048,21.4151C1.6679,21.7125 1.798,21.9916 1.9851,22.2311C2.1722,22.4707 2.4115,22.6645 2.6847,22.7977C2.958,22.9309 3.258,23.0001 3.562,23H20.438C20.742,23.0001 21.042,22.9309 21.3152,22.7977C21.5885,22.6645 21.8277,22.4707 22.0148,22.2311C22.202,21.9916 22.332,21.7125 22.3951,21.4151C22.4582,21.1178 22.4527,20.8099 22.379,20.515ZM18,21V19C18,18.7348 17.8946,18.4804 17.7071,18.2929C17.5195,18.1054 17.2652,18 17,18C16.7348,18 16.4804,18.1054 16.2929,18.2929C16.1053,18.4804 16,18.7348 16,19V21H13V19C13,18.7348 12.8946,18.4804 12.7071,18.2929C12.5195,18.1054 12.2652,18 12,18C11.7348,18 11.4804,18.1054 11.2929,18.2929C11.1053,18.4804 11,18.7348 11,19V21H8V19C8,18.7348 7.8946,18.4804 7.7071,18.2929C7.5195,18.1054 7.2652,18 7,18C6.7348,18 6.4804,18.1054 6.2929,18.2929C6.1053,18.4804 6,18.7348 6,19V21H3.562L4.562,17H19.438L20.438,21H18Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_multicooker_off.xml b/packages/SystemUI/res/drawable/ic_device_multicooker_off.xml
new file mode 100644
index 0000000..8a79b0d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_multicooker_off.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M19,5H16V4C16,3.4696 15.7893,2.9609 15.4142,2.5858C15.0391,2.2107 14.5304,2 14,2H10C9.4696,2 8.9609,2.2107 8.5858,2.5858C8.2107,2.9609 8,3.4696 8,4V5H5C4.4696,5 3.9609,5.2107 3.5858,5.5858C3.2107,5.9609 3,6.4696 3,7V19C3,19.5304 3.2107,20.0391 3.5858,20.4142C3.9609,20.7893 4.4696,21 5,21H19C19.5304,21 20.0391,20.7893 20.4142,20.4142C20.7893,20.0391 21,19.5304 21,19V7C21,6.4696 20.7893,5.9609 20.4142,5.5858C20.0391,5.2107 19.5304,5 19,5ZM10,4H14V5H10V4ZM19,19H5V10H7V12C7,12.5304 7.2107,13.0391 7.5858,13.4142C7.9609,13.7893 8.4696,14 9,14H15C15.5304,14 16.0391,13.7893 16.4142,13.4142C16.7893,13.0391 17,12.5304 17,12V10H19V19ZM9,12V10H15V12H9ZM5,8V7H19V8H5Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M16,18C16.5523,18 17,17.5523 17,17C17,16.4477 16.5523,16 16,16C15.4477,16 15,16.4477 15,17C15,17.5523 15.4477,18 16,18Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M12,18C12.5523,18 13,17.5523 13,17C13,16.4477 12.5523,16 12,16C11.4477,16 11,16.4477 11,17C11,17.5523 11.4477,18 12,18Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M8,18C8.5523,18 9,17.5523 9,17C9,16.4477 8.5523,16 8,16C7.4477,16 7,16.4477 7,17C7,17.5523 7.4477,18 8,18Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_multicooker_on.xml b/packages/SystemUI/res/drawable/ic_device_multicooker_on.xml
new file mode 100644
index 0000000..90ede52
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_multicooker_on.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M17,12C17,12.5304 16.7893,13.0391 16.4142,13.4142C16.0391,13.7893 15.5304,14 15,14H9C8.4696,14 7.9609,13.7893 7.5858,13.4142C7.2107,13.0391 7,12.5304 7,12V10H3V19C3,19.5304 3.2107,20.0391 3.5858,20.4142C3.9609,20.7893 4.4696,21 5,21H19C19.5304,21 20.0391,20.7893 20.4142,20.4142C20.7893,20.0391 21,19.5304 21,19V10H17V12ZM8,18C7.8022,18 7.6089,17.9413 7.4444,17.8314C7.28,17.7215 7.1518,17.5654 7.0761,17.3827C7.0004,17.2 6.9806,16.9989 7.0192,16.8049C7.0578,16.611 7.153,16.4327 7.2929,16.2928C7.4328,16.153 7.6109,16.0578 7.8049,16.0192C7.9989,15.9806 8.2,16.0005 8.3827,16.0762C8.5654,16.1519 8.7216,16.28 8.8315,16.4445C8.9413,16.6089 9,16.8022 9,17C9,17.2652 8.8946,17.5196 8.7071,17.7072C8.5196,17.8947 8.2652,18 8,18ZM12,18C11.8022,18 11.6089,17.9413 11.4444,17.8314C11.28,17.7215 11.1518,17.5654 11.0761,17.3827C11.0004,17.2 10.9806,16.9989 11.0192,16.8049C11.0578,16.611 11.153,16.4327 11.2929,16.2928C11.4327,16.153 11.6109,16.0578 11.8049,16.0192C11.9989,15.9806 12.2,16.0005 12.3827,16.0762C12.5654,16.1519 12.7216,16.28 12.8315,16.4445C12.9414,16.6089 13,16.8022 13,17C13,17.2652 12.8946,17.5196 12.7071,17.7072C12.5196,17.8947 12.2652,18 12,18ZM16,18C15.8022,18 15.6089,17.9413 15.4444,17.8314C15.28,17.7215 15.1518,17.5654 15.0761,17.3827C15.0004,17.2 14.9806,16.9989 15.0192,16.8049C15.0578,16.611 15.153,16.4327 15.2929,16.2928C15.4327,16.153 15.6109,16.0578 15.8049,16.0192C15.9989,15.9806 16.2,16.0005 16.3827,16.0762C16.5654,16.1519 16.7216,16.28 16.8315,16.4445C16.9414,16.6089 17,16.8022 17,17C17,17.2652 16.8946,17.5196 16.7071,17.7072C16.5196,17.8947 16.2652,18 16,18Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M15,10H9V12H15V10Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M21,8V7C21,6.4696 20.7893,5.9609 20.4142,5.5858C20.0391,5.2107 19.5304,5 19,5H16V4C16,3.4696 15.7893,2.9609 15.4142,2.5858C15.0391,2.2107 14.5304,2 14,2H10C9.4696,2 8.9609,2.2107 8.5858,2.5858C8.2107,2.9609 8,3.4696 8,4V5H5C4.4696,5 3.9609,5.2107 3.5858,5.5858C3.2107,5.9609 3,6.4696 3,7V8H21ZM10,4H14V5H10V4Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_outlet_off.xml b/packages/SystemUI/res/drawable/ic_device_outlet_off.xml
new file mode 100644
index 0000000..6fe7d12
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_outlet_off.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12,2C10.0222,2 8.0888,2.5865 6.4443,3.6853C4.7998,4.7841 3.5181,6.346 2.7612,8.1732C2.0043,10.0005 1.8063,12.0111 2.1922,13.9509C2.578,15.8907 3.5304,17.6725 4.9289,19.071C6.3275,20.4696 8.1093,21.422 10.0491,21.8079C11.9889,22.1937 13.9996,21.9956 15.8268,21.2388C17.6541,20.4819 19.2159,19.2002 20.3147,17.5557C21.4135,15.9112 22,13.9778 22,12C22,9.3478 20.9464,6.8043 19.0711,4.929C17.1957,3.0536 14.6522,2 12,2ZM12,20C10.4178,20 8.871,19.5308 7.5554,18.6517C6.2399,17.7727 5.2145,16.5233 4.609,15.0615C4.0035,13.5997 3.845,11.9912 4.1537,10.4393C4.4624,8.8875 5.2243,7.462 6.3432,6.3431C7.462,5.2243 8.8874,4.4624 10.4393,4.1537C11.9911,3.845 13.5997,4.0035 15.0615,4.609C16.5233,5.2145 17.7727,6.2398 18.6518,7.5554C19.5308,8.871 20,10.4177 20,12C20,14.1217 19.1572,16.1566 17.6569,17.6569C16.1566,19.1572 14.1217,20 12,20Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M9,9H7V13H9V9Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M12,15C11.7348,15 11.4804,15.1053 11.2929,15.2928C11.1054,15.4804 11,15.7348 11,16V17H13V16C13,15.7348 12.8946,15.4804 12.7071,15.2928C12.5196,15.1053 12.2652,15 12,15Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M17,9H15V13H17V9Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_outlet_on.xml b/packages/SystemUI/res/drawable/ic_device_outlet_on.xml
new file mode 100644
index 0000000..e9d80cf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_outlet_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12,2C10.0222,2 8.0888,2.5865 6.4443,3.6853C4.7998,4.7841 3.5181,6.346 2.7612,8.1732C2.0043,10.0005 1.8063,12.0111 2.1922,13.9509C2.578,15.8907 3.5304,17.6725 4.9289,19.071C6.3275,20.4696 8.1093,21.422 10.0491,21.8079C11.9889,22.1937 13.9996,21.9956 15.8268,21.2388C17.6541,20.4819 19.2159,19.2002 20.3147,17.5557C21.4135,15.9112 22,13.9778 22,12C22,9.3478 20.9464,6.8043 19.0711,4.929C17.1957,3.0536 14.6522,2 12,2ZM9,13H7V9H9V13ZM13,17H11V16C11,15.7348 11.1054,15.4804 11.2929,15.2928C11.4804,15.1053 11.7348,15 12,15C12.2652,15 12.5196,15.1053 12.7071,15.2928C12.8946,15.4804 13,15.7348 13,16V17ZM17,13H15V9H17V13Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_pergola_off.xml b/packages/SystemUI/res/drawable/ic_device_pergola_off.xml
new file mode 100644
index 0000000..b7113dc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_pergola_off.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20,2C19.7348,2 19.4804,2.1054 19.2929,2.293C19.1054,2.4805 19,2.7348 19,3V4H5V3C5,2.7348 4.8946,2.4805 4.7071,2.293C4.5196,2.1054 4.2652,2 4,2C3.7348,2 3.4804,2.1054 3.2929,2.293C3.1054,2.4805 3,2.7348 3,3V21H5V10H19V21H21V3C21,2.7348 20.8946,2.4805 20.7071,2.293C20.5196,2.1054 20.2652,2 20,2ZM5,8V6H19V8H5Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M8,18H11V21H13V18H16V16H8V18Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_pergola_on.xml b/packages/SystemUI/res/drawable/ic_device_pergola_on.xml
new file mode 100644
index 0000000..b7113dc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_pergola_on.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20,2C19.7348,2 19.4804,2.1054 19.2929,2.293C19.1054,2.4805 19,2.7348 19,3V4H5V3C5,2.7348 4.8946,2.4805 4.7071,2.293C4.5196,2.1054 4.2652,2 4,2C3.7348,2 3.4804,2.1054 3.2929,2.293C3.1054,2.4805 3,2.7348 3,3V21H5V10H19V21H21V3C21,2.7348 20.8946,2.4805 20.7071,2.293C20.5196,2.1054 20.2652,2 20,2ZM5,8V6H19V8H5Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M8,18H11V21H13V18H16V16H8V18Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_refrigerator_off.xml b/packages/SystemUI/res/drawable/ic_device_refrigerator_off.xml
new file mode 100644
index 0000000..33ad44c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_refrigerator_off.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M18,2.01L6,2C5.4696,2 4.9609,2.2107 4.5858,2.5858C4.2107,2.9609 4,3.4696 4,4V20C4.0016,20.5299 4.2128,21.0377 4.5875,21.4125C4.9623,21.7872 5.47,21.9984 6,22H18C18.5299,21.9984 19.0377,21.7872 19.4125,21.4125C19.7872,21.0377 19.9984,20.5299 20,20V4C19.9999,3.7379 19.948,3.4784 19.8473,3.2363C19.7466,2.9943 19.5991,2.7745 19.4133,2.5896C19.2275,2.4047 19.007,2.2583 18.7645,2.1588C18.5219,2.0594 18.2621,2.0088 18,2.01ZM18,20H6V10.98H18V20ZM18,9H6V4H18V9ZM8,5H10V8H8V5ZM8,12H10V17H8V12Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_refrigerator_on.xml b/packages/SystemUI/res/drawable/ic_device_refrigerator_on.xml
new file mode 100644
index 0000000..fe7a4b6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_refrigerator_on.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20,9V4C20,3.4696 19.7893,2.9609 19.4142,2.5858C19.0391,2.2107 18.5304,2 18,2H6C5.4696,2 4.9609,2.2107 4.5858,2.5858C4.2107,2.9609 4,3.4696 4,4V9H20ZM8,5H10V8H8V5Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M4,11V20C4,20.5304 4.2107,21.0391 4.5858,21.4142C4.9609,21.7893 5.4696,22 6,22H18C18.5304,22 19.0391,21.7893 19.4142,21.4142C19.7893,21.0391 20,20.5304 20,20V11H4ZM10,17H8V12H10V17Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_remote_control_off.xml b/packages/SystemUI/res/drawable/ic_device_remote_control_off.xml
new file mode 100644
index 0000000..761f6430
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_remote_control_off.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M16,1H8C7.4696,1 6.9609,1.2107 6.5858,1.5858C6.2107,1.9609 6,2.4696 6,3V21C6,21.5304 6.2107,22.0391 6.5858,22.4142C6.9609,22.7893 7.4696,23 8,23H16C16.5304,23 17.0391,22.7893 17.4142,22.4142C17.7893,22.0391 18,21.5304 18,21V3C18,2.4696 17.7893,1.9609 17.4142,1.5858C17.0391,1.2107 16.5304,1 16,1ZM16,21H8V3H16V21Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M12,10C12.5933,10 13.1734,9.824 13.6667,9.4944C14.1601,9.1648 14.5446,8.6963 14.7716,8.1481C14.9987,7.5999 15.0581,6.9966 14.9424,6.4147C14.8266,5.8327 14.5409,5.2982 14.1213,4.8787C13.7018,4.4591 13.1672,4.1734 12.5853,4.0576C12.0033,3.9419 11.4001,4.0013 10.8519,4.2284C10.3038,4.4555 9.8352,4.8399 9.5056,5.3333C9.1759,5.8266 9,6.4067 9,7C9,7.7957 9.3161,8.5587 9.8787,9.1214C10.4413,9.684 11.2044,10 12,10ZM12,6C12.1978,6 12.3911,6.0587 12.5556,6.1686C12.72,6.2785 12.8482,6.4346 12.9239,6.6173C12.9996,6.8 13.0194,7.0011 12.9808,7.1951C12.9422,7.3891 12.847,7.5673 12.7071,7.7072C12.5673,7.847 12.3891,7.9423 12.1951,7.9809C12.0011,8.0194 11.8,7.9995 11.6173,7.9238C11.4346,7.8481 11.2784,7.72 11.1685,7.5556C11.0587,7.3911 11,7.1978 11,7C11,6.7348 11.1054,6.4804 11.2929,6.2929C11.4804,6.1053 11.7348,6 12,6Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M10,14C10.5523,14 11,13.5523 11,13C11,12.4477 10.5523,12 10,12C9.4477,12 9,12.4477 9,13C9,13.5523 9.4477,14 10,14Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M14,14C14.5523,14 15,13.5523 15,13C15,12.4477 14.5523,12 14,12C13.4477,12 13,12.4477 13,13C13,13.5523 13.4477,14 14,14Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M10,17C10.5523,17 11,16.5523 11,16C11,15.4477 10.5523,15 10,15C9.4477,15 9,15.4477 9,16C9,16.5523 9.4477,17 10,17Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M14,17C14.5523,17 15,16.5523 15,16C15,15.4477 14.5523,15 14,15C13.4477,15 13,15.4477 13,16C13,16.5523 13.4477,17 14,17Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M10,20C10.5523,20 11,19.5523 11,19C11,18.4477 10.5523,18 10,18C9.4477,18 9,18.4477 9,19C9,19.5523 9.4477,20 10,20Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M14,20C14.5523,20 15,19.5523 15,19C15,18.4477 14.5523,18 14,18C13.4477,18 13,18.4477 13,19C13,19.5523 13.4477,20 14,20Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_remote_control_on.xml b/packages/SystemUI/res/drawable/ic_device_remote_control_on.xml
new file mode 100644
index 0000000..b2c55a6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_remote_control_on.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12,8C12.5523,8 13,7.5523 13,7C13,6.4477 12.5523,6 12,6C11.4477,6 11,6.4477 11,7C11,7.5523 11.4477,8 12,8Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M16,1H8C7.4696,1 6.9609,1.2107 6.5858,1.5858C6.2107,1.9609 6,2.4696 6,3V21C6,21.5304 6.2107,22.0391 6.5858,22.4142C6.9609,22.7893 7.4696,23 8,23H16C16.5304,23 17.0391,22.7893 17.4142,22.4142C17.7893,22.0391 18,21.5304 18,21V3C18,2.4696 17.7893,1.9609 17.4142,1.5858C17.0391,1.2107 16.5304,1 16,1ZM10,20C9.8022,20 9.6089,19.9413 9.4444,19.8314C9.28,19.7215 9.1518,19.5654 9.0761,19.3827C9.0004,19.2 8.9806,18.9989 9.0192,18.8049C9.0578,18.611 9.153,18.4327 9.2929,18.2928C9.4327,18.153 9.6109,18.0578 9.8049,18.0192C9.9989,17.9806 10.2,18.0005 10.3827,18.0762C10.5654,18.1519 10.7216,18.28 10.8315,18.4445C10.9414,18.6089 11,18.8022 11,19C11,19.2652 10.8946,19.5196 10.7071,19.7072C10.5196,19.8947 10.2652,20 10,20ZM10,17C9.8022,17 9.6089,16.9413 9.4444,16.8314C9.28,16.7215 9.1518,16.5654 9.0761,16.3827C9.0004,16.2 8.9806,15.9989 9.0192,15.8049C9.0578,15.611 9.153,15.4327 9.2929,15.2928C9.4327,15.153 9.6109,15.0578 9.8049,15.0192C9.9989,14.9806 10.2,15.0005 10.3827,15.0762C10.5654,15.1519 10.7216,15.28 10.8315,15.4445C10.9414,15.6089 11,15.8022 11,16C11,16.2652 10.8946,16.5196 10.7071,16.7072C10.5196,16.8947 10.2652,17 10,17ZM10,14C9.8022,14 9.6089,13.9413 9.4444,13.8314C9.28,13.7215 9.1518,13.5654 9.0761,13.3827C9.0004,13.2 8.9806,12.9989 9.0192,12.8049C9.0578,12.611 9.153,12.4327 9.2929,12.2928C9.4327,12.153 9.6109,12.0578 9.8049,12.0192C9.9989,11.9806 10.2,12.0005 10.3827,12.0762C10.5654,12.1519 10.7216,12.28 10.8315,12.4445C10.9414,12.6089 11,12.8022 11,13C11,13.2652 10.8946,13.5196 10.7071,13.7072C10.5196,13.8947 10.2652,14 10,14ZM14,20C13.8022,20 13.6089,19.9413 13.4444,19.8314C13.28,19.7215 13.1518,19.5654 13.0761,19.3827C13.0004,19.2 12.9806,18.9989 13.0192,18.8049C13.0578,18.611 13.153,18.4327 13.2929,18.2928C13.4327,18.153 13.6109,18.0578 13.8049,18.0192C13.9989,17.9806 14.2,18.0005 14.3827,18.0762C14.5654,18.1519 14.7216,18.28 14.8315,18.4445C14.9414,18.6089 15,18.8022 15,19C15,19.2652 14.8946,19.5196 14.7071,19.7072C14.5196,19.8947 14.2652,20 14,20ZM14,17C13.8022,17 13.6089,16.9413 13.4444,16.8314C13.28,16.7215 13.1518,16.5654 13.0761,16.3827C13.0004,16.2 12.9806,15.9989 13.0192,15.8049C13.0578,15.611 13.153,15.4327 13.2929,15.2928C13.4327,15.153 13.6109,15.0578 13.8049,15.0192C13.9989,14.9806 14.2,15.0005 14.3827,15.0762C14.5654,15.1519 14.7216,15.28 14.8315,15.4445C14.9414,15.6089 15,15.8022 15,16C15,16.2652 14.8946,16.5196 14.7071,16.7072C14.5196,16.8947 14.2652,17 14,17ZM14,14C13.8022,14 13.6089,13.9413 13.4444,13.8314C13.28,13.7215 13.1518,13.5654 13.0761,13.3827C13.0004,13.2 12.9806,12.9989 13.0192,12.8049C13.0578,12.611 13.153,12.4327 13.2929,12.2928C13.4327,12.153 13.6109,12.0578 13.8049,12.0192C13.9989,11.9806 14.2,12.0005 14.3827,12.0762C14.5654,12.1519 14.7216,12.28 14.8315,12.4445C14.9414,12.6089 15,12.8022 15,13C15,13.2652 14.8946,13.5196 14.7071,13.7072C14.5196,13.8947 14.2652,14 14,14ZM12,10C11.4067,10 10.8266,9.824 10.3333,9.4944C9.8399,9.1647 9.4554,8.6962 9.2284,8.1481C9.0013,7.5999 8.9419,6.9966 9.0576,6.4147C9.1734,5.8327 9.4591,5.2982 9.8787,4.8787C10.2982,4.4591 10.8328,4.1734 11.4147,4.0576C11.9967,3.9419 12.5999,4.0013 13.1481,4.2284C13.6962,4.4555 14.1648,4.8399 14.4944,5.3333C14.8241,5.8266 15,6.4067 15,7C15,7.7957 14.6839,8.5587 14.1213,9.1213C13.5587,9.684 12.7956,10 12,10Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_security_system_off.xml b/packages/SystemUI/res/drawable/ic_device_security_system_off.xml
new file mode 100644
index 0000000..7a987b2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_security_system_off.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12,2L4,5V11.0911C4,16.1361 7.413,20.854 12,22C16.587,20.854 20,16.1361 20,11.0911V5L12,2ZM18,11.0911C18.0051,12.9956 17.4351,14.8572 16.3645,16.4324C15.294,18.0075 13.7727,19.2227 12,19.9189C10.2273,19.2227 8.706,18.0075 7.6355,16.4324C6.5649,14.8572 5.9949,12.9956 6,11.0911V6.386L12,4.136L18,6.386V11.0911Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M8.464,10.939L7.05,12.353L10.586,15.8879L16.949,9.5249L15.535,8.1111L10.586,13.0601L8.464,10.939Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_security_system_on.xml b/packages/SystemUI/res/drawable/ic_device_security_system_on.xml
new file mode 100644
index 0000000..f231068
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_security_system_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M12,2L4,5V11.0911C4,16.1361 7.413,20.854 12,22C16.587,20.854 20,16.1361 20,11.0911V5L12,2ZM10.586,15.8889L7.05,12.354L8.464,10.9399L10.586,13.061L15.536,8.1111L16.95,9.5249L10.586,15.8889Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_set_top_off.xml b/packages/SystemUI/res/drawable/ic_device_set_top_off.xml
new file mode 100644
index 0000000..7c9d9ce
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_set_top_off.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M2,7V17H22V7H2ZM20,15H4V9H20V15Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M11,11H5V13H11V11Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M14,13C14.5523,13 15,12.5523 15,12C15,11.4477 14.5523,11 14,11C13.4477,11 13,11.4477 13,12C13,12.5523 13.4477,13 14,13Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M17,13C17.5523,13 18,12.5523 18,12C18,11.4477 17.5523,11 17,11C16.4477,11 16,11.4477 16,12C16,12.5523 16.4477,13 17,13Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_set_top_on.xml b/packages/SystemUI/res/drawable/ic_device_set_top_on.xml
new file mode 100644
index 0000000..c872794
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_set_top_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M2,7V17H22V7H2ZM11,13H5V11H11V13ZM14,13C13.8022,13 13.6089,12.9414 13.4444,12.8315C13.28,12.7217 13.1518,12.5653 13.0761,12.3826C13.0004,12.1998 12.9806,11.9989 13.0192,11.8049C13.0578,11.611 13.153,11.4328 13.2929,11.293C13.4327,11.1531 13.6109,11.0579 13.8049,11.0193C13.9989,10.9807 14.2,11.0005 14.3827,11.0762C14.5654,11.1519 14.7216,11.2799 14.8315,11.4443C14.9414,11.6088 15,11.8022 15,12C15,12.2652 14.8946,12.5195 14.7071,12.707C14.5196,12.8946 14.2652,13 14,13ZM17,13C16.8022,13 16.6089,12.9414 16.4444,12.8315C16.28,12.7217 16.1518,12.5653 16.0761,12.3826C16.0004,12.1998 15.9806,11.9989 16.0192,11.8049C16.0578,11.611 16.153,11.4328 16.2929,11.293C16.4327,11.1531 16.6109,11.0579 16.8049,11.0193C16.9989,10.9807 17.2,11.0005 17.3827,11.0762C17.5654,11.1519 17.7216,11.2799 17.8315,11.4443C17.9414,11.6088 18,11.8022 18,12C18,12.2652 17.8946,12.5195 17.7071,12.707C17.5196,12.8946 17.2652,13 17,13Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_sprinkler_off.xml b/packages/SystemUI/res/drawable/ic_device_sprinkler_off.xml
new file mode 100644
index 0000000..fb6e34e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_sprinkler_off.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M7.5,18H11V21H13V18H16.5V16H7.5V18Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M12,13C12.5523,13 13,12.5523 13,12C13,11.4477 12.5523,11 12,11C11.4477,11 11,11.4477 11,12C11,12.5523 11.4477,13 12,13Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M12,9C12.5523,9 13,8.5523 13,8C13,7.4477 12.5523,7 12,7C11.4477,7 11,7.4477 11,8C11,8.5523 11.4477,9 12,9Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M12,5C12.5523,5 13,4.5523 13,4C13,3.4477 12.5523,3 12,3C11.4477,3 11,3.4477 11,4C11,4.5523 11.4477,5 12,5Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M15.707,14.707C15.8469,14.5672 15.9421,14.3891 15.9808,14.1951C16.0194,14.0011 15.9996,13.7999 15.9239,13.6172C15.8482,13.4344 15.7201,13.2784 15.5556,13.1685C15.3911,13.0586 15.1978,12.9998 15,12.9998C14.8022,12.9998 14.6088,13.0586 14.4444,13.1685C14.2799,13.2784 14.1518,13.4344 14.0761,13.6172C14.0004,13.7999 13.9806,14.0011 14.0192,14.1951C14.0578,14.3891 14.1531,14.5672 14.293,14.707C14.4805,14.8945 14.7348,14.9998 15,14.9998C15.2652,14.9998 15.5195,14.8945 15.707,14.707Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M17.121,10.4641C16.9812,10.604 16.8861,10.7821 16.8476,10.9761C16.8091,11.17 16.829,11.371 16.9047,11.5537C16.9804,11.7364 17.1086,11.8924 17.273,12.0022C17.4375,12.112 17.6308,12.1707 17.8285,12.1707C18.0262,12.1707 18.2195,12.112 18.3839,12.0022C18.5484,11.8924 18.6765,11.7364 18.7523,11.5537C18.828,11.371 18.8479,11.17 18.8094,10.9761C18.7709,10.7821 18.6757,10.604 18.536,10.4641C18.4431,10.3711 18.3328,10.2972 18.2114,10.2468C18.09,10.1965 17.9599,10.1707 17.8285,10.1707C17.6971,10.1707 17.5669,10.1965 17.4455,10.2468C17.3241,10.2972 17.2139,10.3711 17.121,10.4641Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M21.364,7.6359C21.2242,7.496 21.046,7.4009 20.852,7.3622C20.658,7.3236 20.4569,7.3435 20.2742,7.4191C20.0914,7.4948 19.9352,7.6228 19.8253,7.7873C19.7155,7.9517 19.6568,8.1452 19.6568,8.343C19.6568,8.5407 19.7155,8.7342 19.8253,8.8986C19.9352,9.0631 20.0914,9.1911 20.2742,9.2668C20.4569,9.3425 20.658,9.3623 20.852,9.3237C21.046,9.2851 21.2242,9.1899 21.364,9.05C21.5515,8.8625 21.6568,8.6081 21.6568,8.343C21.6568,8.0778 21.5515,7.8235 21.364,7.6359Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M9.707,14.707C9.8469,14.5672 9.9421,14.3891 9.9808,14.1951C10.0194,14.0011 9.9996,13.7999 9.9239,13.6172C9.8482,13.4344 9.72,13.2784 9.5556,13.1685C9.3911,13.0586 9.1978,12.9998 9,12.9998C8.8022,12.9998 8.6088,13.0586 8.4444,13.1685C8.2799,13.2784 8.1518,13.4344 8.0761,13.6172C8.0004,13.7999 7.9806,14.0011 8.0192,14.1951C8.0578,14.3891 8.1531,14.5672 8.293,14.707C8.4805,14.8945 8.7348,14.9998 9,14.9998C9.2652,14.9998 9.5195,14.8945 9.707,14.707Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M5.464,10.4641C5.3243,10.604 5.2291,10.7821 5.1906,10.9761C5.1522,11.17 5.172,11.371 5.2477,11.5537C5.3235,11.7364 5.4516,11.8924 5.616,12.0022C5.7805,12.112 5.9738,12.1707 6.1715,12.1707C6.3692,12.1707 6.5625,12.112 6.727,12.0022C6.8914,11.8924 7.0196,11.7364 7.0953,11.5537C7.171,11.371 7.1909,11.17 7.1524,10.9761C7.1139,10.7821 7.0188,10.604 6.879,10.4641C6.7861,10.3711 6.6758,10.2972 6.5545,10.2468C6.4331,10.1965 6.3029,10.1707 6.1715,10.1707C6.0401,10.1707 5.91,10.1965 5.7886,10.2468C5.6672,10.2972 5.5569,10.3711 5.464,10.4641Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M2.636,7.636C2.4961,7.7758 2.4009,7.954 2.3623,8.148C2.3236,8.3419 2.3434,8.5431 2.4191,8.7258C2.4948,8.9086 2.623,9.0647 2.7874,9.1746C2.9519,9.2845 3.1452,9.3433 3.343,9.3433C3.5408,9.3433 3.7342,9.2845 3.8986,9.1746C4.0631,9.0647 4.1912,8.9086 4.2669,8.7258C4.3426,8.5431 4.3624,8.3419 4.3238,8.148C4.2852,7.954 4.1899,7.7758 4.05,7.636C3.8625,7.4485 3.6082,7.3433 3.343,7.3433C3.0779,7.3433 2.8235,7.4485 2.636,7.636Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_sprinkler_on.xml b/packages/SystemUI/res/drawable/ic_device_sprinkler_on.xml
new file mode 100644
index 0000000..a5bdf1c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_sprinkler_on.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20.657,9.343C21.2093,9.343 21.657,8.8953 21.657,8.343C21.657,7.7907 21.2093,7.343 20.657,7.343C20.1047,7.343 19.657,7.7907 19.657,8.343C19.657,8.8953 20.1047,9.343 20.657,9.343Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M7.5,18H11V21H13V18H16.5V16H7.5V18Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M12,13C12.5523,13 13,12.5523 13,12C13,11.4477 12.5523,11 12,11C11.4477,11 11,11.4477 11,12C11,12.5523 11.4477,13 12,13Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M12,9C12.5523,9 13,8.5523 13,8C13,7.4477 12.5523,7 12,7C11.4477,7 11,7.4477 11,8C11,8.5523 11.4477,9 12,9Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M12,5C12.5523,5 13,4.5523 13,4C13,3.4477 12.5523,3 12,3C11.4477,3 11,3.4477 11,4C11,4.5523 11.4477,5 12,5Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M15.707,14.707C15.8469,14.5672 15.9422,14.3891 15.9808,14.1951C16.0194,14.0011 15.9996,13.7999 15.9239,13.6172C15.8483,13.4344 15.7201,13.2784 15.5556,13.1685C15.3912,13.0586 15.1978,12.9998 15,12.9998C14.8022,12.9998 14.6089,13.0586 14.4444,13.1685C14.28,13.2784 14.1518,13.4344 14.0761,13.6172C14.0004,13.7999 13.9807,14.0011 14.0193,14.1951C14.0579,14.3891 14.1531,14.5672 14.293,14.707C14.4806,14.8945 14.7349,14.9998 15,14.9998C15.2652,14.9998 15.5195,14.8945 15.707,14.707Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M18.536,10.4642C18.3961,10.3245 18.2179,10.2293 18.024,10.1908C17.83,10.1523 17.629,10.1722 17.4464,10.2479C17.2637,10.3236 17.1076,10.4516 16.9978,10.616C16.8879,10.7805 16.8293,10.9737 16.8293,11.1715C16.8293,11.3692 16.8879,11.5627 16.9978,11.7271C17.1076,11.8916 17.2637,12.0196 17.4464,12.0953C17.629,12.171 17.83,12.1909 18.024,12.1524C18.2179,12.1139 18.3961,12.0187 18.536,11.879C18.629,11.7861 18.7028,11.6759 18.7531,11.5545C18.8034,11.4331 18.8293,11.3029 18.8293,11.1715C18.8293,11.0401 18.8034,10.9101 18.7531,10.7887C18.7028,10.6673 18.629,10.5571 18.536,10.4642Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M9.707,14.707C9.8469,14.5672 9.9422,14.3891 9.9808,14.1951C10.0194,14.0011 9.9996,13.7999 9.9239,13.6172C9.8483,13.4344 9.7201,13.2784 9.5556,13.1685C9.3912,13.0586 9.1978,12.9998 9,12.9998C8.8022,12.9998 8.6089,13.0586 8.4444,13.1685C8.28,13.2784 8.1518,13.4344 8.0761,13.6172C8.0004,13.7999 7.9807,14.0011 8.0193,14.1951C8.0579,14.3891 8.1531,14.5672 8.293,14.707C8.4806,14.8945 8.7349,14.9998 9,14.9998C9.2652,14.9998 9.5195,14.8945 9.707,14.707Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M5.464,10.4641C5.3243,10.604 5.2291,10.7821 5.1906,10.9761C5.1522,11.17 5.172,11.371 5.2477,11.5537C5.3235,11.7364 5.4516,11.8924 5.616,12.0022C5.7805,12.112 5.9738,12.1707 6.1715,12.1707C6.3692,12.1707 6.5625,12.112 6.727,12.0022C6.8914,11.8924 7.0196,11.7364 7.0953,11.5537C7.171,11.371 7.1909,11.17 7.1524,10.9761C7.1139,10.7821 7.0188,10.604 6.879,10.4641C6.7861,10.3711 6.6758,10.2972 6.5545,10.2468C6.4331,10.1965 6.3029,10.1707 6.1715,10.1707C6.0401,10.1707 5.91,10.1965 5.7886,10.2468C5.6672,10.2972 5.5569,10.3711 5.464,10.4641Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M2.636,7.636C2.4961,7.7758 2.4009,7.954 2.3623,8.148C2.3236,8.3419 2.3434,8.5431 2.4191,8.7258C2.4948,8.9086 2.623,9.0647 2.7874,9.1746C2.9519,9.2845 3.1452,9.3433 3.343,9.3433C3.5408,9.3433 3.7342,9.2845 3.8986,9.1746C4.0631,9.0647 4.1912,8.9086 4.2669,8.7258C4.3426,8.5431 4.3624,8.3419 4.3238,8.148C4.2852,7.954 4.1899,7.7758 4.05,7.636C3.8625,7.4485 3.6082,7.3433 3.343,7.3433C3.0779,7.3433 2.8235,7.4485 2.636,7.636Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_styler_off.xml b/packages/SystemUI/res/drawable/ic_device_styler_off.xml
new file mode 100644
index 0000000..4d5e3f3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_styler_off.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M19.56,11.361L13,8.4408V6.9999C13,6.7347 12.8946,6.4804 12.7071,6.2929C12.5196,6.1054 12.2652,5.9999 12,5.9999C11.8022,5.9999 11.6089,5.9413 11.4444,5.8315C11.28,5.7216 11.1518,5.5652 11.0761,5.3825C11.0004,5.1998 10.9806,4.9988 11.0192,4.8049C11.0578,4.6109 11.153,4.4327 11.2929,4.2929C11.4327,4.153 11.6109,4.0578 11.8049,4.0192C11.9989,3.9806 12.2,4.0004 12.3827,4.0761C12.5654,4.1518 12.7216,4.2798 12.8315,4.4443C12.9414,4.6087 13,4.8021 13,4.9999H15C15.0015,4.4496 14.8517,3.9095 14.5668,3.4386C14.2819,2.9678 13.8729,2.5843 13.3847,2.3303C12.8965,2.0762 12.3478,1.9613 11.7987,1.9982C11.2496,2.0351 10.7212,2.2224 10.2714,2.5395C9.8216,2.8566 9.4677,3.2915 9.2484,3.7963C9.0291,4.3011 8.9529,4.8563 9.0282,5.4015C9.1034,5.9467 9.3272,6.4608 9.6749,6.8874C10.0227,7.3139 10.4811,7.6365 11,7.82V8.4499L4.44,11.37C4.0115,11.5562 3.6468,11.8636 3.3909,12.2546C3.1351,12.6455 2.9992,13.1028 3,13.57V13.58C2.9995,13.8979 3.0617,14.2129 3.1831,14.5068C3.3046,14.8006 3.4828,15.0676 3.7076,15.2924C3.9325,15.5172 4.1994,15.6954 4.4933,15.8168C4.7871,15.9382 5.1021,16.0004 5.42,15.9999H7V21.9999H17V15.9999H18.58C18.898,16.0004 19.2129,15.9382 19.5067,15.8168C19.8006,15.6954 20.0676,15.5172 20.2924,15.2924C20.5172,15.0676 20.6954,14.8006 20.8169,14.5068C20.9383,14.2129 21.0005,13.8979 21,13.58V13.57C20.9994,13.1019 20.8631,12.644 20.6075,12.2519C20.3519,11.8598 19.988,11.5504 19.56,11.361ZM15,19.9999H9V14.9999H15V19.9999ZM18.58,13.9999H17V12.9999H7V13.9999H5.42C5.3642,13.9993 5.3091,13.9877 5.2577,13.9657C5.2064,13.9438 5.1599,13.9119 5.1209,13.872C5.0819,13.8321 5.0512,13.7846 5.0304,13.7328C5.0097,13.681 4.9993,13.6258 5,13.57C4.999,13.4889 5.0222,13.4094 5.0668,13.3417C5.1114,13.274 5.1752,13.221 5.25,13.1899L12,10.1899L18.75,13.1899C18.8245,13.2243 18.8876,13.2795 18.9319,13.3486C18.9761,13.4176 18.9998,13.4979 19,13.58C18.9995,13.6912 18.9551,13.7977 18.8764,13.8764C18.7978,13.955 18.6912,13.9994 18.58,13.9999Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_styler_on.xml b/packages/SystemUI/res/drawable/ic_device_styler_on.xml
new file mode 100644
index 0000000..58e04e0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_styler_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M19.56,11.361L13,8.4408V6.9999C13,6.7347 12.8946,6.4804 12.7071,6.2929C12.5196,6.1054 12.2652,5.9999 12,5.9999C11.8022,5.9999 11.6089,5.9413 11.4444,5.8315C11.28,5.7216 11.1518,5.5652 11.0761,5.3825C11.0004,5.1998 10.9806,4.9988 11.0192,4.8049C11.0578,4.6109 11.153,4.4327 11.2929,4.2929C11.4327,4.153 11.6109,4.0578 11.8049,4.0192C11.9989,3.9806 12.2,4.0004 12.3827,4.0761C12.5654,4.1518 12.7216,4.2798 12.8315,4.4443C12.9414,4.6087 13,4.8021 13,4.9999H15C15.0015,4.4496 14.8517,3.9095 14.5668,3.4386C14.2819,2.9678 13.8729,2.5843 13.3847,2.3303C12.8965,2.0762 12.3478,1.9613 11.7987,1.9982C11.2496,2.0351 10.7212,2.2224 10.2714,2.5395C9.8216,2.8566 9.4677,3.2915 9.2484,3.7963C9.0291,4.3011 8.9529,4.8563 9.0282,5.4015C9.1034,5.9467 9.3272,6.4608 9.6749,6.8874C10.0227,7.3139 10.4811,7.6365 11,7.82V8.4499L4.44,11.37C4.0115,11.5562 3.6468,11.8636 3.3909,12.2546C3.1351,12.6455 2.9992,13.1028 3,13.57V13.58C2.9995,13.8979 3.0617,14.2129 3.1831,14.5068C3.3046,14.8006 3.4828,15.0676 3.7076,15.2924C3.9325,15.5172 4.1994,15.6954 4.4933,15.8168C4.7871,15.9382 5.1021,16.0004 5.42,15.9999H7V21.9999H17V15.9999H18.58C18.898,16.0004 19.2129,15.9382 19.5067,15.8168C19.8006,15.6954 20.0676,15.5172 20.2924,15.2924C20.5172,15.0676 20.6954,14.8006 20.8169,14.5068C20.9383,14.2129 21.0005,13.8979 21,13.58V13.57C20.9994,13.1019 20.8631,12.644 20.6075,12.2519C20.3519,11.8598 19.988,11.5504 19.56,11.361ZM18.58,14.0009H17V13.0009H7V14.0009H5.42C5.3642,14.0002 5.3091,13.9887 5.2577,13.9667C5.2064,13.9448 5.1599,13.9129 5.1209,13.873C5.0819,13.833 5.0512,13.7859 5.0304,13.7341C5.0097,13.6822 4.9993,13.6268 5,13.571C4.999,13.4899 5.0222,13.4104 5.0668,13.3427C5.1114,13.275 5.1752,13.222 5.25,13.1908L12,10.1908L18.75,13.1908C18.8245,13.2253 18.8876,13.2804 18.9319,13.3495C18.9761,13.4186 18.9998,13.4989 19,13.581C18.9992,13.692 18.9547,13.7982 18.8761,13.8766C18.7974,13.9551 18.6911,13.9994 18.58,13.9999V14.0009Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_switch_off.xml b/packages/SystemUI/res/drawable/ic_device_switch_off.xml
new file mode 100644
index 0000000..12dcd81
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_switch_off.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M19,3H5C4.4696,3 3.9609,3.2107 3.5858,3.5858C3.2107,3.9609 3,4.4696 3,5V19C3,19.5304 3.2107,20.0391 3.5858,20.4142C3.9609,20.7893 4.4696,21 5,21H19C19.5304,21 20.0391,20.7893 20.4142,20.4142C20.7893,20.0391 21,19.5304 21,19V5C21,4.4696 20.7893,3.9609 20.4142,3.5858C20.0391,3.2107 19.5304,3 19,3ZM19,19H5V5H19V19Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M16,7H8V17H16V7ZM14,15H10V9H14V15Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M13,10H11V12H13V10Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_switch_on.xml b/packages/SystemUI/res/drawable/ic_device_switch_on.xml
new file mode 100644
index 0000000..68678a39
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_switch_on.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M14,9H10V15H14V9ZM13,12H11V10H13V12Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M19,3H5C4.4696,3 3.9609,3.2107 3.5858,3.5858C3.2107,3.9609 3,4.4696 3,5V19C3,19.5304 3.2107,20.0391 3.5858,20.4142C3.9609,20.7893 4.4696,21 5,21H19C19.5304,21 20.0391,20.7893 20.4142,20.4142C20.7893,20.0391 21,19.5304 21,19V5C21,4.4696 20.7893,3.9609 20.4142,3.5858C20.0391,3.2107 19.5304,3 19,3ZM16,17H8V7H16V17Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_thermostat_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_device_thermostat_gm2_24px.xml
deleted file mode 100644
index 45a658f..0000000
--- a/packages/SystemUI/res/drawable/ic_device_thermostat_gm2_24px.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-  <path
-      android:fillColor="#FF000000"
-      android:pathData="M18,9h-5v2h5m3,-6h-8v2h8m-9,11.97c0.62,-0.83 1,-1.85 1,-2.97 0,-1.63 -0.79,-3.09 -2,-4V6c0,-1.66 -1.34,-3 -3,-3S5,4.34 5,6v6c-1.21,0.91 -2,2.37 -2,4 0,1.12 0.38,2.14 1,2.97V19h0.02c0.91,1.21 2.35,2 3.98,2s3.06,-0.79 3.98,-2H12v-0.03zM6.2,13.6L7,13V6c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v7l0.8,0.6c0.75,0.57 1.2,1.46 1.2,2.4H5c0,-0.94 0.45,-1.84 1.2,-2.4z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_thermostat_off.xml b/packages/SystemUI/res/drawable/ic_device_thermostat_off.xml
new file mode 100644
index 0000000..1ba8741
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_thermostat_off.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M16,18.97C16.6469,18.1148 16.9979,17.0723 17,16C16.9993,15.2239 16.8183,14.4586 16.4712,13.7644C16.1241,13.0702 15.6205,12.4662 15,12V6C15,5.2043 14.6839,4.4413 14.1213,3.8787C13.5587,3.3161 12.7956,3 12,3C11.2044,3 10.4413,3.3161 9.8787,3.8787C9.3161,4.4413 9,5.2043 9,6V12C8.3795,12.4662 7.8759,13.0702 7.5288,13.7644C7.1817,14.4586 7.0007,15.2239 7,16C7.0021,17.0723 7.3531,18.1148 8,18.97V19H8.02C8.4815,19.6206 9.0818,20.1246 9.7729,20.4719C10.4639,20.8192 11.2266,21.0001 12,21.0001C12.7734,21.0001 13.5361,20.8192 14.2271,20.4719C14.9182,20.1246 15.5185,19.6206 15.98,19H16V18.97ZM10.2,13.6L11,13V6C11,5.7348 11.1054,5.4804 11.2929,5.2929C11.4804,5.1054 11.7348,5 12,5C12.2652,5 12.5196,5.1054 12.7071,5.2929C12.8946,5.4804 13,5.7348 13,6V13L13.8,13.6C14.1711,13.8809 14.4723,14.2435 14.6805,14.6598C14.8886,15.076 14.9979,15.5346 15,16H9C9.0009,15.5344 9.1098,15.0754 9.318,14.659C9.5262,14.2426 9.8281,13.8801 10.2,13.6Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_thermostat_on.xml b/packages/SystemUI/res/drawable/ic_device_thermostat_on.xml
new file mode 100644
index 0000000..1ba8741
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_thermostat_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M16,18.97C16.6469,18.1148 16.9979,17.0723 17,16C16.9993,15.2239 16.8183,14.4586 16.4712,13.7644C16.1241,13.0702 15.6205,12.4662 15,12V6C15,5.2043 14.6839,4.4413 14.1213,3.8787C13.5587,3.3161 12.7956,3 12,3C11.2044,3 10.4413,3.3161 9.8787,3.8787C9.3161,4.4413 9,5.2043 9,6V12C8.3795,12.4662 7.8759,13.0702 7.5288,13.7644C7.1817,14.4586 7.0007,15.2239 7,16C7.0021,17.0723 7.3531,18.1148 8,18.97V19H8.02C8.4815,19.6206 9.0818,20.1246 9.7729,20.4719C10.4639,20.8192 11.2266,21.0001 12,21.0001C12.7734,21.0001 13.5361,20.8192 14.2271,20.4719C14.9182,20.1246 15.5185,19.6206 15.98,19H16V18.97ZM10.2,13.6L11,13V6C11,5.7348 11.1054,5.4804 11.2929,5.2929C11.4804,5.1054 11.7348,5 12,5C12.2652,5 12.5196,5.1054 12.7071,5.2929C12.8946,5.4804 13,5.7348 13,6V13L13.8,13.6C14.1711,13.8809 14.4723,14.2435 14.6805,14.6598C14.8886,15.076 14.9979,15.5346 15,16H9C9.0009,15.5344 9.1098,15.0754 9.318,14.659C9.5262,14.2426 9.8281,13.8801 10.2,13.6Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_tv_off.xml b/packages/SystemUI/res/drawable/ic_device_tv_off.xml
new file mode 100644
index 0000000..dd91ed8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_tv_off.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20,4H4C3.4701,4.0016 2.9623,4.2129 2.5875,4.5876C2.2128,4.9624 2.0016,5.47 2,6V17C2.0016,17.5299 2.2128,18.0376 2.5875,18.4124C2.9623,18.7871 3.4701,18.9984 4,19V21H5L5.667,19H18.333L19,21H20V19C20.5299,18.9984 21.0377,18.7871 21.4125,18.4124C21.7872,18.0376 21.9984,17.5299 22,17V6C21.9984,5.47 21.7872,4.9624 21.4125,4.5876C21.0377,4.2129 20.5299,4.0016 20,4ZM20,17H4V6H20V17Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_tv_on.xml b/packages/SystemUI/res/drawable/ic_device_tv_on.xml
new file mode 100644
index 0000000..dd91ed8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_tv_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20,4H4C3.4701,4.0016 2.9623,4.2129 2.5875,4.5876C2.2128,4.9624 2.0016,5.47 2,6V17C2.0016,17.5299 2.2128,18.0376 2.5875,18.4124C2.9623,18.7871 3.4701,18.9984 4,19V21H5L5.667,19H18.333L19,21H20V19C20.5299,18.9984 21.0377,18.7871 21.4125,18.4124C21.7872,18.0376 21.9984,17.5299 22,17V6C21.9984,5.47 21.7872,4.9624 21.4125,4.5876C21.0377,4.2129 20.5299,4.0016 20,4ZM20,17H4V6H20V17Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_vacuum_off.xml b/packages/SystemUI/res/drawable/ic_device_vacuum_off.xml
new file mode 100644
index 0000000..e0fadc8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_vacuum_off.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M4,16.0001C3.4067,16.0001 2.8266,16.176 2.3333,16.5057C1.8399,16.8353 1.4554,17.3039 1.2284,17.8521C1.0013,18.4002 0.9419,19.0034 1.0576,19.5854C1.1734,20.1673 1.4591,20.7019 1.8787,21.1214C2.2982,21.541 2.8328,21.8267 3.4147,21.9425C3.9967,22.0582 4.5999,21.9988 5.148,21.7717C5.6962,21.5447 6.1648,21.1602 6.4944,20.6668C6.824,20.1735 7,19.5934 7,19.0001C7,18.2045 6.6839,17.4414 6.1213,16.8788C5.5587,16.3162 4.7957,16.0001 4,16.0001ZM4,20.0001C3.8022,20.0001 3.6089,19.9415 3.4444,19.8316C3.28,19.7217 3.1518,19.5655 3.0761,19.3828C3.0004,19.2001 2.9806,18.999 3.0192,18.805C3.0578,18.611 3.153,18.4329 3.2929,18.293C3.4327,18.1532 3.6109,18.0579 3.8049,18.0193C3.9989,17.9807 4.2,18.0005 4.3827,18.0762C4.5654,18.1519 4.7216,18.2801 4.8315,18.4445C4.9413,18.609 5,18.8023 5,19.0001C5,19.2653 4.8946,19.5197 4.7071,19.7072C4.5196,19.8947 4.2652,20.0001 4,20.0001ZM23,20.0001V22.0001H16V20.0001H18.49L12.01,4.5901C11.7747,4.0366 11.3553,3.5814 10.823,3.3016C10.2906,3.0217 9.6779,2.9344 9.0885,3.0544C8.4991,3.1744 7.9693,3.4943 7.5888,3.96C7.2082,4.4257 7.0002,5.0086 7,5.6101V9.0001H9C10.0609,9.0001 11.0783,9.4215 11.8284,10.1717C12.5786,10.9218 13,11.9392 13,13.0001V22.0001H7.99C8.4398,21.4103 8.7508,20.7267 8.9,20.0001H11V13.0001C10.9984,12.4702 10.7872,11.9624 10.4125,11.5876C10.0377,11.2129 9.5299,11.0017 9,11.0001H4V14.0001C3.3113,13.9992 2.6301,14.1422 2,14.4201V9.0001H5V5.6101C5.0002,4.5458 5.3685,3.5144 6.0426,2.6908C6.7165,1.8672 7.6547,1.3021 8.6979,1.0913C9.741,0.8806 10.825,1.0372 11.7659,1.5345C12.7068,2.0319 13.4466,2.8394 13.86,3.8201L20.66,20.0001H23Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_vacuum_on.xml b/packages/SystemUI/res/drawable/ic_device_vacuum_on.xml
new file mode 100644
index 0000000..d3b0a7d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_vacuum_on.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M4,16.0001C3.4067,16.0001 2.8266,16.176 2.3333,16.5057C1.8399,16.8353 1.4554,17.3039 1.2284,17.8521C1.0013,18.4002 0.9419,19.0034 1.0576,19.5854C1.1734,20.1673 1.4591,20.7018 1.8787,21.1214C2.2982,21.541 2.8328,21.8267 3.4147,21.9424C3.9967,22.0582 4.5999,21.9988 5.148,21.7717C5.6962,21.5447 6.1648,21.1602 6.4944,20.6668C6.824,20.1735 7,19.5934 7,19.0001C7,18.2044 6.6839,17.4414 6.1213,16.8788C5.5587,16.3162 4.7957,16.0001 4,16.0001ZM4,20.0001C3.8022,20.0001 3.6089,19.9415 3.4444,19.8316C3.28,19.7217 3.1518,19.5655 3.0761,19.3828C3.0004,19.2001 2.9806,18.999 3.0192,18.805C3.0578,18.611 3.153,18.4329 3.2929,18.293C3.4327,18.1531 3.6109,18.0579 3.8049,18.0193C3.9989,17.9807 4.2,18.0005 4.3827,18.0762C4.5654,18.1519 4.7216,18.2801 4.8315,18.4445C4.9413,18.609 5,18.8023 5,19.0001C5,19.2653 4.8946,19.5196 4.7071,19.7072C4.5196,19.8947 4.2652,20.0001 4,20.0001Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M20.66,20.0001L13.86,3.8201C13.4466,2.8394 12.7068,2.0319 11.7659,1.5345C10.825,1.0372 9.7411,0.8806 8.6979,1.0913C7.6547,1.3021 6.7165,1.8672 6.0426,2.6908C5.3685,3.5144 5.0002,4.5458 5,5.6101V9.0001H2V14.4261C2.76,14.0908 3.5918,13.9506 4.4197,14.0184C5.2476,14.0861 6.0455,14.3596 6.7409,14.814C7.4363,15.2684 8.0072,15.8893 8.4017,16.6203C8.7962,17.3514 9.0019,18.1694 9,19.0001C8.9968,20.0853 8.637,21.1394 7.976,22.0001H13V13.0001C13,11.9392 12.5786,10.9218 11.8284,10.1717C11.0783,9.4215 10.0609,9.0001 9,9.0001H7V5.6101C7.0002,5.0086 7.2082,4.4257 7.5888,3.96C7.9693,3.4943 8.4991,3.1744 9.0885,3.0544C9.6779,2.9344 10.2906,3.0217 10.823,3.3016C11.3553,3.5814 11.7747,4.0366 12.01,4.5901L18.49,20.0001H16V22.0001H23V20.0001H20.66Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_valve_off.xml b/packages/SystemUI/res/drawable/ic_device_valve_off.xml
new file mode 100644
index 0000000..5bfb46f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_valve_off.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M11,8H13V5H17V3H7V5H11V8Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M18,13V14H15V11H16V9H8V11H9V14H6V13H4V21H6V20H18V21H20V13H18ZM6,18V16H11V11H13V16H18V18H6Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_valve_on.xml b/packages/SystemUI/res/drawable/ic_device_valve_on.xml
new file mode 100644
index 0000000..66b8829
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_valve_on.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M11,8H13V5H17V3H7V5H11V8Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M18,13V14H15V11H16V9H8V11H9V14H6V13H4V21H6V20H18V21H20V13H18Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_washer_off.xml b/packages/SystemUI/res/drawable/ic_device_washer_off.xml
new file mode 100644
index 0000000..f759bcc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_washer_off.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M18,2.01L6,2C5.7371,1.9991 5.4766,2.0502 5.2336,2.1504C4.9905,2.2506 4.7696,2.3979 4.5837,2.5838C4.3978,2.7696 4.2505,2.9906 4.1504,3.2336C4.0502,3.4767 3.9991,3.7371 4,4V20C3.9991,20.2629 4.0502,20.5233 4.1504,20.7664C4.2505,21.0094 4.3978,21.2304 4.5837,21.4163C4.7696,21.6022 4.9905,21.7494 5.2336,21.8496C5.4766,21.9498 5.7371,22.0009 6,22H18C18.2629,22.0009 18.5234,21.9498 18.7665,21.8496C19.0095,21.7494 19.2304,21.6022 19.4163,21.4163C19.6022,21.2304 19.7495,21.0094 19.8497,20.7664C19.9498,20.5233 20.0009,20.2629 20,20V4C20.0007,3.7376 19.9493,3.4778 19.8489,3.2354C19.7485,2.993 19.6011,2.773 19.4151,2.5879C19.2291,2.4028 19.0083,2.2564 18.7654,2.1572C18.5225,2.0581 18.2624,2.008 18,2.01ZM18,20H6L5.993,4H18V20Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M11,7C11.5523,7 12,6.5523 12,6C12,5.4477 11.5523,5 11,5C10.4477,5 10,5.4477 10,6C10,6.5523 10.4477,7 11,7Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M8,7C8.5523,7 9,6.5523 9,6C9,5.4477 8.5523,5 8,5C7.4477,5 7,5.4477 7,6C7,6.5523 7.4477,7 8,7Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M12,18.9999C12.9889,18.9999 13.9556,18.7065 14.7778,18.1571C15.6001,17.6077 16.241,16.8269 16.6194,15.9132C16.9978,14.9996 17.0969,13.9945 16.9039,13.0246C16.711,12.0547 16.2348,11.1635 15.5355,10.4643C14.8363,9.765 13.9454,9.289 12.9754,9.0961C12.0055,8.9032 11.0002,9.0021 10.0866,9.3805C9.1729,9.759 8.3921,10.3998 7.8426,11.2221C7.2932,12.0443 7,13.011 7,13.9999C7,15.326 7.5268,16.5979 8.4645,17.5356C9.4021,18.4732 10.6739,18.9999 12,18.9999ZM14.36,11.6398C14.9689,12.2692 15.3061,13.1127 15.2989,13.9884C15.2916,14.8641 14.9405,15.702 14.3213,16.3212C13.7021,16.9404 12.8643,17.2915 11.9886,17.2987C11.1129,17.306 10.2694,16.9689 9.64,16.36L14.36,11.6398Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_washer_on.xml b/packages/SystemUI/res/drawable/ic_device_washer_on.xml
new file mode 100644
index 0000000..b624fb6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_washer_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M9.64,16.3601C10.2694,16.969 11.1129,17.3061 11.9886,17.2988C12.8643,17.2916 13.7021,16.9405 14.3213,16.3213C14.9406,15.7021 15.2916,14.8642 15.2989,13.9885C15.3061,13.1128 14.9689,12.2693 14.36,11.6399L9.64,16.3601ZM18,2.01L6,2C5.7371,1.9991 5.4766,2.0502 5.2336,2.1504C4.9905,2.2506 4.7696,2.3979 4.5837,2.5838C4.3978,2.7696 4.2505,2.9906 4.1504,3.2336C4.0502,3.4767 3.9991,3.7371 4,4V20C3.9991,20.2629 4.0502,20.5233 4.1504,20.7664C4.2505,21.0094 4.3978,21.2304 4.5837,21.4163C4.7696,21.6022 4.9905,21.7494 5.2336,21.8496C5.4766,21.9498 5.7371,22.0009 6,22H18C18.2629,22.0009 18.5234,21.9498 18.7665,21.8496C19.0095,21.7494 19.2304,21.6022 19.4163,21.4163C19.6022,21.2304 19.7495,21.0094 19.8497,20.7664C19.9498,20.5233 20.0009,20.2629 20,20V4C20.0007,3.7376 19.9493,3.4778 19.8489,3.2354C19.7485,2.993 19.6011,2.773 19.4151,2.5879C19.2291,2.4028 19.0083,2.2564 18.7654,2.1572C18.5225,2.0581 18.2624,2.008 18,2.01ZM11,5C11.1978,5 11.3911,5.0586 11.5556,5.1685C11.72,5.2783 11.8482,5.4347 11.9239,5.6174C11.9996,5.8002 12.0194,6.0011 11.9808,6.1951C11.9422,6.3891 11.847,6.5672 11.7071,6.707C11.5673,6.8469 11.3891,6.9421 11.1951,6.9807C11.0011,7.0193 10.8001,6.9995 10.6173,6.9238C10.4346,6.8481 10.2784,6.7201 10.1685,6.5557C10.0587,6.3912 10,6.1978 10,6C10,5.7348 10.1054,5.4805 10.2929,5.293C10.4804,5.1054 10.7348,5 11,5ZM8,5C8.1978,5 8.3911,5.0586 8.5556,5.1685C8.72,5.2783 8.8482,5.4347 8.9239,5.6174C8.9996,5.8002 9.0194,6.0011 8.9808,6.1951C8.9422,6.3891 8.847,6.5672 8.7071,6.707C8.5673,6.8469 8.3891,6.9421 8.1951,6.9807C8.0011,7.0193 7.8001,6.9995 7.6173,6.9238C7.4346,6.8481 7.2784,6.7201 7.1685,6.5557C7.0587,6.3912 7,6.1978 7,6C7,5.7348 7.1054,5.4805 7.2929,5.293C7.4804,5.1054 7.7348,5 8,5ZM12,19C11.0111,19 10.0444,18.7066 9.2222,18.1572C8.3999,17.6078 7.759,16.827 7.3806,15.9133C7.0022,14.9997 6.9032,13.9946 7.0961,13.0247C7.289,12.0548 7.7652,11.1636 8.4645,10.4644C9.1637,9.7651 10.0547,9.2891 11.0246,9.0962C11.9945,8.9033 12.9998,9.0022 13.9134,9.3806C14.8271,9.7591 15.608,10.3999 16.1574,11.2222C16.7068,12.0444 17,13.0111 17,14C17,15.3261 16.4732,16.598 15.5355,17.5357C14.5979,18.4733 13.3261,19 12,19Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_water_heater_off.xml b/packages/SystemUI/res/drawable/ic_device_water_heater_off.xml
new file mode 100644
index 0000000..1791958
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_water_heater_off.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M16,2H8C6.9391,2 5.9217,2.4212 5.1716,3.1714C4.4214,3.9215 4,4.9391 4,6V20C4,20.5304 4.2107,21.039 4.5858,21.4141C4.9609,21.7891 5.4696,22 6,22H18C18.5304,22 19.0391,21.7891 19.4142,21.4141C19.7893,21.039 20,20.5304 20,20V6C20,4.9391 19.5786,3.9215 18.8284,3.1714C18.0783,2.4212 17.0609,2 16,2ZM18,20H6V18C7.05,18 7.18,19 9,19C10.82,19 10.952,18 12,18C13.048,18 13.189,19 15,19C16.811,19 16.953,18 18,18V20ZM18,16C16.18,16 16.046,17 15,17C13.954,17 13.81,16 12,16C10.19,16 10.047,17 9,17C7.953,17 7.821,16 6,16V6C6,5.4696 6.2107,4.961 6.5858,4.5859C6.9609,4.2109 7.4696,4 8,4H16C16.5304,4 17.0391,4.2109 17.4142,4.5859C17.7893,4.961 18,5.4696 18,6V16Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M13.553,8.0161C13.1979,7.6625 12.9486,7.2166 12.8333,6.729C12.7179,6.2413 12.741,5.7311 12.9,5.2558C12.9134,5.2214 12.9162,5.1839 12.9082,5.1479C12.9003,5.1119 12.8818,5.0792 12.8552,5.0537C12.8286,5.0281 12.795,5.0109 12.7586,5.0044C12.7223,4.9979 12.6849,5.0023 12.6511,5.0171C8.8941,6.6581 10.4031,10.6938 10.4031,10.7568C10.4028,10.8187 10.3848,10.8792 10.3511,10.9311C10.3174,10.9831 10.2695,11.0243 10.2131,11.0498C10.2031,11.0498 9.974,11.1708 9.769,10.8838C9.5395,10.5707 9.3759,10.2145 9.2879,9.8364C9.1999,9.4583 9.1894,9.0663 9.2571,8.6841C9.2647,8.6434 9.2587,8.6012 9.2398,8.5644C9.2209,8.5276 9.1902,8.4984 9.1527,8.4809C9.1152,8.4635 9.0729,8.459 9.0326,8.4682C8.9923,8.4775 8.9562,8.5001 8.93,8.5322C8.503,9.08 8.2253,9.7288 8.1241,10.416C8.023,11.1032 8.1018,11.805 8.3528,12.4526C8.6039,13.1003 9.0187,13.6719 9.5567,14.1113C10.0946,14.5507 10.7373,14.8431 11.4221,14.9599C15.6071,15.4939 17.1871,11.2899 14.7041,8.9599C14.3381,8.6159 13.909,8.3721 13.553,8.0161ZM13.3241,12.8691C13.0354,13.1235 12.6634,13.2626 12.2787,13.2607C11.8939,13.2589 11.5233,13.116 11.2371,12.8589C11.225,12.8488 11.2159,12.836 11.2107,12.8213C11.2054,12.8065 11.2042,12.7903 11.2072,12.7749C11.2101,12.7595 11.2172,12.7452 11.2275,12.7334C11.2379,12.7216 11.2512,12.7129 11.2661,12.708C11.5164,12.6335 11.7426,12.4939 11.9216,12.3037C12.1005,12.1135 12.226,11.8794 12.2851,11.625C12.3198,11.2109 12.2684,10.7941 12.1341,10.4009C12.067,10.0764 12.087,9.74 12.1921,9.4258C12.196,9.4132 12.2035,9.4023 12.2138,9.394C12.2241,9.3858 12.2366,9.3809 12.2497,9.3799C12.2628,9.3788 12.2759,9.3817 12.2874,9.3882C12.2989,9.3946 12.3081,9.4042 12.3141,9.416C12.6801,10.236 13.8361,10.6222 13.8361,11.6162C13.843,11.8492 13.801,12.081 13.7128,12.2969C13.6246,12.5127 13.4922,12.7076 13.3241,12.8691Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_water_heater_on.xml b/packages/SystemUI/res/drawable/ic_device_water_heater_on.xml
new file mode 100644
index 0000000..ee1ca91
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_water_heater_on.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M13.324,12.8694C13.4916,12.7083 13.6238,12.5136 13.7119,12.2986C13.8001,12.0835 13.8424,11.8527 13.836,11.6203C13.836,10.6203 12.68,10.2361 12.314,9.4201C12.3081,9.4084 12.2988,9.3988 12.2874,9.3923C12.2759,9.3859 12.2628,9.3829 12.2496,9.384C12.2365,9.3851 12.224,9.3899 12.2138,9.3982C12.2035,9.4064 12.1959,9.4178 12.192,9.4304C12.087,9.7446 12.067,10.0806 12.134,10.405C12.2683,10.7982 12.3197,11.2151 12.285,11.6291C12.2259,11.8835 12.1005,12.1181 11.9215,12.3083C11.7425,12.4985 11.5164,12.6377 11.266,12.7121C11.2511,12.717 11.2378,12.7257 11.2275,12.7375C11.2171,12.7493 11.2101,12.7636 11.2071,12.779C11.2042,12.7944 11.2054,12.8106 11.2106,12.8254C11.2159,12.8402 11.225,12.8529 11.237,12.863C11.5237,13.1195 11.8947,13.2623 12.2794,13.2634C12.6641,13.2645 13.0358,13.1242 13.324,12.8694Z"
+      android:fillColor="#FF000000" />
+  <path
+      android:pathData="M16,2H8C6.9391,2 5.9217,2.4212 5.1716,3.1714C4.4214,3.9215 4,4.9391 4,6V20C4,20.5304 4.2107,21.039 4.5858,21.4141C4.9609,21.7891 5.4696,22 6,22H18C18.5304,22 19.0391,21.7891 19.4142,21.4141C19.7893,21.039 20,20.5304 20,20V6C20,4.9391 19.5786,3.9215 18.8284,3.1714C18.0783,2.4212 17.0609,2 16,2ZM8.93,8.5278C8.9561,8.4957 8.9922,8.4736 9.0325,8.4644C9.0729,8.4551 9.1151,8.4591 9.1526,8.4766C9.1902,8.494 9.2208,8.5237 9.2397,8.5605C9.2586,8.5974 9.2647,8.6395 9.257,8.6802C9.1893,9.0625 9.1998,9.4544 9.2878,9.8325C9.3758,10.2106 9.5395,10.5668 9.769,10.8799C9.969,11.1669 10.203,11.0499 10.213,11.0459C10.2694,11.0204 10.3173,10.9792 10.351,10.9272C10.3847,10.8753 10.4027,10.8148 10.403,10.7529C10.403,10.6899 8.894,6.6532 12.651,5.0132C12.6848,4.9985 12.7223,4.994 12.7586,5.0005C12.7949,5.007 12.8285,5.0238 12.8551,5.0493C12.8817,5.0749 12.9002,5.108 12.9082,5.144C12.9162,5.1801 12.9133,5.2175 12.9,5.2519C12.741,5.7272 12.7178,6.2374 12.8332,6.7251C12.9486,7.2127 13.1979,7.6586 13.553,8.0122C13.909,8.3682 14.338,8.6121 14.704,8.9541C17.187,11.2821 15.604,15.4861 11.422,14.9541C10.7376,14.8371 10.0952,14.5448 9.5576,14.1055C9.0199,13.6662 8.6053,13.0946 8.3543,12.4473C8.1033,11.7999 8.0244,11.0986 8.1252,10.4116C8.2261,9.7247 8.5034,9.0756 8.93,8.5278ZM18,20H6V16C6.7396,15.9897 7.4619,16.2246 8.054,16.668C8.3188,16.8888 8.6527,17.0098 8.9975,17.0098C9.3423,17.0098 9.6762,16.8888 9.941,16.668C10.5357,16.2281 11.2558,15.9907 11.9955,15.9907C12.7352,15.9907 13.4553,16.2281 14.05,16.668C14.3161,16.889 14.6511,17.0103 14.997,17.0103C15.3429,17.0103 15.6779,16.889 15.944,16.668C16.5368,16.2244 17.2597,15.9896 18,16V20Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_window_off.xml b/packages/SystemUI/res/drawable/ic_device_window_off.xml
new file mode 100644
index 0000000..ea4af98
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_window_off.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20,2H4C3.4701,2.0016 2.9623,2.2129 2.5875,2.5877C2.2128,2.9624 2.0016,3.4701 2,4V20C2.0016,20.5299 2.2128,21.0376 2.5875,21.4124C2.9623,21.7871 3.4701,21.9984 4,22H20C20.5299,21.9984 21.0377,21.7871 21.4125,21.4124C21.7872,21.0376 21.9984,20.5299 22,20V4C21.9984,3.4701 21.7872,2.9624 21.4125,2.5877C21.0377,2.2129 20.5299,2.0016 20,2ZM20,11H13V4H20V11ZM11,4V11H4V4H11ZM4,13H11V20H4V13ZM13,20V13H20V20H13Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_device_window_on.xml b/packages/SystemUI/res/drawable/ic_device_window_on.xml
new file mode 100644
index 0000000..ea4af98
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_device_window_on.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M20,2H4C3.4701,2.0016 2.9623,2.2129 2.5875,2.5877C2.2128,2.9624 2.0016,3.4701 2,4V20C2.0016,20.5299 2.2128,21.0376 2.5875,21.4124C2.9623,21.7871 3.4701,21.9984 4,22H20C20.5299,21.9984 21.0377,21.7871 21.4125,21.4124C21.7872,21.0376 21.9984,20.5299 22,20V4C21.9984,3.4701 21.7872,2.9624 21.4125,2.5877C21.0377,2.2129 20.5299,2.0016 20,2ZM20,11H13V4H20V11ZM11,4V11H4V4H11ZM4,13H11V20H4V13ZM13,20V13H20V20H13Z"
+      android:fillColor="#FF000000" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_light_off_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_light_off_gm2_24px.xml
deleted file mode 100644
index 78c3cc5..0000000
--- a/packages/SystemUI/res/drawable/ic_light_off_gm2_24px.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-  <path
-      android:fillColor="#FF000000"
-      android:pathData="M9,21v-1h6v1c0,0.55 -0.45,1 -1,1h-4c-0.55,0 -1,-0.45 -1,-1z"/>
-  <group>
-    <clip-path android:pathData="M0,0h24v24H0z M 0,0"/>
-  </group>
-  <path
-      android:fillColor="#FF000000"
-      android:pathData="M12,2c-1.89,0 -3.6,0.75 -4.86,1.97l1.41,1.41C9.45,4.53 10.67,4 12,4c2.76,0 5,2.24 5,5 0,1.28 -0.5,2.5 -1.36,3.42l-0.02,0.02 1.41,1.41C18.25,12.6 19,10.89 19,9c0,-3.86 -3.14,-7 -7,-7z"
-      android:fillType="evenOdd"/>
-  <path
-      android:fillColor="#FF000000"
-      android:pathData="M2.92,2.29L1.65,3.57l3.59,3.59C5.09,7.75 5,8.36 5,9c0,2.38 1.19,4.47 3,5.74V17c0,0.55 0.45,1 1,1h6c0.3,0 0.57,-0.13 0.75,-0.34L20.09,22l1.27,-1.27L2.92,2.29zM10,16v-2.3l-0.85,-0.6C7.8,12.16 7,10.63 7,9v-0.08L14.09,16H10z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lightbulb_outline_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_lightbulb_outline_gm2_24px.xml
deleted file mode 100644
index 87684a3..0000000
--- a/packages/SystemUI/res/drawable/ic_lightbulb_outline_gm2_24px.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-  <path
-      android:fillColor="#FF000000"
-      android:pathData="M9,21c0,0.55 0.45,1 1,1h4c0.55,0 1,-0.45 1,-1v-1L9,20v1zM12,2C8.14,2 5,5.14 5,9c0,2.38 1.19,4.47 3,5.74L8,17c0,0.55 0.45,1 1,1h6c0.55,0 1,-0.45 1,-1v-2.26c1.81,-1.27 3,-3.36 3,-5.74 0,-3.86 -3.14,-7 -7,-7zM14.85,13.1l-0.85,0.6L14,16h-4v-2.3l-0.85,-0.6C7.8,12.16 7,10.63 7,9c0,-2.76 2.24,-5 5,-5s5,2.24 5,5c0,1.63 -0.8,3.16 -2.15,4.1z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_lock_gm2_24px.xml
deleted file mode 100644
index f4299e6..0000000
--- a/packages/SystemUI/res/drawable/ic_lock_gm2_24px.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-  <path
-      android:fillColor="#FF000000"
-      android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM9,6c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2L9,8L9,6zM18,20L6,20L6,10h12v10zM12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_open_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_lock_open_gm2_24px.xml
deleted file mode 100644
index 59fe0a9..0000000
--- a/packages/SystemUI/res/drawable/ic_lock_open_gm2_24px.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-  <path
-      android:fillColor="#FF000000"
-      android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h2c0,-1.66 1.34,-3 3,-3s3,1.34 3,3v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM18,20L6,20L6,10h12v10z"/>
-  <path
-      android:fillColor="#FF000000"
-      android:pathData="M12,15m-2,0a2,2 0,1 1,4 0a2,2 0,1 1,-4 0"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_power_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_power_gm2_24px.xml
deleted file mode 100644
index cd95719..0000000
--- a/packages/SystemUI/res/drawable/ic_power_gm2_24px.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-  <path
-      android:fillColor="#FF000000"
-      android:pathData="M16,9v4.66l-3.5,3.51V19h-1v-1.83L8,13.65V9h8m0,-6h-2v4h-4V3H8v4h-0.01C6.9,6.99 6,7.89 6,8.98v5.52L9.5,18v3h5v-3l3.5,-3.51V9c0,-1.1 -0.9,-2 -2,-2V3z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_power_off_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_power_off_gm2_24px.xml
deleted file mode 100644
index 3eb7dd6..0000000
--- a/packages/SystemUI/res/drawable/ic_power_off_gm2_24px.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-  <path
-      android:fillColor="#FF000000"
-      android:pathData="M21.19,21.19L2.81,2.81 1.39,4.22l4.63,4.63L6,14.5 9.5,18v3h5v-3l0.34,-0.34 4.94,4.94 1.41,-1.41zM12.5,17.17L12.5,19h-1v-1.83L8,13.65v-2.83l5.42,5.42 -0.92,0.93zM11.83,9L8,5.17L8,3h2v4h4L14,3h2v4c1.1,0 2,0.9 2,2v5.49l-0.34,0.34L16,13.17L16,9h-4.17z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_switches_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_switches_gm2_24px.xml
deleted file mode 100644
index bb535ce..0000000
--- a/packages/SystemUI/res/drawable/ic_switches_gm2_24px.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-  <path
-      android:fillColor="#FF000000"
-      android:pathData="M19,9h-8.02C10.06,7.79 8.63,7 7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5c1.63,0 3.06,-0.79 3.98,-2H19c1.66,0 3,-1.34 3,-3S20.66,9 19,9zM19,13h-7.1c0.07,-0.32 0.1,-0.66 0.1,-1s-0.04,-0.68 -0.1,-1H19c0.55,0 1,0.45 1,1S19.55,13 19,13z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_vacuum_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_vacuum_gm2_24px.xml
deleted file mode 100644
index 86b9591..0000000
--- a/packages/SystemUI/res/drawable/ic_vacuum_gm2_24px.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-  <path
-      android:fillColor="#FF000000"
-      android:pathData="M4,16c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3zM4,20c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM23,20v2h-7v-2h2.49L12.01,4.59C11.6,3.63 10.66,3 9.61,3 8.17,3 7,4.17 7,5.61L7,9h2c2.21,0 4,1.79 4,4v9L7.99,22c0.44,-0.58 0.76,-1.26 0.91,-2L11,20v-7c0,-1.1 -0.9,-2 -2,-2L4,11v3c-0.71,0 -1.39,0.15 -2,0.42L2,9h3L5,5.61C5,3.07 7.07,1 9.61,1c1.86,0 3.53,1.11 4.25,2.82L20.66,20L23,20z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_videocam_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_videocam_gm2_24px.xml
deleted file mode 100644
index 687c9c4..0000000
--- a/packages/SystemUI/res/drawable/ic_videocam_gm2_24px.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-  <path
-      android:fillColor="#FF000000"
-      android:pathData="M18,10.48L18,6c0,-1.1 -0.9,-2 -2,-2L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-4.48l4,3.98v-11l-4,3.98zM16,9.69L16,18L4,18L4,6h12v3.69z"/>
-</vector>
diff --git a/packages/SystemUI/res/layout/bubble_overflow_activity.xml b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
index a06f434..65b04fd 100644
--- a/packages/SystemUI/res/layout/bubble_overflow_activity.xml
+++ b/packages/SystemUI/res/layout/bubble_overflow_activity.xml
@@ -19,6 +19,7 @@
     android:id="@+id/bubble_overflow_container"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:paddingTop="@dimen/bubble_overflow_padding"
     android:orientation="vertical"
     android:layout_gravity="center_horizontal">
 
diff --git a/packages/SystemUI/res/layout/bubble_overflow_view.xml b/packages/SystemUI/res/layout/bubble_overflow_view.xml
new file mode 100644
index 0000000..d67c81d
--- /dev/null
+++ b/packages/SystemUI/res/layout/bubble_overflow_view.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/bubble_overflow_view"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <com.android.systemui.bubbles.BadgedImageView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/bubble_view"
+        android:layout_gravity="center"
+        android:layout_width="@dimen/individual_bubble_size"
+        android:layout_height="@dimen/individual_bubble_size"/>
+
+    <TextView
+        android:id="@+id/bubble_view_name"
+        android:fontFamily="@*android:string/config_bodyFontFamily"
+        android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"
+        android:textColor="?android:attr/textColorSecondary"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:maxLines="1"
+        android:layout_gravity="center"
+        android:gravity="center"/>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml
index 7708b8e..b83e500 100644
--- a/packages/SystemUI/res/layout/controls_base_item.xml
+++ b/packages/SystemUI/res/layout/controls_base_item.xml
@@ -30,8 +30,8 @@
 
     <ImageView
         android:id="@+id/icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_width="@dimen/control_icon_size"
+        android:layout_height="@dimen/control_icon_size"
         android:paddingTop="@dimen/control_padding_adjustment"
         android:clickable="false"
         android:focusable="false"
@@ -50,6 +50,7 @@
         app:layout_constraintBottom_toBottomOf="@+id/icon"
         app:layout_constraintStart_toEndOf="@+id/icon" />
 
+
     <TextView
         android:id="@+id/status_extra"
         android:layout_width="wrap_content"
@@ -64,7 +65,7 @@
 
     <TextView
         android:id="@+id/title"
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:textAppearance="@style/TextAppearance.Control.Title"
         android:paddingLeft="@dimen/control_padding_adjustment"
@@ -73,12 +74,20 @@
         android:focusable="false"
         android:maxLines="1"
         android:ellipsize="end"
-        app:layout_constraintBottom_toTopOf="@+id/subtitle"
-        app:layout_constraintStart_toStartOf="parent" />
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/barrier"/>
+
+    <androidx.constraintlayout.widget.Barrier
+        android:id="@+id/barrier"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:barrierDirection="top"
+        app:constraint_referenced_ids="subtitle,favorite" />
 
     <TextView
         android:id="@+id/subtitle"
-        android:layout_width="wrap_content"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:textAppearance="@style/TextAppearance.Control.Subtitle"
         android:paddingLeft="@dimen/control_padding_adjustment"
@@ -88,24 +97,22 @@
         android:focusable="false"
         android:maxLines="1"
         android:ellipsize="end"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/favorite"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintStart_toStartOf="parent"/>
+    />
 
-    <FrameLayout
-        android:id="@+id/favorite_container"
+    <CheckBox
+        android:id="@+id/favorite"
         android:visibility="gone"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom|end"
+        android:button="@drawable/controls_btn_star"
+        android:layout_marginTop="4dp"
+        android:layout_marginStart="4dp"
+        app:layout_constraintStart_toEndOf="@id/subtitle"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent">
-
-        <CheckBox
-            android:id="@+id/favorite"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="bottom|end"
-            android:button="@drawable/controls_btn_star"/>
-    </FrameLayout>
-
+        app:layout_constraintBottom_toBottomOf="parent"/>
 
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/keyguard_media_header.xml b/packages/SystemUI/res/layout/keyguard_media_header.xml
index 9c2d244..de9ef21 100644
--- a/packages/SystemUI/res/layout/keyguard_media_header.xml
+++ b/packages/SystemUI/res/layout/keyguard_media_header.xml
@@ -52,7 +52,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="horizontal"
-        android:gravity="center_horizontal|fill_vertical"
+        android:gravity="center"
         android:padding="16dp"
     >
         <ImageView
@@ -67,7 +67,7 @@
         <LinearLayout
             android:orientation="vertical"
             android:layout_width="0dp"
-            android:layout_height="@dimen/qs_media_album_size"
+            android:layout_height="wrap_content"
             android:layout_weight="1"
         >
             <LinearLayout
diff --git a/packages/SystemUI/res/layout/qs_media_panel.xml b/packages/SystemUI/res/layout/qs_media_panel.xml
index 9ef8c1d..fe8557b 100644
--- a/packages/SystemUI/res/layout/qs_media_panel.xml
+++ b/packages/SystemUI/res/layout/qs_media_panel.xml
@@ -56,7 +56,7 @@
             <LinearLayout
                 android:orientation="vertical"
                 android:layout_width="0dp"
-                android:layout_height="@dimen/qs_media_album_size"
+                android:layout_height="wrap_content"
                 android:layout_weight="1"
             >
                 <LinearLayout
diff --git a/packages/SystemUI/res/values-television/dimens.xml b/packages/SystemUI/res/values-television/dimens.xml
new file mode 100644
index 0000000..6da0c69
--- /dev/null
+++ b/packages/SystemUI/res/values-television/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<resources>
+    <!-- Opacity at which the background for the shutdown UI will be drawn. -->
+    <item name="shutdown_scrim_behind_alpha" format="float" type="dimen">1.0</item>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-television/styles.xml b/packages/SystemUI/res/values-television/styles.xml
index b59f007..b01c5d8 100644
--- a/packages/SystemUI/res/values-television/styles.xml
+++ b/packages/SystemUI/res/values-television/styles.xml
@@ -17,4 +17,9 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
     <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Dialog" />
     <style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Dialog.Alert" />
+
+    <style name="Animation.ShutdownUi">
+        <item name="android:windowEnterAnimation">@null</item>
+        <item name="android:windowExitAnimation">@null</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e45cbec..864442e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1150,8 +1150,8 @@
     <dimen name="bubble_overflow_height">380dp</dimen>
     <!-- Bubble overflow padding when there are no bubbles  -->
     <dimen name="bubble_overflow_empty_state_padding">16dp</dimen>
-    <!-- Margin of overflow bubbles -->
-    <dimen name="bubble_overflow_margin">16dp</dimen>
+    <!-- Padding of container for overflow bubbles -->
+    <dimen name="bubble_overflow_padding">5dp</dimen>
     <!-- Height of the triangle that points to the expanded bubble -->
     <dimen name="bubble_pointer_height">4dp</dimen>
     <!-- Width of the triangle that points to the expanded bubble -->
@@ -1225,6 +1225,7 @@
     <dimen name="controls_top_margin">44dp</dimen>
     <dimen name="control_header_text_size">22sp</dimen>
     <dimen name="control_text_size">14sp</dimen>
+    <dimen name="control_icon_size">24dp</dimen>
     <dimen name="control_spacing">4dp</dimen>
     <dimen name="control_list_divider">1dp</dimen>
     <dimen name="control_corner_radius">12dp</dimen>
@@ -1265,4 +1266,7 @@
     <dimen name="screenrecord_status_icon_radius">5dp</dimen>
 
     <dimen name="kg_user_switcher_text_size">16sp</dimen>
+
+    <!-- Opacity at which the background for the shutdown UI will be drawn. -->
+    <item name="shutdown_scrim_behind_alpha" format="float" type="dimen">0.95</item>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c8c35c7..b779130 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -374,9 +374,31 @@
     <string name="biometric_dialog_wrong_password">Wrong password</string>
     <!-- Error string shown when the user enters too many incorrect attempts [CHAR LIMIT=120]-->
     <string name="biometric_dialog_credential_too_many_attempts">Too many incorrect attempts.\nTry again in <xliff:g id="number">%d</xliff:g> seconds.</string>
+
     <!-- Error string shown when the user enters an incorrect PIN/pattern/password and it counts towards the max attempts before the data on the device is wiped. [CHAR LIMIT=NONE]-->
     <string name="biometric_dialog_credential_attempts_before_wipe">Try again. Attempt <xliff:g id="attempts" example="1">%1$d</xliff:g> of <xliff:g id="max_attempts" example="3">%2$d</xliff:g>.</string>
 
+    <!-- Title of a dialog shown when the user only has one attempt left to provide the correct PIN/pattern/password before the device, one of its users, or a work profile is wiped. [CHAR LIMIT=NONE] -->
+    <string name="biometric_dialog_last_attempt_before_wipe_dialog_title">Your data will be deleted</string>
+    <!-- Content of a dialog shown when the user only has one attempt left to provide the correct lock pattern before the device is wiped. [CHAR LIMIT=NONE] -->
+    <string name="biometric_dialog_last_pattern_attempt_before_wipe_device">If you enter an incorrect pattern on the next attempt, this device\u2019s data will be deleted.</string>
+    <!-- Content of a dialog shown when the user only has one attempt left to provide the correct PIN before the device is wiped. [CHAR LIMIT=NONE] -->
+    <string name="biometric_dialog_last_pin_attempt_before_wipe_device">If you enter an incorrect PIN on the next attempt, this device\u2019s data will be deleted.</string>
+    <!-- Content of a dialog shown when the user only has one attempt left to provide the correct password before the device is wiped. [CHAR LIMIT=NONE] -->
+    <string name="biometric_dialog_last_password_attempt_before_wipe_device">If you enter an incorrect password on the next attempt, this device\u2019s data will be deleted.</string>
+    <!-- Content of a dialog shown when the user only has one attempt left to provide the correct lock pattern before the user is removed. [CHAR LIMIT=NONE] -->
+    <string name="biometric_dialog_last_pattern_attempt_before_wipe_user">If you enter an incorrect pattern on the next attempt, this user will be deleted.</string>
+    <!-- Content of a dialog shown when the user only has one attempt left to provide the correct PIN before the user is removed. [CHAR LIMIT=NONE] -->
+    <string name="biometric_dialog_last_pin_attempt_before_wipe_user">If you enter an incorrect PIN on the next attempt, this user will be deleted.</string>
+    <!-- Content of a dialog shown when the user only has one attempt left to provide the correct password before the user is removed. [CHAR LIMIT=NONE] -->
+    <string name="biometric_dialog_last_password_attempt_before_wipe_user">If you enter an incorrect password on the next attempt, this user will be deleted.</string>
+    <!-- Content of a dialog shown when the user only has one attempt left to provide the correct pattern before the work profile is removed. [CHAR LIMIT=NONE] -->
+    <string name="biometric_dialog_last_pattern_attempt_before_wipe_profile">If you enter an incorrect pattern on the next attempt, your work profile and its data will be deleted.</string>
+    <!-- Content of a dialog shown when the user only has one attempt left to provide the correct PIN before the work profile is removed. [CHAR LIMIT=NONE] -->
+    <string name="biometric_dialog_last_pin_attempt_before_wipe_profile">If you enter an incorrect PIN on the next attempt, your work profile and its data will be deleted.</string>
+    <!-- Content of a dialog shown when the user only has one attempt left to provide the correct password before the work profile is removed. [CHAR LIMIT=NONE] -->
+    <string name="biometric_dialog_last_password_attempt_before_wipe_profile">If you enter an incorrect password on the next attempt, your work profile and its data will be deleted.</string>
+
     <!-- Content of a dialog shown when the user has failed to provide the device lock too many times and the device is wiped. [CHAR LIMIT=NONE] -->
     <string name="biometric_dialog_failed_attempts_now_wiping_device">Too many incorrect attempts. This device\u2019s data will be deleted.</string>
     <!-- Content of a dialog shown when the user has failed to provide the user lock too many times and the user is removed. [CHAR LIMIT=NONE] -->
@@ -2665,9 +2687,9 @@
     <!-- Controls management controls screen header for Other zone [CHAR LIMIT=60] -->
     <string name="controls_favorite_other_zone_header">Other</string>
 
-    <!-- Controls dialog title [CHAR LIMIT=30] -->
+    <!-- Controls dialog title [CHAR LIMIT=40] -->
     <string name="controls_dialog_title">Add to quick controls</string>
-    <!-- Controls dialog add to favorites [CHAR LIMIT=30] -->
+    <!-- Controls dialog add to favorites [CHAR LIMIT=40] -->
     <string name="controls_dialog_ok">Add to favorites</string>
     <!-- Controls dialog message [CHAR LIMIT=NONE] -->
     <string name="controls_dialog_message"><xliff:g id="app" example="System UI">%s</xliff:g> suggested this control to add to your favorites.</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 5e6af8d..1598465 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -316,6 +316,9 @@
         <item name="android:windowExitAnimation">@null</item>
     </style>
 
+    <style name="Animation.ShutdownUi" parent="@android:style/Animation.Toast">
+    </style>
+
     <!-- Standard animations for hiding and showing the status bar. -->
     <style name="Animation.StatusBar">
     </style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 49e3e57..3bda3c8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -20,9 +20,7 @@
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
 import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -38,7 +36,6 @@
 import android.app.AppGlobals;
 import android.app.IAssistDataReceiver;
 import android.app.WindowConfiguration;
-import android.app.WindowConfiguration.ActivityType;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -113,15 +110,18 @@
      * @return the top running task (can be {@code null}).
      */
     public ActivityManager.RunningTaskInfo getRunningTask() {
-        return getRunningTask(ACTIVITY_TYPE_RECENTS /* ignoreActivityType */);
+        return getRunningTask(false /* filterVisibleRecents */);
     }
 
-    public ActivityManager.RunningTaskInfo getRunningTask(@ActivityType int ignoreActivityType) {
+    /**
+     * @return the top running task filtering only for tasks that can be visible in the recent tasks
+     * list (can be {@code null}).
+     */
+    public ActivityManager.RunningTaskInfo getRunningTask(boolean filterOnlyVisibleRecents) {
         // Note: The set of running tasks from the system is ordered by recency
         try {
             List<ActivityManager.RunningTaskInfo> tasks =
-                    ActivityTaskManager.getService().getFilteredTasks(1, ignoreActivityType,
-                            WINDOWING_MODE_PINNED /* ignoreWindowingMode */);
+                    ActivityTaskManager.getService().getFilteredTasks(1, filterOnlyVisibleRecents);
             if (tasks.isEmpty()) {
                 return null;
             }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 07bd3a0..1369350 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -73,14 +73,19 @@
     public static final int SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED = 1 << 9;
     // The search feature is disabled (either by SUW/SysUI/device policy)
     public static final int SYSUI_STATE_SEARCH_DISABLED = 1 << 10;
-    // The notification panel is expanded and interactive (either locked or unlocked), and the
-    // quick settings is not expanded
+    // The notification panel is expanded and interactive (either locked or unlocked), and quick
+    // settings is expanded.
     public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11;
     // Winscope tracing is enabled
     public static final int SYSUI_STATE_TRACING_ENABLED = 1 << 12;
     // The Assistant gesture should be constrained. It is up to the launcher implementation to
     // decide how to constrain it
     public static final int SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED = 1 << 13;
+    // The bubble stack is expanded. This means that the home gesture should be ignored, since a
+    // swipe up is an attempt to close the bubble stack, but that the back gesture should remain
+    // enabled (since it's used to navigate back within the bubbled app, or to collapse the bubble
+    // stack.
+    public static final int SYSUI_STATE_BUBBLES_EXPANDED = 1 << 14;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({SYSUI_STATE_SCREEN_PINNING,
@@ -96,7 +101,8 @@
             SYSUI_STATE_HOME_DISABLED,
             SYSUI_STATE_SEARCH_DISABLED,
             SYSUI_STATE_TRACING_ENABLED,
-            SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED
+            SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED,
+            SYSUI_STATE_BUBBLES_EXPANDED
     })
     public @interface SystemUiStateFlags {}
 
@@ -118,6 +124,7 @@
         str.add((flags & SYSUI_STATE_TRACING_ENABLED) != 0 ? "tracing" : "");
         str.add((flags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0
                 ? "asst_gesture_constrain" : "");
+        str.add((flags & SYSUI_STATE_BUBBLES_EXPANDED) != 0 ? "bubbles_expanded" : "");
         return str.toString();
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMedia.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardMedia.kt
new file mode 100644
index 0000000..487c295
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMedia.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 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.keyguard
+
+import android.graphics.drawable.Drawable
+
+import java.util.List
+
+/** State for lock screen media controls. */
+data class KeyguardMedia(
+    val foregroundColor: Int,
+    val backgroundColor: Int,
+    val app: String?,
+    val appIcon: Drawable?,
+    val artist: String?,
+    val song: String?,
+    val artwork: Drawable?,
+    val actionIcons: List<Drawable>
+)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
index b001726..d154434 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMediaPlayer.java
@@ -32,6 +32,9 @@
 
 import androidx.core.graphics.drawable.RoundedBitmapDrawable;
 import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.Observer;
 import androidx.palette.graphics.Palette;
 
 import com.android.internal.util.ContrastColorUtil;
@@ -64,39 +67,47 @@
 
     private final Context mContext;
     private final Executor mBackgroundExecutor;
-    private float mAlbumArtRadius;
-    private int mAlbumArtSize;
-    private View mMediaNotifView;
+    private final KeyguardMediaViewModel mViewModel;
+    private KeyguardMediaObserver mObserver;
 
     @Inject
     public KeyguardMediaPlayer(Context context, @Background Executor backgroundExecutor) {
         mContext = context;
         mBackgroundExecutor = backgroundExecutor;
-        loadDimens();
+        mViewModel = new KeyguardMediaViewModel(context);
     }
 
     /** Binds media controls to a view hierarchy. */
     public void bindView(View v) {
-        if (mMediaNotifView != null) {
+        if (mObserver != null) {
             throw new IllegalStateException("cannot bind views, already bound");
         }
-        mMediaNotifView = v;
-        loadDimens();
+        mViewModel.loadDimens();
+        mObserver = new KeyguardMediaObserver(v);
+        // Control buttons
+        for (int i = 0; i < ACTION_IDS.length; i++) {
+            ImageButton button = v.findViewById(ACTION_IDS[i]);
+            if (button == null) {
+                continue;
+            }
+            final int index = i;
+            button.setOnClickListener(unused -> mViewModel.onActionClick(index));
+        }
+        mViewModel.getKeyguardMedia().observeForever(mObserver);
     }
 
     /** Unbinds media controls. */
     public void unbindView() {
-        if (mMediaNotifView == null) {
+        if (mObserver == null) {
             throw new IllegalStateException("cannot unbind views, nothing bound");
         }
-        mMediaNotifView = null;
+        mViewModel.getKeyguardMedia().removeObserver(mObserver);
+        mObserver = null;
     }
 
     /** Clear the media controls because there isn't an active session. */
     public void clearControls() {
-        if (mMediaNotifView != null) {
-            mMediaNotifView.setVisibility(View.GONE);
-        }
+        mBackgroundExecutor.execute(mViewModel::clearControls);
     }
 
     /**
@@ -110,157 +121,244 @@
      */
     public void updateControls(NotificationEntry entry, Icon appIcon,
             MediaMetadata mediaMetadata) {
-        if (mMediaNotifView == null) {
+        if (mObserver == null) {
             throw new IllegalStateException("cannot update controls, views not bound");
         }
         if (mediaMetadata == null) {
-            throw new IllegalArgumentException("media metadata was null");
+            Log.d(TAG, "media metadata was null, closing media controls");
+            // Note that clearControls() executes on the same background executor, so there
+            // shouldn't be an issue with an outdated update running after clear. However, if stale
+            // controls are observed then consider removing any enqueued updates.
+            clearControls();
+            return;
         }
-        mMediaNotifView.setVisibility(View.VISIBLE);
+        mBackgroundExecutor.execute(() -> mViewModel.updateControls(entry, appIcon, mediaMetadata));
+    }
 
-        Notification notif = entry.getSbn().getNotification();
+    /** ViewModel for KeyguardMediaControls. */
+    private static final class KeyguardMediaViewModel {
 
-        // Computed foreground and background color based on album art.
-        int fgColor = notif.color;
-        int bgColor = entry.getRow() == null ? -1 : entry.getRow().getCurrentBackgroundTint();
-        Bitmap artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
-        if (artworkBitmap == null) {
-            artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
-        }
-        if (artworkBitmap != null) {
-            // If we have art, get colors from that
-            Palette p = MediaNotificationProcessor.generateArtworkPaletteBuilder(artworkBitmap)
-                    .generate();
-            Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(p);
-            bgColor = swatch.getRgb();
-            fgColor = MediaNotificationProcessor.selectForegroundColor(bgColor, p);
-        }
-        // Make sure colors will be legible
-        boolean isDark = !ContrastColorUtil.isColorLight(bgColor);
-        fgColor = ContrastColorUtil.resolveContrastColor(mContext, fgColor, bgColor,
-                isDark);
-        fgColor = ContrastColorUtil.ensureTextContrast(fgColor, bgColor, isDark);
+        private final Context mContext;
+        private final MutableLiveData<KeyguardMedia> mMedia = new MutableLiveData<>();
+        private final Object mActionsLock = new Object();
+        private List<PendingIntent> mActions;
+        private float mAlbumArtRadius;
+        private int mAlbumArtSize;
 
-        // Album art
-        ImageView albumView = mMediaNotifView.findViewById(R.id.album_art);
-        if (albumView != null) {
-            // Resize art in a background thread
-            final Bitmap bm = artworkBitmap;
-            mBackgroundExecutor.execute(() -> processAlbumArt(bm, albumView));
+        KeyguardMediaViewModel(Context context) {
+            mContext = context;
+            loadDimens();
         }
 
-        // App icon
-        ImageView appIconView = mMediaNotifView.findViewById(R.id.icon);
-        if (appIconView != null) {
-            Drawable iconDrawable = appIcon.loadDrawable(mContext);
-            iconDrawable.setTint(fgColor);
-            appIconView.setImageDrawable(iconDrawable);
+        /** Close the media player because there isn't an active session. */
+        public void clearControls() {
+            synchronized (mActionsLock) {
+                mActions = null;
+            }
+            mMedia.postValue(null);
         }
 
-        // App name
-        TextView appName = mMediaNotifView.findViewById(R.id.app_name);
-        if (appName != null) {
+        /** Update the media player with information about the active session. */
+        public void updateControls(NotificationEntry entry, Icon appIcon,
+                MediaMetadata mediaMetadata) {
+
+            // Foreground and Background colors computed from album art
+            Notification notif = entry.getSbn().getNotification();
+            int fgColor = notif.color;
+            int bgColor = entry.getRow() == null ? -1 : entry.getRow().getCurrentBackgroundTint();
+            Bitmap artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
+            if (artworkBitmap == null) {
+                artworkBitmap = mediaMetadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
+            }
+            if (artworkBitmap != null) {
+                // If we have art, get colors from that
+                Palette p = MediaNotificationProcessor.generateArtworkPaletteBuilder(artworkBitmap)
+                        .generate();
+                Palette.Swatch swatch = MediaNotificationProcessor.findBackgroundSwatch(p);
+                bgColor = swatch.getRgb();
+                fgColor = MediaNotificationProcessor.selectForegroundColor(bgColor, p);
+            }
+            // Make sure colors will be legible
+            boolean isDark = !ContrastColorUtil.isColorLight(bgColor);
+            fgColor = ContrastColorUtil.resolveContrastColor(mContext, fgColor, bgColor,
+                    isDark);
+            fgColor = ContrastColorUtil.ensureTextContrast(fgColor, bgColor, isDark);
+
+            // Album art
+            RoundedBitmapDrawable artwork = null;
+            if (artworkBitmap != null) {
+                Bitmap original = artworkBitmap.copy(Bitmap.Config.ARGB_8888, true);
+                Bitmap scaled = Bitmap.createScaledBitmap(original, mAlbumArtSize, mAlbumArtSize,
+                        false);
+                artwork = RoundedBitmapDrawableFactory.create(mContext.getResources(), scaled);
+                artwork.setCornerRadius(mAlbumArtRadius);
+            }
+
+            // App name
             Notification.Builder builder = Notification.Builder.recoverBuilder(mContext, notif);
-            String appNameString = builder.loadHeaderAppName();
-            appName.setText(appNameString);
-            appName.setTextColor(fgColor);
-        }
+            String app = builder.loadHeaderAppName();
 
-        // Song name
-        TextView titleText = mMediaNotifView.findViewById(R.id.header_title);
-        if (titleText != null) {
-            String songName = mediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
-            titleText.setText(songName);
-            titleText.setTextColor(fgColor);
-        }
+            // App Icon
+            Drawable appIconDrawable = appIcon.loadDrawable(mContext);
 
-        // Artist name
-        TextView artistText = mMediaNotifView.findViewById(R.id.header_artist);
-        if (artistText != null) {
-            String artistName = mediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
-            artistText.setText(artistName);
-            artistText.setTextColor(fgColor);
-        }
+            // Song name
+            String song = mediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE);
 
-        // Background color
-        if (mMediaNotifView instanceof MediaHeaderView) {
-            MediaHeaderView head = (MediaHeaderView) mMediaNotifView;
-            head.setBackgroundColor(bgColor);
-        }
+            // Artist name
+            String artist = mediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
 
-        // Control buttons
-        final List<Icon> icons = new ArrayList<>();
-        final List<PendingIntent> intents = new ArrayList<>();
-        Notification.Action[] actions = notif.actions;
-        final int[] actionsToShow = notif.extras.getIntArray(Notification.EXTRA_COMPACT_ACTIONS);
+            // Control buttons
+            List<Drawable> actionIcons = new ArrayList<>();
+            final List<PendingIntent> intents = new ArrayList<>();
+            Notification.Action[] actions = notif.actions;
+            final int[] actionsToShow = notif.extras.getIntArray(
+                    Notification.EXTRA_COMPACT_ACTIONS);
 
-        for (int i = 0; i < ACTION_IDS.length; i++) {
-            if (actionsToShow != null && actions != null && i < actionsToShow.length
-                    && actionsToShow[i] < actions.length) {
-                final int idx = actionsToShow[i];
-                icons.add(actions[idx].getIcon());
-                intents.add(actions[idx].actionIntent);
-            } else {
-                icons.add(null);
-                intents.add(null);
+            Context packageContext = entry.getSbn().getPackageContext(mContext);
+            for (int i = 0; i < ACTION_IDS.length; i++) {
+                if (actionsToShow != null && actions != null && i < actionsToShow.length
+                        && actionsToShow[i] < actions.length) {
+                    final int idx = actionsToShow[i];
+                    actionIcons.add(actions[idx].getIcon().loadDrawable(packageContext));
+                    intents.add(actions[idx].actionIntent);
+                } else {
+                    actionIcons.add(null);
+                    intents.add(null);
+                }
             }
+            synchronized (mActionsLock) {
+                mActions = intents;
+            }
+
+            KeyguardMedia data = new KeyguardMedia(fgColor, bgColor, app, appIconDrawable, artist,
+                    song, artwork, actionIcons);
+            mMedia.postValue(data);
         }
 
-        Context packageContext = entry.getSbn().getPackageContext(mContext);
-        for (int i = 0; i < ACTION_IDS.length; i++) {
-            ImageButton button = mMediaNotifView.findViewById(ACTION_IDS[i]);
-            if (button == null) {
-                continue;
+        /** Gets state for the lock screen media controls. */
+        public LiveData<KeyguardMedia> getKeyguardMedia() {
+            return mMedia;
+        }
+
+        /**
+         * Handle user clicks on media control buttons (actions).
+         *
+         * @param index position of the button that was clicked.
+         */
+        public void onActionClick(int index) {
+            PendingIntent intent = null;
+            // This might block the ui thread to wait for the lock. Currently, however, the
+            // lock is held by the bg thread to assign a member, which should be fast. An
+            // alternative could be to add the intents to the state and let the observer set
+            // the onClick listeners.
+            synchronized (mActionsLock) {
+                if (mActions != null && index < mActions.size()) {
+                    intent = mActions.get(index);
+                }
             }
-            Icon icon = icons.get(i);
-            if (icon == null) {
-                button.setVisibility(View.GONE);
-            } else {
-                button.setVisibility(View.VISIBLE);
-                button.setImageDrawable(icon.loadDrawable(packageContext));
-                button.setImageTintList(ColorStateList.valueOf(fgColor));
-                final PendingIntent intent = intents.get(i);
-                if (intent != null) {
-                    button.setOnClickListener(v -> {
-                        try {
-                            intent.send();
-                        } catch (PendingIntent.CanceledException e) {
-                            Log.d(TAG, "failed to send action intent", e);
-                        }
-                    });
+            if (intent != null) {
+                try {
+                    intent.send();
+                } catch (PendingIntent.CanceledException e) {
+                    Log.d(TAG, "failed to send action intent", e);
                 }
             }
         }
+
+        void loadDimens() {
+            mAlbumArtRadius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
+            mAlbumArtSize = (int) mContext.getResources().getDimension(
+                    R.dimen.qs_media_album_size);
+        }
     }
 
-    /**
-     * Process album art for layout
-     * @param albumArt bitmap to use for album art
-     * @param albumView view to hold the album art
-     */
-    private void processAlbumArt(Bitmap albumArt, ImageView albumView) {
-        RoundedBitmapDrawable roundedDrawable = null;
-        if (albumArt != null) {
-            Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true);
-            Bitmap scaled = Bitmap.createScaledBitmap(original, mAlbumArtSize, mAlbumArtSize,
-                    false);
-            roundedDrawable = RoundedBitmapDrawableFactory.create(mContext.getResources(), scaled);
-            roundedDrawable.setCornerRadius(mAlbumArtRadius);
-        } else {
-            Log.e(TAG, "No album art available");
+    /** Observer for state changes of lock screen media controls. */
+    private static final class KeyguardMediaObserver implements Observer<KeyguardMedia> {
+
+        private final View mRootView;
+        private final MediaHeaderView mMediaHeaderView;
+        private final ImageView mAlbumView;
+        private final ImageView mAppIconView;
+        private final TextView mAppNameView;
+        private final TextView mTitleView;
+        private final TextView mArtistView;
+        private final List<ImageButton> mButtonViews = new ArrayList<>();
+
+        KeyguardMediaObserver(View v) {
+            mRootView = v;
+            mMediaHeaderView = v instanceof MediaHeaderView ? (MediaHeaderView) v : null;
+            mAlbumView = v.findViewById(R.id.album_art);
+            mAppIconView = v.findViewById(R.id.icon);
+            mAppNameView = v.findViewById(R.id.app_name);
+            mTitleView = v.findViewById(R.id.header_title);
+            mArtistView = v.findViewById(R.id.header_artist);
+            for (int i = 0; i < ACTION_IDS.length; i++) {
+                mButtonViews.add(v.findViewById(ACTION_IDS[i]));
+            }
         }
 
-        // Now that it's resized, update the UI
-        final RoundedBitmapDrawable result = roundedDrawable;
-        albumView.post(() -> {
-            albumView.setImageDrawable(result);
-            albumView.setVisibility(result == null ? View.GONE : View.VISIBLE);
-        });
-    }
+        /** Updates lock screen media player views when state changes. */
+        @Override
+        public void onChanged(KeyguardMedia data) {
+            if (data == null) {
+                mRootView.setVisibility(View.GONE);
+                return;
+            }
+            mRootView.setVisibility(View.VISIBLE);
 
-    private void loadDimens() {
-        mAlbumArtRadius = mContext.getResources().getDimension(R.dimen.qs_media_corner_radius);
-        mAlbumArtSize = (int) mContext.getResources().getDimension(
-                    R.dimen.qs_media_album_size);
+            // Background color
+            if (mMediaHeaderView != null) {
+                mMediaHeaderView.setBackgroundColor(data.getBackgroundColor());
+            }
+
+            // Album art
+            if (mAlbumView != null) {
+                mAlbumView.setImageDrawable(data.getArtwork());
+                mAlbumView.setVisibility(data.getArtwork() == null ? View.GONE : View.VISIBLE);
+            }
+
+            // App icon
+            if (mAppIconView != null) {
+                Drawable iconDrawable = data.getAppIcon();
+                iconDrawable.setTint(data.getForegroundColor());
+                mAppIconView.setImageDrawable(iconDrawable);
+            }
+
+            // App name
+            if (mAppNameView != null) {
+                String appNameString = data.getApp();
+                mAppNameView.setText(appNameString);
+                mAppNameView.setTextColor(data.getForegroundColor());
+            }
+
+            // Song name
+            if (mTitleView != null) {
+                mTitleView.setText(data.getSong());
+                mTitleView.setTextColor(data.getForegroundColor());
+            }
+
+            // Artist name
+            if (mArtistView != null) {
+                mArtistView.setText(data.getArtist());
+                mArtistView.setTextColor(data.getForegroundColor());
+            }
+
+            // Control buttons
+            for (int i = 0; i < ACTION_IDS.length; i++) {
+                ImageButton button = mButtonViews.get(i);
+                if (button == null) {
+                    continue;
+                }
+                Drawable icon = data.getActionIcons().get(i);
+                if (icon == null) {
+                    button.setVisibility(View.GONE);
+                    button.setImageDrawable(null);
+                } else {
+                    button.setVisibility(View.VISIBLE);
+                    button.setImageDrawable(icon);
+                    button.setImageTintList(ColorStateList.valueOf(data.getForegroundColor()));
+                }
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 3afe19f..7cbc840 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -127,6 +127,7 @@
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
     private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
     private static final boolean DEBUG_FACE = true;
+    private static final boolean DEBUG_SPEW = false;
     private static final int LOW_BATTERY_THRESHOLD = 20;
 
     private static final String ACTION_FACE_UNLOCK_STARTED
@@ -324,7 +325,8 @@
                 }
             };
 
-    private class BiometricAuthenticated {
+    @VisibleForTesting
+    static class BiometricAuthenticated {
         private final boolean mAuthenticated;
         private final boolean mIsStrongBiometric;
 
@@ -338,11 +340,14 @@
     private SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
     private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray();
     private SparseBooleanArray mUserTrustIsUsuallyManaged = new SparseBooleanArray();
-    private SparseArray<BiometricAuthenticated> mUserFingerprintAuthenticated = new SparseArray<>();
-    private SparseArray<BiometricAuthenticated> mUserFaceAuthenticated = new SparseArray<>();
     private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray();
     private Map<Integer, Intent> mSecondaryLockscreenRequirement = new HashMap<Integer, Intent>();
 
+    @VisibleForTesting
+    SparseArray<BiometricAuthenticated> mUserFingerprintAuthenticated = new SparseArray<>();
+    @VisibleForTesting
+    SparseArray<BiometricAuthenticated> mUserFaceAuthenticated = new SparseArray<>();
+
     private static int sCurrentUser;
     private Runnable mUpdateBiometricListeningState = this::updateBiometricListeningState;
 
@@ -1850,11 +1855,33 @@
 
         // Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
         // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
-        return (mBouncer || mAuthInterruptActive || awakeKeyguard || shouldListenForFaceAssistant())
+        final boolean shouldListen =
+                (mBouncer || mAuthInterruptActive || awakeKeyguard
+                        || shouldListenForFaceAssistant())
                 && !mSwitchingUser && !isFaceDisabled(user) && becauseCannotSkipBouncer
                 && !mKeyguardGoingAway && mFaceSettingEnabledForUser.get(user) && !mLockIconPressed
                 && strongAuthAllowsScanning && mIsPrimaryUser
                 && !mSecureCameraLaunched;
+
+        // Too chatty, but very useful when debugging issues.
+        if (DEBUG_SPEW) {
+            Log.v(TAG, "shouldListenForFace(" + user + ")=" + shouldListen + "... "
+                    + ", mBouncer: " + mBouncer
+                    + ", mAuthInterruptActive: " + mAuthInterruptActive
+                    + ", awakeKeyguard: " + awakeKeyguard
+                    + ", shouldListenForFaceAssistant: " + shouldListenForFaceAssistant()
+                    + ", mSwitchingUser: " + mSwitchingUser
+                    + ", isFaceDisabled(" + user + "): " + isFaceDisabled(user)
+                    + ", becauseCannotSkipBouncer: " + becauseCannotSkipBouncer
+                    + ", mKeyguardGoingAway: " + mKeyguardGoingAway
+                    + ", mFaceSettingEnabledForUser(" + user + "): "
+                            + mFaceSettingEnabledForUser.get(user)
+                    + ", mLockIconPressed: " + mLockIconPressed
+                    + ", strongAuthAllowsScanning: " + strongAuthAllowsScanning
+                    + ", isPrimaryUser: " + mIsPrimaryUser
+                    + ", mSecureCameraLaunched: " + mSecureCameraLaunched);
+        }
+        return shouldListen;
     }
 
     /**
@@ -2049,8 +2076,10 @@
     /**
      * Handle {@link #MSG_USER_SWITCHING}
      */
-    private void handleUserSwitching(int userId, IRemoteCallback reply) {
+    @VisibleForTesting
+    void handleUserSwitching(int userId, IRemoteCallback reply) {
         Assert.isMainThread();
+        clearBiometricRecognized();
         mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId));
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 13f3c0f..b006bc1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -347,21 +347,35 @@
             showError(message);
         }
 
-        // Only show popup dialog before wipe.
+        // Only show dialog if <=1 attempts are left before wiping.
         final int remainingAttempts = maxAttempts - numAttempts;
-        if (remainingAttempts <= 0) {
-            showNowWipingMessage();
-            mContainerView.animateAway(AuthDialogCallback.DISMISSED_ERROR);
+        if (remainingAttempts == 1) {
+            showLastAttemptBeforeWipeDialog();
+        } else if (remainingAttempts <= 0) {
+            showNowWipingDialog();
         }
         return true;
     }
 
-    private void showNowWipingMessage() {
+    private void showLastAttemptBeforeWipeDialog() {
+        final AlertDialog alertDialog = new AlertDialog.Builder(mContext)
+                .setTitle(R.string.biometric_dialog_last_attempt_before_wipe_dialog_title)
+                .setMessage(
+                        getLastAttemptBeforeWipeMessageRes(getUserTypeForWipe(), mCredentialType))
+                .setPositiveButton(android.R.string.ok, null)
+                .create();
+        alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
+        alertDialog.show();
+    }
+
+    private void showNowWipingDialog() {
         final AlertDialog alertDialog = new AlertDialog.Builder(mContext)
                 .setMessage(getNowWipingMessageRes(getUserTypeForWipe()))
                 .setPositiveButton(R.string.biometric_dialog_now_wiping_dialog_dismiss, null)
+                .setOnDismissListener(
+                        dialog -> mContainerView.animateAway(AuthDialogCallback.DISMISSED_ERROR))
                 .create();
-        alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+        alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
         alertDialog.show();
     }
 
@@ -377,6 +391,59 @@
         }
     }
 
+    private static @StringRes int getLastAttemptBeforeWipeMessageRes(
+            @UserType int userType, @Utils.CredentialType int credentialType) {
+        switch (userType) {
+            case USER_TYPE_PRIMARY:
+                return getLastAttemptBeforeWipeDeviceMessageRes(credentialType);
+            case USER_TYPE_MANAGED_PROFILE:
+                return getLastAttemptBeforeWipeProfileMessageRes(credentialType);
+            case USER_TYPE_SECONDARY:
+                return getLastAttemptBeforeWipeUserMessageRes(credentialType);
+            default:
+                throw new IllegalArgumentException("Unrecognized user type:" + userType);
+        }
+    }
+
+    private static @StringRes int getLastAttemptBeforeWipeDeviceMessageRes(
+            @Utils.CredentialType int credentialType) {
+        switch (credentialType) {
+            case Utils.CREDENTIAL_PIN:
+                return R.string.biometric_dialog_last_pin_attempt_before_wipe_device;
+            case Utils.CREDENTIAL_PATTERN:
+                return R.string.biometric_dialog_last_pattern_attempt_before_wipe_device;
+            case Utils.CREDENTIAL_PASSWORD:
+            default:
+                return R.string.biometric_dialog_last_password_attempt_before_wipe_device;
+        }
+    }
+
+    private static @StringRes int getLastAttemptBeforeWipeProfileMessageRes(
+            @Utils.CredentialType int credentialType) {
+        switch (credentialType) {
+            case Utils.CREDENTIAL_PIN:
+                return R.string.biometric_dialog_last_pin_attempt_before_wipe_profile;
+            case Utils.CREDENTIAL_PATTERN:
+                return R.string.biometric_dialog_last_pattern_attempt_before_wipe_profile;
+            case Utils.CREDENTIAL_PASSWORD:
+            default:
+                return R.string.biometric_dialog_last_password_attempt_before_wipe_profile;
+        }
+    }
+
+    private static @StringRes int getLastAttemptBeforeWipeUserMessageRes(
+            @Utils.CredentialType int credentialType) {
+        switch (credentialType) {
+            case Utils.CREDENTIAL_PIN:
+                return R.string.biometric_dialog_last_pin_attempt_before_wipe_user;
+            case Utils.CREDENTIAL_PATTERN:
+                return R.string.biometric_dialog_last_pattern_attempt_before_wipe_user;
+            case Utils.CREDENTIAL_PASSWORD:
+            default:
+                return R.string.biometric_dialog_last_password_attempt_before_wipe_user;
+        }
+    }
+
     private static @StringRes int getNowWipingMessageRes(@UserType int userType) {
         switch (userType) {
             case USER_TYPE_PRIMARY:
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
index a1cb7f6..55be77c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
@@ -28,6 +28,8 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 
+import java.util.EnumSet;
+
 /**
  * View that displays an adaptive icon with an app-badge and a dot.
  *
@@ -42,12 +44,27 @@
     /** Same as value in Launcher3 IconShape */
     public static final int DEFAULT_PATH_SIZE = 100;
 
-    static final int DOT_STATE_DEFAULT = 0;
-    static final int DOT_STATE_SUPPRESSED_FOR_FLYOUT = 1;
-    static final int DOT_STATE_ANIMATING = 2;
+    /**
+     * Flags that suppress the visibility of the 'new' dot, for one reason or another. If any of
+     * these flags are set, the dot will not be shown even if {@link Bubble#showDot()} returns true.
+     */
+    enum SuppressionFlag {
+        // Suppressed because the flyout is visible - it will morph into the dot via animation.
+        FLYOUT_VISIBLE,
+        // Suppressed because this bubble is behind others in the collapsed stack.
+        BEHIND_STACK,
+    }
 
-    // Flyout gets shown before the dot
-    private int mCurrentDotState = DOT_STATE_SUPPRESSED_FOR_FLYOUT;
+    /**
+     * Start by suppressing the dot because the flyout is visible - most bubbles are added with a
+     * flyout, so this is a reasonable default.
+     */
+    private final EnumSet<SuppressionFlag> mDotSuppressionFlags =
+            EnumSet.of(SuppressionFlag.FLYOUT_VISIBLE);
+
+    private float mDotScale = 0f;
+    private float mAnimatingToDotScale = 0f;
+    private boolean mDotIsAnimating = false;
 
     private BubbleViewProvider mBubble;
 
@@ -57,8 +74,6 @@
     private boolean mOnLeft;
 
     private int mDotColor;
-    private float mDotScale = 0f;
-    private boolean mDotDrawn;
 
     private Rect mTempBounds = new Rect();
 
@@ -83,28 +98,28 @@
         Path iconPath = PathParser.createPathFromPathData(
                 getResources().getString(com.android.internal.R.string.config_icon_mask));
         mDotRenderer = new DotRenderer(mBubbleBitmapSize, iconPath, DEFAULT_PATH_SIZE);
+
+        setFocusable(true);
     }
 
     /**
      * Updates the view with provided info.
      */
-    public void update(BubbleViewProvider bubble) {
+    public void setRenderedBubble(BubbleViewProvider bubble) {
         mBubble = bubble;
         setImageBitmap(bubble.getBadgedImage());
-        setDotState(DOT_STATE_SUPPRESSED_FOR_FLYOUT);
         mDotColor = bubble.getDotColor();
         drawDot(bubble.getDotPath());
-        animateDot();
     }
 
     @Override
     public void onDraw(Canvas canvas) {
         super.onDraw(canvas);
-        if (isDotHidden()) {
-            mDotDrawn = false;
+
+        if (!shouldDrawDot()) {
             return;
         }
-        mDotDrawn = mDotScale > 0.1f;
+
         getDrawingRect(mTempBounds);
 
         mDrawParams.color = mDotColor;
@@ -115,23 +130,33 @@
         mDotRenderer.draw(canvas, mDrawParams);
     }
 
-    /**
-     * Sets the dot state, does not animate changes.
-     */
-    void setDotState(int state) {
-        mCurrentDotState = state;
-        if (state == DOT_STATE_SUPPRESSED_FOR_FLYOUT || state == DOT_STATE_DEFAULT) {
-            mDotScale = mBubble.showDot() ? 1f : 0f;
-            invalidate();
+    /** Adds a dot suppression flag, updating dot visibility if needed. */
+    void addDotSuppressionFlag(SuppressionFlag flag) {
+        if (mDotSuppressionFlags.add(flag)) {
+            // Update dot visibility, and animate out if we're now behind the stack.
+            updateDotVisibility(flag == SuppressionFlag.BEHIND_STACK /* animate */);
         }
     }
 
-    /**
-     * Whether the dot should be hidden based on current dot state.
-     */
-    private boolean isDotHidden() {
-        return (mCurrentDotState == DOT_STATE_DEFAULT && !mBubble.showDot())
-                || mCurrentDotState == DOT_STATE_SUPPRESSED_FOR_FLYOUT;
+    /** Removes a dot suppression flag, updating dot visibility if needed. */
+    void removeDotSuppressionFlag(SuppressionFlag flag) {
+        if (mDotSuppressionFlags.remove(flag)) {
+            // Update dot visibility, animating if we're no longer behind the stack.
+            updateDotVisibility(flag == SuppressionFlag.BEHIND_STACK);
+        }
+    }
+
+    /** Updates the visibility of the dot, animating if requested. */
+    void updateDotVisibility(boolean animate) {
+        final float targetScale = shouldDrawDot() ? 1f : 0f;
+
+        if (animate) {
+            animateDotScale(targetScale, null /* after */);
+        } else {
+            mDotScale = targetScale;
+            mAnimatingToDotScale = targetScale;
+            invalidate();
+        }
     }
 
     /**
@@ -194,11 +219,11 @@
     }
 
     /** Sets the position of the 'new' dot, animating it out and back in if requested. */
-    void setDotPosition(boolean onLeft, boolean animate) {
-        if (animate && onLeft != getDotOnLeft() && !isDotHidden()) {
-            animateDot(false /* showDot */, () -> {
+    void setDotPositionOnLeft(boolean onLeft, boolean animate) {
+        if (animate && onLeft != getDotOnLeft() && shouldDrawDot()) {
+            animateDotScale(0f /* showDot */, () -> {
                 setDotOnLeft(onLeft);
-                animateDot(true /* showDot */, null);
+                animateDotScale(1.0f, null /* after */);
             });
         } else {
             setDotOnLeft(onLeft);
@@ -209,28 +234,34 @@
         return getDotOnLeft();
     }
 
-    /** Changes the dot's visibility to match the bubble view's state. */
-    void animateDot() {
-        if (mCurrentDotState == DOT_STATE_DEFAULT) {
-            animateDot(mBubble.showDot(), null);
-        }
+    /** Whether to draw the dot in onDraw(). */
+    private boolean shouldDrawDot() {
+        // Always render the dot if it's animating, since it could be animating out. Otherwise, show
+        // it if the bubble wants to show it, and we aren't suppressing it.
+        return mDotIsAnimating || (mBubble.showDot() && mDotSuppressionFlags.isEmpty());
     }
 
     /**
-     * Animates the dot to show or hide.
+     * Animates the dot to the given scale, running the optional callback when the animation ends.
      */
-    private void animateDot(boolean showDot, Runnable after) {
-        if (mDotDrawn == showDot) {
-            // State is consistent, do nothing.
+    private void animateDotScale(float toScale, @Nullable Runnable after) {
+        mDotIsAnimating = true;
+
+        // Don't restart the animation if we're already animating to the given value.
+        if (mAnimatingToDotScale == toScale || !shouldDrawDot()) {
+            mDotIsAnimating = false;
             return;
         }
 
-        setDotState(DOT_STATE_ANIMATING);
+        mAnimatingToDotScale = toScale;
+
+        final boolean showDot = toScale > 0f;
 
         // Do NOT wait until after animation ends to setShowDot
         // to avoid overriding more recent showDot states.
         clearAnimation();
-        animate().setDuration(200)
+        animate()
+                .setDuration(200)
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .setUpdateListener((valueAnimator) -> {
                     float fraction = valueAnimator.getAnimatedFraction();
@@ -238,7 +269,7 @@
                     setDotScale(fraction);
                 }).withEndAction(() -> {
                     setDotScale(showDot ? 1f : 0f);
-                    setDotState(DOT_STATE_DEFAULT);
+                    mDotIsAnimating = false;
                     if (after != null) {
                         after.run();
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 726a7dd..71f2bc0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -76,7 +76,6 @@
     private BadgedImageView mIconView;
     private BubbleExpandedView mExpandedView;
 
-    private boolean mInflated;
     private BubbleViewInfoTask mInflationTask;
     private boolean mInflateSynchronously;
 
@@ -166,10 +165,16 @@
         return mExpandedView;
     }
 
-    void cleanupExpandedState() {
+    /**
+     * Call when the views should be removed, ensure this is called to clean up ActivityView
+     * content.
+     */
+    void cleanupViews() {
         if (mExpandedView != null) {
             mExpandedView.cleanUpExpandedState();
+            mExpandedView = null;
         }
+        mIconView = null;
     }
 
     /**
@@ -213,17 +218,15 @@
     }
 
     boolean isInflated() {
-        return mInflated;
+        return mIconView != null && mExpandedView != null;
     }
 
     void stopInflation() {
         if (mInflationTask == null) {
             return;
         }
-        mInflationTask.cancel(/* mayInterruptIfRunning */ true);
-        mIconView = null;
-        mExpandedView = null;
-        mInflated = false;
+        mInflationTask.cancel(true /* mayInterruptIfRunning */);
+        cleanupViews();
     }
 
     void setViewInfo(BubbleViewInfoTask.BubbleViewInfo info) {
@@ -240,21 +243,14 @@
         mDotColor = info.dotColor;
         mDotPath = info.dotPath;
 
-        if (mExpandedView != null && mIconView != null) {
-            mInflated = true;
-        }
         if (mExpandedView != null) {
             mExpandedView.update(/* bubble */ this);
         }
         if (mIconView != null) {
-            mIconView.update(/* bubble */ this);
+            mIconView.setRenderedBubble(/* bubble */ this);
         }
     }
 
-    void setInflated(boolean inflated) {
-        mInflated = inflated;
-    }
-
     /**
      * Set visibility of bubble in the expanded state.
      *
@@ -306,7 +302,7 @@
     void markAsAccessedAt(long lastAccessedMillis) {
         mLastAccessed = lastAccessedMillis;
         setSuppressNotification(true);
-        setShowDot(false /* show */, true /* animate */);
+        setShowDot(false /* show */);
     }
 
     /**
@@ -346,12 +342,11 @@
     /**
      * Sets whether the bubble for this notification should show a dot indicating updated content.
      */
-    void setShowDot(boolean showDot, boolean animate) {
+    void setShowDot(boolean showDot) {
         mShowBubbleUpdateDot = showDot;
-        if (animate && mIconView != null) {
-            mIconView.animateDot();
-        } else if (mIconView != null) {
-            mIconView.invalidate();
+
+        if (mIconView != null) {
+            mIconView.updateDotVisibility(true /* animate */);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index c9ce8a1..9d885fd 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -74,6 +74,7 @@
 import com.android.systemui.R;
 import com.android.systemui.bubbles.dagger.BubbleModule;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.PinnedStackListenerForwarder;
@@ -174,6 +175,7 @@
 
     private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
     private IStatusBarService mBarService;
+    private SysUiState mSysUiState;
 
     // Used for determining view rect for touch interaction
     private Rect mTempRect = new Rect();
@@ -290,11 +292,12 @@
             NotifPipeline notifPipeline,
             FeatureFlags featureFlags,
             DumpManager dumpManager,
-            FloatingContentCoordinator floatingContentCoordinator) {
+            FloatingContentCoordinator floatingContentCoordinator,
+            SysUiState sysUiState) {
         this(context, notificationShadeWindowController, statusBarStateController, shadeController,
                 data, null /* synchronizer */, configurationController, interruptionStateProvider,
                 zenModeController, notifUserManager, groupManager, entryManager,
-                notifPipeline, featureFlags, dumpManager, floatingContentCoordinator);
+                notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, sysUiState);
     }
 
     /**
@@ -315,7 +318,8 @@
             NotifPipeline notifPipeline,
             FeatureFlags featureFlags,
             DumpManager dumpManager,
-            FloatingContentCoordinator floatingContentCoordinator) {
+            FloatingContentCoordinator floatingContentCoordinator,
+            SysUiState sysUiState) {
         dumpManager.registerDumpable(TAG, this);
         mContext = context;
         mShadeController = shadeController;
@@ -327,19 +331,20 @@
             @Override
             public void onZenChanged(int zen) {
                 for (Bubble b : mBubbleData.getBubbles()) {
-                    b.setShowDot(b.showInShade(), true /* animate */);
+                    b.setShowDot(b.showInShade());
                 }
             }
 
             @Override
             public void onConfigChanged(ZenModeConfig config) {
                 for (Bubble b : mBubbleData.getBubbles()) {
-                    b.setShowDot(b.showInShade(), true /* animate */);
+                    b.setShowDot(b.showInShade());
                 }
             }
         });
 
         configurationController.addCallback(this /* configurationListener */);
+        mSysUiState = sysUiState;
 
         mBubbleData = data;
         mBubbleData.setListener(mBubbleDataListener);
@@ -593,7 +598,8 @@
     private void ensureStackViewCreated() {
         if (mStackView == null) {
             mStackView = new BubbleStackView(
-                    mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator);
+                    mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator,
+                    mSysUiState);
             ViewGroup nsv = mNotificationShadeWindowController.getNotificationShadeView();
             int bubbleScrimIndex = nsv.indexOfChild(nsv.findViewById(R.id.scrim_for_bubble));
             int stackIndex = bubbleScrimIndex + 1;  // Show stack above bubble scrim.
@@ -957,9 +963,10 @@
             String key = orderedKeys[i];
             NotificationEntry entry = mNotificationEntryManager.getPendingOrActiveNotif(key);
             rankingMap.getRanking(key, mTmpRanking);
-            if (mBubbleData.hasBubbleWithKey(key) && !mTmpRanking.canBubble()) {
+            boolean isActiveBubble = mBubbleData.hasBubbleWithKey(key);
+            if (isActiveBubble && !mTmpRanking.canBubble()) {
                 mBubbleData.notificationEntryRemoved(entry, BubbleController.DISMISS_BLOCKED);
-            } else if (entry != null && mTmpRanking.isBubble()) {
+            } else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
                 entry.setFlagBubble(true);
                 onEntryUpdated(entry);
             }
@@ -1094,7 +1101,7 @@
         } else if (interceptBubbleDismissal) {
             Bubble bubble = mBubbleData.getBubbleWithKey(entry.getKey());
             bubble.setSuppressNotification(true);
-            bubble.setShowDot(false /* show */, true /* animate */);
+            bubble.setShowDot(false /* show */);
         } else {
             return false;
         }
@@ -1134,7 +1141,7 @@
                     Bubble bubbleChild = mBubbleData.getBubbleWithKey(child.getKey());
                     mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry());
                     bubbleChild.setSuppressNotification(true);
-                    bubbleChild.setShowDot(false /* show */, true /* animate */);
+                    bubbleChild.setShowDot(false /* show */);
                 } else {
                     // non-bubbled children can be removed
                     for (NotifCallback cb : mCallbacks) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 077ffd3..1c69594 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -208,7 +208,7 @@
                 b -> {
                     notificationEntryUpdated(bubble, /* suppressFlyout */
                             false, /* showInShade */ true);
-                    setSelectedBubbleInternal(bubble);
+                    setSelectedBubble(bubble);
                 },
                 mContext, stack, factory);
         dispatchPendingChanges();
@@ -233,6 +233,7 @@
                 Bubble b = mOverflowBubbles.get(i);
                 if (b.getKey().equals(entry.getKey())) {
                     moveOverflowBubbleToPending(b);
+                    b.setEntry(entry);
                     return b;
                 }
             }
@@ -240,6 +241,7 @@
             for (int i = 0; i < mPendingBubbles.size(); i++) {
                 Bubble b = mPendingBubbles.get(i);
                 if (b.getKey().equals(entry.getKey())) {
+                    b.setEntry(entry);
                     return b;
                 }
             }
@@ -286,7 +288,7 @@
         boolean isBubbleExpandedAndSelected = mExpanded && mSelectedBubble == bubble;
         boolean suppress = isBubbleExpandedAndSelected || !showInShade || !bubble.showInShade();
         bubble.setSuppressNotification(suppress);
-        bubble.setShowDot(!isBubbleExpandedAndSelected /* show */, true /* animate */);
+        bubble.setShowDot(!isBubbleExpandedAndSelected /* show */);
 
         dispatchPendingChanges();
     }
@@ -759,6 +761,17 @@
     }
 
     @VisibleForTesting(visibility = PRIVATE)
+    Bubble getOverflowBubbleWithKey(String key) {
+        for (int i = 0; i < mOverflowBubbles.size(); i++) {
+            Bubble bubble = mOverflowBubbles.get(i);
+            if (bubble.getKey().equals(key)) {
+                return bubble;
+            }
+        }
+        return null;
+    }
+
+    @VisibleForTesting(visibility = PRIVATE)
     void setTimeSource(TimeSource timeSource) {
         mTimeSource = timeSource;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
index 4fb2d08..13669a6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
@@ -112,7 +112,7 @@
         mPath.transform(matrix);
 
         mOverflowBtn.setVisibility(GONE);
-        mOverflowBtn.update(this);
+        mOverflowBtn.setRenderedBubble(this);
     }
 
     ImageView getBtn() {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index 7636c67..2231d11 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -21,14 +21,20 @@
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.app.Activity;
+import android.app.Notification;
+import android.app.Person;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
@@ -67,13 +73,22 @@
 
         mEmptyState = findViewById(R.id.bubble_overflow_empty_state);
         mRecyclerView = findViewById(R.id.bubble_overflow_recycler);
-        mRecyclerView.setLayoutManager(
-                new GridLayoutManager(getApplicationContext(),
-                        getResources().getInteger(R.integer.bubbles_overflow_columns)));
 
-        int bubbleMargin = getResources().getDimensionPixelSize(R.dimen.bubble_overflow_margin);
+        Resources res = getResources();
+        final int columns = res.getInteger(R.integer.bubbles_overflow_columns);
+        mRecyclerView.setLayoutManager(
+                new GridLayoutManager(getApplicationContext(), columns));
+
+        DisplayMetrics displayMetrics = new DisplayMetrics();
+        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+        final int viewWidth = displayMetrics.widthPixels / columns;
+
+        final int maxOverflowBubbles = res.getInteger(R.integer.bubbles_max_overflow);
+        final int rows = (int) Math.ceil((double) maxOverflowBubbles / columns);
+        final int viewHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height) / rows;
+
         mAdapter = new BubbleOverflowAdapter(mOverflowBubbles,
-                mBubbleController::promoteBubbleFromOverflow, bubbleMargin);
+                mBubbleController::promoteBubbleFromOverflow, viewWidth, viewHeight);
         mRecyclerView.setAdapter(mAdapter);
         onDataChanged(mBubbleController.getOverflowBubbles());
         mBubbleController.setOverflowCallback(() -> {
@@ -139,39 +154,48 @@
 class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.ViewHolder> {
     private Consumer<Bubble> mPromoteBubbleFromOverflow;
     private List<Bubble> mBubbles;
-    private int mBubbleMargin;
+    private int mWidth;
+    private int mHeight;
 
-    public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble,
-            int bubbleMargin) {
+    public BubbleOverflowAdapter(List<Bubble> list, Consumer<Bubble> promoteBubble, int width,
+            int height) {
         mBubbles = list;
         mPromoteBubbleFromOverflow = promoteBubble;
-        mBubbleMargin = bubbleMargin;
+        mWidth = width;
+        mHeight = height;
     }
 
     @Override
     public BubbleOverflowAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
             int viewType) {
-        BadgedImageView view = (BadgedImageView) LayoutInflater.from(parent.getContext())
-                .inflate(R.layout.bubble_view, parent, false);
+        LinearLayout overflowView = (LinearLayout) LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.bubble_overflow_view, parent, false);
         LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                 LinearLayout.LayoutParams.WRAP_CONTENT,
-                LinearLayout.LayoutParams.WRAP_CONTENT
-        );
-        params.setMargins(mBubbleMargin, mBubbleMargin, mBubbleMargin, mBubbleMargin);
-        view.setLayoutParams(params);
-        return new ViewHolder(view);
+                LinearLayout.LayoutParams.WRAP_CONTENT);
+        params.width = mWidth;
+        params.height = mHeight;
+        overflowView.setLayoutParams(params);
+        return new ViewHolder(overflowView);
     }
 
     @Override
     public void onBindViewHolder(ViewHolder vh, int index) {
-        Bubble bubble = mBubbles.get(index);
+        Bubble b = mBubbles.get(index);
 
-        vh.mBadgedImageView.update(bubble);
-        vh.mBadgedImageView.setOnClickListener(view -> {
-            mBubbles.remove(bubble);
+        vh.iconView.setRenderedBubble(b);
+        vh.iconView.setOnClickListener(view -> {
+            mBubbles.remove(b);
             notifyDataSetChanged();
-            mPromoteBubbleFromOverflow.accept(bubble);
+            mPromoteBubbleFromOverflow.accept(b);
         });
+
+        Bubble.FlyoutMessage message = b.getFlyoutMessage();
+        if (message != null && message.senderName != null) {
+            vh.textView.setText(message.senderName);
+        } else {
+            vh.textView.setText(b.getAppName());
+        }
     }
 
     @Override
@@ -180,11 +204,13 @@
     }
 
     public static class ViewHolder extends RecyclerView.ViewHolder {
-        public BadgedImageView mBadgedImageView;
+        public BadgedImageView iconView;
+        public TextView textView;
 
-        public ViewHolder(BadgedImageView v) {
+        public ViewHolder(LinearLayout v) {
             super(v);
-            mBadgedImageView = v;
+            iconView = v.findViewById(R.id.bubble_view);
+            textView = v.findViewById(R.id.bubble_view_name);
         }
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 541c8cf..6fd6b8d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -22,8 +22,6 @@
 import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION;
 import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION;
-import static com.android.systemui.bubbles.BadgedImageView.DOT_STATE_DEFAULT;
-import static com.android.systemui.bubbles.BadgedImageView.DOT_STATE_SUPPRESSED_FOR_FLYOUT;
 import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW;
 import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_USER_EDUCATION;
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
@@ -80,6 +78,8 @@
 import com.android.systemui.bubbles.animation.ExpandedAnimationController;
 import com.android.systemui.bubbles.animation.PhysicsAnimationLayout;
 import com.android.systemui.bubbles.animation.StackAnimationController;
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.util.DismissCircleView;
 import com.android.systemui.util.FloatingContentCoordinator;
@@ -224,7 +224,7 @@
     private boolean mIsExpanded;
 
     /** Whether the stack is currently on the left side of the screen, or animating there. */
-    private boolean mStackOnLeftOrWillBe = false;
+    private boolean mStackOnLeftOrWillBe = true;
 
     /** Whether a touch gesture, such as a stack/bubble drag or flyout drag, is in progress. */
     private boolean mIsGestureInProgress = false;
@@ -241,6 +241,7 @@
 
     private BubbleTouchHandler mTouchHandler;
     private BubbleController.BubbleExpandListener mExpandListener;
+    private SysUiState mSysUiState;
 
     private boolean mViewUpdatedRequested = false;
     private boolean mIsExpansionAnimating = false;
@@ -437,7 +438,8 @@
 
     public BubbleStackView(Context context, BubbleData data,
             @Nullable SurfaceSynchronizer synchronizer,
-            FloatingContentCoordinator floatingContentCoordinator) {
+            FloatingContentCoordinator floatingContentCoordinator,
+            SysUiState sysUiState) {
         super(context);
 
         mBubbleData = data;
@@ -445,6 +447,8 @@
         mTouchHandler = new BubbleTouchHandler(this, data, context);
         setOnTouchListener(mTouchHandler);
 
+        mSysUiState = sysUiState;
+
         Resources res = getResources();
         mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
         mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
@@ -930,9 +934,13 @@
             mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
         }
 
+        if (bubble.getIconView() == null) {
+            return;
+        }
+
         // Set the dot position to the opposite of the side the stack is resting on, since the stack
         // resting slightly off-screen would result in the dot also being off-screen.
-        bubble.getIconView().setDotPosition(
+        bubble.getIconView().setDotPositionOnLeft(
                 !mStackOnLeftOrWillBe /* onLeft */, false /* animate */);
 
         mBubbleContainer.addView(bubble.getIconView(), 0,
@@ -955,8 +963,7 @@
             if (v instanceof BadgedImageView
                     && ((BadgedImageView) v).getKey().equals(bubble.getKey())) {
                 mBubbleContainer.removeViewAt(i);
-                bubble.cleanupExpandedState();
-                bubble.setInflated(false);
+                bubble.cleanupViews();
                 logBubbleEvent(bubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__DISMISSED);
                 return;
             }
@@ -1055,6 +1062,11 @@
         if (shouldExpand == mIsExpanded) {
             return;
         }
+
+        mSysUiState
+                .setFlag(QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED, shouldExpand)
+                .commitUpdate(mContext.getDisplayId());
+
         if (mIsExpanded) {
             animateCollapse();
             logBubbleEvent(mExpandedBubble, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
@@ -1687,7 +1699,7 @@
                 || mBubbleToExpandAfterFlyoutCollapse != null
                 || bubbleView == null) {
             if (bubbleView != null) {
-                bubbleView.setDotState(DOT_STATE_DEFAULT);
+                bubbleView.removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE);
             }
             // Skip the message if none exists, we're expanded or animating expansion, or we're
             // about to expand a bubble from the previous tapped flyout, or if bubble view is null.
@@ -1706,12 +1718,16 @@
                 mBubbleData.setExpanded(true);
                 mBubbleToExpandAfterFlyoutCollapse = null;
             }
-            bubbleView.setDotState(DOT_STATE_DEFAULT);
+
+            // Stop suppressing the dot now that the flyout has morphed into the dot.
+            bubbleView.removeDotSuppressionFlag(
+                    BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE);
         };
         mFlyout.setVisibility(INVISIBLE);
 
-        // Don't show the dot when we're animating the flyout
-        bubbleView.setDotState(DOT_STATE_SUPPRESSED_FOR_FLYOUT);
+        // Suppress the dot when we are animating the flyout.
+        bubbleView.addDotSuppressionFlag(
+                BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE);
 
         // Start flyout expansion. Post in case layout isn't complete and getWidth returns 0.
         post(() -> {
@@ -1732,6 +1748,11 @@
                 };
                 mFlyout.postDelayed(mAnimateInFlyout, 200);
             };
+
+            if (bubble.getIconView() == null) {
+                return;
+            }
+
             mFlyout.setupFlyoutStartingAsDot(flyoutMessage,
                     mStackAnimationController.getStackPosition(), getWidth(),
                     mStackAnimationController.isStackOnLeftSide(),
@@ -1866,9 +1887,19 @@
         for (int i = 0; i < bubbleCount; i++) {
             BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i);
             bv.setZ((mMaxBubbles * mBubbleElevation) - i);
+
             // If the dot is on the left, and so is the stack, we need to change the dot position.
             if (bv.getDotPositionOnLeft() == mStackOnLeftOrWillBe) {
-                bv.setDotPosition(!mStackOnLeftOrWillBe, animate);
+                bv.setDotPositionOnLeft(!mStackOnLeftOrWillBe, animate);
+            }
+
+            if (!mIsExpanded && i > 0) {
+                // If we're collapsed and this bubble is behind other bubbles, suppress its dot.
+                bv.addDotSuppressionFlag(
+                        BadgedImageView.SuppressionFlag.BEHIND_STACK);
+            } else {
+                bv.removeDotSuppressionFlag(
+                        BadgedImageView.SuppressionFlag.BEHIND_STACK);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
index 7ee162e..00de8b4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java
@@ -287,7 +287,7 @@
     /** Whether the stack is on the left side of the screen. */
     public boolean isStackOnLeftSide() {
         if (mLayout == null || !isStackPositionSet()) {
-            return false;
+            return true; // Default to left, which is where it starts by default.
         }
 
         float stackCenter = mStackPosition.x + mBubbleBitmapSize / 2;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
index 27c9e98..e84e932 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -21,6 +21,7 @@
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.bubbles.BubbleData;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -62,7 +63,8 @@
             NotifPipeline notifPipeline,
             FeatureFlags featureFlags,
             DumpManager dumpManager,
-            FloatingContentCoordinator floatingContentCoordinator) {
+            FloatingContentCoordinator floatingContentCoordinator,
+            SysUiState sysUiState) {
         return new BubbleController(
                 context,
                 notificationShadeWindowController,
@@ -79,6 +81,7 @@
                 notifPipeline,
                 featureFlags,
                 dumpManager,
-                floatingContentCoordinator);
+                floatingContentCoordinator,
+                sysUiState);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 6c49c82..fdb0e4c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -65,7 +65,7 @@
 
     companion object {
         private const val TAG = "ControlsControllerImpl"
-        internal const val CONTROLS_AVAILABLE = "systemui.controls_available"
+        internal const val CONTROLS_AVAILABLE = Settings.Secure.CONTROLS_ENABLED
         internal val URI = Settings.Secure.getUriFor(CONTROLS_AVAILABLE)
         private const val USER_CHANGE_RETRY_DELAY = 500L // ms
         private const val DEFAULT_ENABLED = 1
@@ -252,10 +252,17 @@
                                     it.controlId in favoritesForComponentKeys
                                 )
                             }
+                            val removedControls = mutableListOf<ControlStatus>()
+                            Favorites.getStructuresForComponent(componentName).forEach { st ->
+                                st.controls.forEach {
+                                    if (it.controlId in removed) {
+                                        val r = createRemovedStatus(componentName, it, st.structure)
+                                        removedControls.add(r)
+                                    }
+                                }
+                            }
                             val loadData = createLoadDataObject(
-                                Favorites.getControlsForComponent(componentName)
-                                    .filter { it.controlId in removed }
-                                    .map { createRemovedStatus(componentName, it) } +
+                                removedControls +
                                 controlsWithFavorite,
                                 favoritesForComponentKeys
                             )
@@ -266,17 +273,15 @@
                     override fun error(message: String) {
                         loadCanceller = null
                         executor.execute {
-                            val loadData = Favorites.getControlsForComponent(componentName)
-                                .let { controls ->
-                                val keys = controls.map { it.controlId }
-                                createLoadDataObject(
-                                        controls.map {
-                                            createRemovedStatus(componentName, it, false)
-                                        },
-                                        keys,
-                                        true
-                                )
-                            }
+                            val controls = Favorites.getStructuresForComponent(componentName)
+                                    .flatMap { st ->
+                                        st.controls.map {
+                                            createRemovedStatus(componentName, it, st.structure,
+                                                    false)
+                                        }
+                                    }
+                            val keys = controls.map { it.control.controlId }
+                            val loadData = createLoadDataObject(controls, keys, true)
                             dataCallback.accept(loadData)
                         }
                     }
@@ -372,6 +377,7 @@
     private fun createRemovedStatus(
         componentName: ComponentName,
         controlInfo: ControlInfo,
+        structure: CharSequence,
         setRemoved: Boolean = true
     ): ControlStatus {
         val intent = Intent(Intent.ACTION_MAIN).apply {
@@ -384,6 +390,8 @@
                 0)
         val control = Control.StatelessBuilder(controlInfo.controlId, pendingIntent)
                 .setTitle(controlInfo.controlTitle)
+                .setSubtitle(controlInfo.controlSubtitle)
+                .setStructure(structure)
                 .setDeviceType(controlInfo.deviceType)
                 .build()
         return ControlStatus(control, componentName, true, setRemoved)
@@ -431,13 +439,14 @@
             Log.d(TAG, "Controls not available")
             return
         }
-        executor.execute {
-            val changed = Favorites.updateControls(
-                componentName,
-                listOf(control)
-            )
-            if (changed) {
-                persistenceWrapper.storeFavorites(Favorites.getAllStructures())
+
+        // Assume that non STATUS_OK responses may contain incomplete or invalid information about
+        // the control, and do not attempt to update it
+        if (control.getStatus() == Control.STATUS_OK) {
+            executor.execute {
+                if (Favorites.updateControls(componentName, listOf(control))) {
+                    persistenceWrapper.storeFavorites(Favorites.getAllStructures())
+                }
             }
         }
         uiController.onRefreshState(componentName, listOf(control))
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index 563c2f6..764fda0 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -68,6 +68,8 @@
                             width = ViewGroup.LayoutParams.MATCH_PARENT
                         }
                         elevation = this@ControlAdapter.elevation
+                        background = parent.context.getDrawable(
+                                R.drawable.control_background_ripple)
                     }
                 ) { id, favorite ->
                     model?.changeFavoriteStatus(id, favorite)
@@ -137,10 +139,7 @@
     private val title: TextView = itemView.requireViewById(R.id.title)
     private val subtitle: TextView = itemView.requireViewById(R.id.subtitle)
     private val removed: TextView = itemView.requireViewById(R.id.status)
-    private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite)
-    private val favoriteFrame: ViewGroup = itemView
-            .requireViewById<ViewGroup>(R.id.favorite_container)
-            .apply {
+    private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite).apply {
         visibility = View.VISIBLE
     }
 
@@ -155,7 +154,7 @@
         favorite.setOnClickListener {
             favoriteCallback(data.control.controlId, favorite.isChecked)
         }
-        favoriteFrame.setOnClickListener { favorite.performClick() }
+        itemView.setOnClickListener { favorite.performClick() }
         applyRenderInfo(renderInfo)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt
index 15c2a0a..a7a4103 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt
@@ -34,24 +34,29 @@
 
 /**
  * Creates all dialogs for challengeValues that can occur from a call to
- * {@link ControlsProviderService#performControlAction}. The types of challenge
- * responses are listed in {@link ControlAction.ResponseResult}.
+ * [ControlsProviderService#performControlAction]. The types of challenge responses are listed in
+ * [ControlAction.ResponseResult].
  */
 object ChallengeDialogs {
 
-    fun createPinDialog(cvh: ControlViewHolder): Dialog? {
+    private const val WINDOW_TYPE = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY
+    private const val STYLE = android.R.style.Theme_DeviceDefault_Dialog_Alert
+
+    /**
+     * AlertDialogs to handle [ControlAction#RESPONSE_CHALLENGE_PIN] and
+     * [ControlAction#RESPONSE_CHALLENGE_PIN] responses, decided by the useAlphaNumeric
+     * parameter.
+     */
+    fun createPinDialog(cvh: ControlViewHolder, useAlphaNumeric: Boolean): Dialog? {
         val lastAction = cvh.lastAction
         if (lastAction == null) {
             Log.e(ControlsUiController.TAG,
                 "PIN Dialog attempted but no last action is set. Will not show")
             return null
         }
-        val builder = AlertDialog.Builder(
-            cvh.context,
-            android.R.style.Theme_DeviceDefault_Dialog_Alert
-        ).apply {
+        val builder = AlertDialog.Builder(cvh.context, STYLE).apply {
             val res = cvh.context.resources
-            setTitle(res.getString(R.string.controls_pin_verify, *arrayOf(cvh.title.getText())))
+            setTitle(res.getString(R.string.controls_pin_verify, cvh.title.getText()))
             setView(R.layout.controls_dialog_pin)
             setPositiveButton(
                 android.R.string.ok,
@@ -71,25 +76,64 @@
         }
         return builder.create().apply {
             getWindow().apply {
-                setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+                setType(WINDOW_TYPE)
                 setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
             }
             setOnShowListener(DialogInterface.OnShowListener { _ ->
                 val editText = requireViewById<EditText>(R.id.controls_pin_input)
-                requireViewById<CheckBox>(R.id.controls_pin_use_alpha).setOnClickListener { v ->
-                    if ((v as CheckBox).isChecked) {
-                        editText.setInputType(
-                            InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD)
-                    } else {
-                        editText.setInputType(
-                            InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD)
-                    }
+                val useAlphaCheckBox = requireViewById<CheckBox>(R.id.controls_pin_use_alpha)
+                useAlphaCheckBox.setChecked(useAlphaNumeric)
+                setInputType(editText, useAlphaCheckBox.isChecked())
+                requireViewById<CheckBox>(R.id.controls_pin_use_alpha).setOnClickListener { _ ->
+                    setInputType(editText, useAlphaCheckBox.isChecked())
                 }
                 editText.requestFocus()
             })
         }
     }
 
+    /**
+     * AlertDialogs to handle [ControlAction#RESPONSE_CHALLENGE_ACK] response type.
+     */
+    fun createConfirmationDialog(cvh: ControlViewHolder): Dialog? {
+        val lastAction = cvh.lastAction
+        if (lastAction == null) {
+            Log.e(ControlsUiController.TAG,
+                "Confirmation Dialog attempted but no last action is set. Will not show")
+            return null
+        }
+        val builder = AlertDialog.Builder(cvh.context, STYLE).apply {
+            val res = cvh.context.resources
+            setMessage(res.getString(
+                R.string.controls_confirmation_message, cvh.title.getText()))
+            setPositiveButton(
+                android.R.string.ok,
+                DialogInterface.OnClickListener { dialog, _ ->
+                    cvh.action(addChallengeValue(lastAction, "true"))
+                    dialog.dismiss()
+            })
+            setNegativeButton(
+                android.R.string.cancel,
+                DialogInterface.OnClickListener { dialog, _ -> dialog.cancel() }
+            )
+        }
+        return builder.create().apply {
+            getWindow().apply {
+                setType(WINDOW_TYPE)
+            }
+        }
+    }
+
+    private fun setInputType(editText: EditText, useTextInput: Boolean) {
+        if (useTextInput) {
+            editText.setInputType(
+                InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD)
+        } else {
+            editText.setInputType(
+                InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_VARIATION_PASSWORD)
+        }
+    }
+
     private fun addChallengeValue(action: ControlAction, challengeValue: String): ControlAction {
         val id = action.getTemplateId()
         return when (action) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 9f5dd02..7d3a860 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -21,6 +21,7 @@
 import android.graphics.drawable.GradientDrawable
 import android.graphics.drawable.LayerDrawable
 import android.service.controls.Control
+import android.service.controls.DeviceTypes
 import android.service.controls.actions.ControlAction
 import android.service.controls.templates.ControlTemplate
 import android.service.controls.templates.StatelessTemplate
@@ -156,7 +157,11 @@
         statusExtra.setTextColor(fg)
 
         icon.setImageDrawable(ri.icon)
-        icon.setImageTintList(fg)
+
+        // do not color app icons
+        if (deviceType != DeviceTypes.TYPE_ROUTINE) {
+            icon.setImageTintList(fg)
+        }
 
         (clipLayer.getDrawable() as GradientDrawable).apply {
             setColor(context.getResources().getColor(bg, context.getTheme()))
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index b0db437..05a0c45 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -442,7 +442,15 @@
             controlViewsById.get(key)?.let { cvh ->
                 when (response) {
                     ControlAction.RESPONSE_CHALLENGE_PIN -> {
-                        activeDialog = ChallengeDialogs.createPinDialog(cvh)
+                        activeDialog = ChallengeDialogs.createPinDialog(cvh, false)
+                        activeDialog?.show()
+                    }
+                    ControlAction.RESPONSE_CHALLENGE_PASSPHRASE -> {
+                        activeDialog = ChallengeDialogs.createPinDialog(cvh, true)
+                        activeDialog?.show()
+                    }
+                    ControlAction.RESPONSE_CHALLENGE_ACK -> {
+                        activeDialog = ChallengeDialogs.createConfirmationDialog(cvh)
                         activeDialog?.show()
                     }
                     else -> cvh.actionResponse(response)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
index 27e4649..d33cd94 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
@@ -64,7 +64,7 @@
 
             val iconState = deviceIconMap.getValue(iconKey)
             val resourceId = iconState[enabled]
-            var icon: Drawable? = null
+            var icon: Drawable?
             if (resourceId == APP_ICON_ID) {
                 icon = appIconMap.get(componentName)
                 if (icon == null) {
@@ -110,60 +110,160 @@
 
 private val deviceIconMap = mapOf<Int, IconState>(
     THERMOSTAT_RANGE to IconState(
-        R.drawable.ic_device_thermostat_gm2_24px,
-        R.drawable.ic_device_thermostat_gm2_24px
+        R.drawable.ic_device_thermostat_off,
+        R.drawable.ic_device_thermostat_on
     ),
     (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_OFF) to IconState(
-        R.drawable.ic_device_thermostat_gm2_24px,
-        R.drawable.ic_device_thermostat_gm2_24px
+        R.drawable.ic_device_thermostat_off,
+        R.drawable.ic_device_thermostat_on
     ),
     (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT) to IconState(
-        R.drawable.ic_device_thermostat_gm2_24px,
-        R.drawable.ic_device_thermostat_gm2_24px
+        R.drawable.ic_device_thermostat_off,
+        R.drawable.ic_device_thermostat_on
     ),
     (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_COOL) to IconState(
-        R.drawable.ic_device_thermostat_gm2_24px,
-        R.drawable.ic_device_thermostat_gm2_24px
+        R.drawable.ic_device_thermostat_off,
+        R.drawable.ic_device_thermostat_on
     ),
     (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_HEAT_COOL) to IconState(
-        R.drawable.ic_device_thermostat_gm2_24px,
-        R.drawable.ic_device_thermostat_gm2_24px
+        R.drawable.ic_device_thermostat_off,
+        R.drawable.ic_device_thermostat_on
     ),
     (THERMOSTAT_RANGE + TemperatureControlTemplate.MODE_ECO) to IconState(
-        R.drawable.ic_device_thermostat_gm2_24px,
-        R.drawable.ic_device_thermostat_gm2_24px
+        R.drawable.ic_device_thermostat_off,
+        R.drawable.ic_device_thermostat_on
     ),
     DeviceTypes.TYPE_THERMOSTAT to IconState(
-        R.drawable.ic_device_thermostat_gm2_24px,
-        R.drawable.ic_device_thermostat_gm2_24px
+        R.drawable.ic_device_thermostat_off,
+        R.drawable.ic_device_thermostat_on
     ),
     DeviceTypes.TYPE_LIGHT to IconState(
-        R.drawable.ic_light_off_gm2_24px,
-        R.drawable.ic_lightbulb_outline_gm2_24px
+        R.drawable.ic_device_light_off,
+        R.drawable.ic_device_light_on
     ),
     DeviceTypes.TYPE_CAMERA to IconState(
-        R.drawable.ic_videocam_gm2_24px,
-        R.drawable.ic_videocam_gm2_24px
+        R.drawable.ic_device_camera_off,
+        R.drawable.ic_device_camera_on
     ),
     DeviceTypes.TYPE_LOCK to IconState(
-        R.drawable.ic_lock_open_gm2_24px,
-        R.drawable.ic_lock_gm2_24px
+        R.drawable.ic_device_lock_off,
+        R.drawable.ic_device_lock_on
     ),
     DeviceTypes.TYPE_SWITCH to IconState(
-        R.drawable.ic_switches_gm2_24px,
-        R.drawable.ic_switches_gm2_24px
+        R.drawable.ic_device_switch_off,
+        R.drawable.ic_device_switch_on
     ),
     DeviceTypes.TYPE_OUTLET to IconState(
-        R.drawable.ic_power_off_gm2_24px,
-        R.drawable.ic_power_gm2_24px
+        R.drawable.ic_device_outlet_off,
+        R.drawable.ic_device_outlet_on
     ),
     DeviceTypes.TYPE_VACUUM to IconState(
-        R.drawable.ic_vacuum_gm2_24px,
-        R.drawable.ic_vacuum_gm2_24px
+        R.drawable.ic_device_vacuum_off,
+        R.drawable.ic_device_vacuum_on
     ),
     DeviceTypes.TYPE_MOP to IconState(
-        R.drawable.ic_vacuum_gm2_24px,
-        R.drawable.ic_vacuum_gm2_24px
+        R.drawable.ic_device_mop_off,
+        R.drawable.ic_device_mop_on
+    ),
+    DeviceTypes.TYPE_AIR_FRESHENER to IconState(
+        R.drawable.ic_device_air_freshener_off,
+        R.drawable.ic_device_air_freshener_on
+    ),
+    DeviceTypes.TYPE_AIR_PURIFIER to IconState(
+        R.drawable.ic_device_air_purifier_off,
+        R.drawable.ic_device_air_purifier_on
+    ),
+    DeviceTypes.TYPE_FAN to IconState(
+        R.drawable.ic_device_fan_off,
+        R.drawable.ic_device_fan_on
+    ),
+    DeviceTypes.TYPE_HOOD to IconState(
+        R.drawable.ic_device_hood_off,
+        R.drawable.ic_device_hood_on
+    ),
+    DeviceTypes.TYPE_KETTLE to IconState(
+        R.drawable.ic_device_kettle_off,
+        R.drawable.ic_device_kettle_on
+    ),
+    DeviceTypes.TYPE_MICROWAVE to IconState(
+        R.drawable.ic_device_microwave_off,
+        R.drawable.ic_device_microwave_on
+    ),
+    DeviceTypes.TYPE_REMOTE_CONTROL to IconState(
+        R.drawable.ic_device_remote_control_off,
+        R.drawable.ic_device_remote_control_on
+    ),
+    DeviceTypes.TYPE_SET_TOP to IconState(
+        R.drawable.ic_device_set_top_off,
+        R.drawable.ic_device_set_top_on
+    ),
+    DeviceTypes.TYPE_STYLER to IconState(
+        R.drawable.ic_device_styler_off,
+        R.drawable.ic_device_styler_on
+    ),
+    DeviceTypes.TYPE_TV to IconState(
+        R.drawable.ic_device_tv_off,
+        R.drawable.ic_device_tv_on
+    ),
+    DeviceTypes.TYPE_WATER_HEATER to IconState(
+        R.drawable.ic_device_water_heater_off,
+        R.drawable.ic_device_water_heater_on
+    ),
+    DeviceTypes.TYPE_DISHWASHER to IconState(
+        R.drawable.ic_device_dishwasher_off,
+        R.drawable.ic_device_dishwasher_on
+    ),
+    DeviceTypes.TYPE_MULTICOOKER to IconState(
+        R.drawable.ic_device_multicooker_off,
+        R.drawable.ic_device_multicooker_on
+    ),
+    DeviceTypes.TYPE_SPRINKLER to IconState(
+        R.drawable.ic_device_sprinkler_off,
+        R.drawable.ic_device_sprinkler_on
+    ),
+    DeviceTypes.TYPE_WASHER to IconState(
+        R.drawable.ic_device_washer_off,
+        R.drawable.ic_device_washer_on
+    ),
+    DeviceTypes.TYPE_BLINDS to IconState(
+        R.drawable.ic_device_blinds_off,
+        R.drawable.ic_device_blinds_on
+    ),
+    DeviceTypes.TYPE_DRAWER to IconState(
+        R.drawable.ic_device_drawer_off,
+        R.drawable.ic_device_drawer_on
+    ),
+    DeviceTypes.TYPE_GARAGE to IconState(
+        R.drawable.ic_device_garage_off,
+        R.drawable.ic_device_garage_on
+    ),
+    DeviceTypes.TYPE_GATE to IconState(
+        R.drawable.ic_device_gate_off,
+        R.drawable.ic_device_gate_on
+    ),
+    DeviceTypes.TYPE_PERGOLA to IconState(
+        R.drawable.ic_device_pergola_off,
+        R.drawable.ic_device_pergola_on
+    ),
+    DeviceTypes.TYPE_WINDOW to IconState(
+        R.drawable.ic_device_window_off,
+        R.drawable.ic_device_window_on
+    ),
+    DeviceTypes.TYPE_VALVE to IconState(
+        R.drawable.ic_device_valve_off,
+        R.drawable.ic_device_valve_on
+    ),
+    DeviceTypes.TYPE_SECURITY_SYSTEM to IconState(
+        R.drawable.ic_device_security_system_off,
+        R.drawable.ic_device_security_system_on
+    ),
+    DeviceTypes.TYPE_REFRIGERATOR to IconState(
+        R.drawable.ic_device_refrigerator_off,
+        R.drawable.ic_device_refrigerator_on
+    ),
+    DeviceTypes.TYPE_DOORBELL to IconState(
+        R.drawable.ic_device_doorbell_off,
+        R.drawable.ic_device_doorbell_on
     ),
     DeviceTypes.TYPE_ROUTINE to IconState(
         RenderInfo.APP_ICON_ID,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
index c495c58..f79c8b2 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -64,12 +64,13 @@
 
         val gestureListener = ToggleRangeGestureListener(cvh.layout)
         val gestureDetector = GestureDetector(context, gestureListener)
-        cvh.layout.setOnTouchListener { _: View, e: MotionEvent ->
+        cvh.layout.setOnTouchListener { v: View, e: MotionEvent ->
             if (gestureDetector.onTouchEvent(e)) {
                 return@setOnTouchListener true
             }
 
             if (e.getAction() == MotionEvent.ACTION_UP && gestureListener.isDragging) {
+                v.getParent().requestDisallowInterceptTouchEvent(false)
                 gestureListener.isDragging = false
                 endUpdateRange()
                 return@setOnTouchListener true
@@ -254,6 +255,7 @@
             yDiff: Float
         ): Boolean {
             if (!isDragging) {
+                v.getParent().requestDisallowInterceptTouchEvent(true)
                 this@ToggleRangeBehavior.beginUpdateRange()
                 isDragging = true
             }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 09d7d26..b329991 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -490,6 +490,9 @@
         public void onPowerSaveChanged(boolean active) {
             if (mDozeHost.isPowerSaveActive()) {
                 mMachine.requestState(DozeMachine.State.DOZE);
+            } else if (mMachine.getState() == DozeMachine.State.DOZE
+                    && mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) {
+                mMachine.requestState(DozeMachine.State.DOZE_AOD);
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 73539f9..6514ca4 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1849,7 +1849,7 @@
                     .alpha(1)
                     .translationX(0)
                     .translationY(0)
-                    .setDuration(300)
+                    .setDuration(450)
                     .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                     .setUpdateListener(animation -> {
                         float animatedValue = animation.getAnimatedFraction();
@@ -1878,7 +1878,7 @@
                     .alpha(0)
                     .translationX(mGlobalActionsLayout.getAnimationOffsetX())
                     .translationY(mGlobalActionsLayout.getAnimationOffsetY())
-                    .setDuration(300)
+                    .setDuration(550)
                     .withEndAction(this::completeDismiss)
                     .setInterpolator(new LogAccelerateInterpolator())
                     .setUpdateListener(animation -> {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 12955a1..ce29859 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -49,8 +49,6 @@
 
 public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks {
 
-    private static final float SHUTDOWN_SCRIM_ALPHA = 0.95f;
-
     private final Context mContext;
     private final Lazy<GlobalActionsDialog> mGlobalActionsDialogLazy;
     private final KeyguardStateController mKeyguardStateController;
@@ -124,7 +122,7 @@
                         | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                         | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
         window.setBackgroundDrawable(background);
-        window.setWindowAnimations(R.style.Animation_Toast);
+        window.setWindowAnimations(com.android.systemui.R.style.Animation_ShutdownUi);
 
         d.setContentView(R.layout.shutdown_dialog);
         d.setCancelable(false);
@@ -153,7 +151,9 @@
             mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(),
                         mBlurUtils.blurRadiusOfRatio(1));
         } else {
-            background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255));
+            float backgroundAlpha = mContext.getResources().getFloat(
+                    com.android.systemui.R.dimen.shutdown_scrim_behind_alpha);
+            background.setAlpha((int) (backgroundAlpha * 255));
         }
 
         d.show();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 2cc3d9e..96494cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -305,7 +305,8 @@
                 oldInstance.onDestroy();
             }
             mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern);
-            mPendingIntent = PendingIntent.getActivity(getContext(), 0, new Intent(), 0);
+            mPendingIntent = PendingIntent.getActivity(getContext(), 0,
+                    new Intent(getContext(), KeyguardSliceProvider.class), 0);
             mMediaManager.addCallback(this);
             mStatusBarStateController.addCallback(this);
             mNextAlarmController.addCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 902b578..af8b184 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.pip;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
 import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA;
 import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
 import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_NONE;
@@ -26,8 +29,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.window.ITaskOrganizerController;
 import android.app.PictureInPictureParams;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -38,9 +39,9 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Size;
+import android.view.SurfaceControl;
 import android.window.ITaskOrganizer;
 import android.window.IWindowContainer;
-import android.view.SurfaceControl;
 import android.window.WindowContainerTransaction;
 import android.window.WindowOrganizer;
 
@@ -216,6 +217,29 @@
         mOneShotAnimationType = animationType;
     }
 
+    /**
+     * Dismiss PiP, this is done in two phases using {@link WindowContainerTransaction}
+     * - setActivityWindowingMode to fullscreen at beginning of the transaction. without changing
+     *   the windowing mode of the Task itself. This makes sure the activity render it's fullscreen
+     *   configuration while the Task is still in PiP.
+     * - setWindowingMode to fullscreen at the end of transition
+     * @param animationDurationMs duration in millisecond for the exiting PiP transition
+     */
+    public void dismissPip(int animationDurationMs) {
+        try {
+            final WindowContainerTransaction wct = new WindowContainerTransaction();
+            wct.setActivityWindowingMode(mToken, WINDOWING_MODE_FULLSCREEN);
+            WindowOrganizer.applyTransaction(wct);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to apply container transaction", e);
+        }
+        final Rect destinationBounds = mBoundsToRestore.remove(mToken.asBinder());
+        scheduleAnimateResizePip(mLastReportedBounds, destinationBounds,
+                TRANSITION_DIRECTION_TO_FULLSCREEN, animationDurationMs,
+                null /* updateBoundsCallback */);
+        mInPip = false;
+    }
+
     @Override
     public void onTaskAppeared(ActivityManager.RunningTaskInfo info) {
         Objects.requireNonNull(info, "Requires RunningTaskInfo");
@@ -235,7 +259,8 @@
         mBoundsToRestore.put(mToken.asBinder(), currentBounds);
         if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
             scheduleAnimateResizePip(currentBounds, destinationBounds,
-                    TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, null);
+                    TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration,
+                    null /* updateBoundsCallback */);
         } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
             mUpdateHandler.post(() -> mPipAnimationController
                     .getAnimator(mLeash, destinationBounds, 0f, 1f)
@@ -249,6 +274,12 @@
         }
     }
 
+    /**
+     * Note that dismissing PiP is now originated from SystemUI, see {@link #dismissPip(int)}.
+     * Meanwhile this callback is invoked whenever the task is removed. For instance:
+     *   - as a result of removeStacksInWindowingModes from WM
+     *   - activity itself is died
+     */
     @Override
     public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
         IWindowContainer token = info.token;
@@ -259,7 +290,8 @@
         }
         final Rect boundsToRestore = mBoundsToRestore.remove(token.asBinder());
         scheduleAnimateResizePip(mLastReportedBounds, boundsToRestore,
-                TRANSITION_DIRECTION_TO_FULLSCREEN, mEnterExitAnimationDuration, null);
+                TRANSITION_DIRECTION_TO_FULLSCREEN, mEnterExitAnimationDuration,
+                null /* updateBoundsCallback */);
         mInPip = false;
     }
 
@@ -274,7 +306,8 @@
                 getAspectRatioOrDefault(newParams),
                 null /* bounds */, getMinimalSize(info.topActivityInfo));
         Objects.requireNonNull(destinationBounds, "Missing destination bounds");
-        scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration, null);
+        scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration,
+                null /* updateBoundsCallback */);
     }
 
     /**
@@ -346,6 +379,7 @@
         final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
         mSurfaceTransactionHelper
                 .crop(tx, mLeash, destinationBounds)
+                .resetScale(tx, mLeash, destinationBounds)
                 .round(tx, mLeash, mInPip);
         scheduleFinishResizePip(tx, destinationBounds, TRANSITION_DIRECTION_NONE, null);
     }
@@ -433,12 +467,19 @@
         }
         mLastReportedBounds.set(destinationBounds);
         try {
-            // If we are animating to fullscreen, then we need to reset the override bounds on the
-            // task to ensure that the task "matches" the parent's bounds
-            Rect taskBounds = direction == TRANSITION_DIRECTION_TO_FULLSCREEN
-                    ? null
-                    : destinationBounds;
             final WindowContainerTransaction wct = new WindowContainerTransaction();
+            final Rect taskBounds;
+            if (direction == TRANSITION_DIRECTION_TO_FULLSCREEN) {
+                // If we are animating to fullscreen, then we need to reset the override bounds
+                // on the task to ensure that the task "matches" the parent's bounds, this applies
+                // also to the final windowing mode, which should be reset to undefined rather than
+                // fullscreen.
+                taskBounds = null;
+                wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED)
+                        .setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
+            } else {
+                taskBounds = destinationBounds;
+            }
             if (direction == TRANSITION_DIRECTION_TO_PIP) {
                 wct.scheduleFinishEnterPip(mToken, taskBounds);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 449a2bc..7974281 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -219,11 +219,7 @@
         cancelAnimations();
         mMenuController.hideMenuWithoutResize();
         mPipTaskOrganizer.getUpdateHandler().post(() -> {
-            try {
-                mActivityTaskManager.dismissPip(!skipAnimation, EXPAND_STACK_TO_FULLSCREEN_DURATION);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error expanding PiP activity", e);
-            }
+            mPipTaskOrganizer.dismissPip(skipAnimation ? 0 : EXPAND_STACK_TO_FULLSCREEN_DURATION);
         });
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 014f3b5..0b07655 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -99,14 +99,14 @@
         mEnablePipResize = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 PIP_USER_RESIZE,
-                /* defaultValue = */ false);
+                /* defaultValue = */ true);
         deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor,
                 new DeviceConfig.OnPropertiesChangedListener() {
                     @Override
                     public void onPropertiesChanged(DeviceConfig.Properties properties) {
                         if (properties.getKeyset().contains(PIP_USER_RESIZE)) {
                             mEnablePipResize = properties.getBoolean(
-                                    PIP_USER_RESIZE, /* defaultValue = */ false);
+                                    PIP_USER_RESIZE, /* defaultValue = */ true);
                         }
                     }
                 });
@@ -208,7 +208,8 @@
                     final Rect currentPipBounds = mMotionHelper.getBounds();
                     mLastResizeBounds.set(TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(),
                             mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
-                            mMinSize.y, mMaxSize, true, true));
+                            mMinSize.y, mMaxSize, true,
+                            mLastDownBounds.width() > mLastDownBounds.height()));
                     mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds);
                     mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds,
                             null);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 0403a05..cd73721 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -142,6 +142,9 @@
     @Override
     public void computeScroll() {
         if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
+            if (!isFakeDragging()) {
+                beginFakeDrag();
+            }
             fakeDragBy(getScrollX() - mScroller.getCurrX());
             // Keep on drawing until the animation has finished.
             postInvalidateOnAnimation();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 5ccf8c7..33cc086 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -133,6 +133,9 @@
             new LocalMediaManager.DeviceCallback() {
         @Override
         public void onDeviceListUpdate(List<MediaDevice> devices) {
+            if (mLocalMediaManager == null) {
+                return;
+            }
             MediaDevice currentDevice = mLocalMediaManager.getCurrentConnectedDevice();
             // Check because this can be called several times while changing devices
             if (mDevice == null || !mDevice.equals(currentDevice)) {
@@ -293,14 +296,17 @@
         if (mMediaPlayers.size() > 0) {
             ((View) mMediaCarousel.getParent()).setVisibility(View.VISIBLE);
 
-            // Set up listener for device changes
-            // TODO: integrate with MediaTransferManager?
-            InfoMediaManager imm =
-                    new InfoMediaManager(mContext, null, null, mLocalBluetoothManager);
-            mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager, imm, null);
-            mLocalMediaManager.startScan();
-            mDevice = mLocalMediaManager.getCurrentConnectedDevice();
-            mLocalMediaManager.registerCallback(mDeviceCallback);
+            if (mLocalMediaManager == null) {
+                // Set up listener for device changes
+                // TODO: integrate with MediaTransferManager?
+                InfoMediaManager imm =
+                        new InfoMediaManager(mContext, null, null, mLocalBluetoothManager);
+                mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager, imm,
+                        null);
+                mLocalMediaManager.startScan();
+                mDevice = mLocalMediaManager.getCurrentConnectedDevice();
+                mLocalMediaManager.registerCallback(mDeviceCallback);
+            }
         }
     }
 
@@ -323,8 +329,11 @@
         mMediaCarousel.removeView(player.getView());
         if (mMediaPlayers.size() == 0) {
             ((View) mMediaCarousel.getParent()).setVisibility(View.GONE);
-            mLocalMediaManager.stopScan();
-            mLocalMediaManager.unregisterCallback(mDeviceCallback);
+            if (mLocalMediaManager != null) {
+                mLocalMediaManager.stopScan();
+                mLocalMediaManager.unregisterCallback(mDeviceCallback);
+                mLocalMediaManager = null;
+            }
         }
         return true;
     }
@@ -397,6 +406,7 @@
         if (mLocalMediaManager != null) {
             mLocalMediaManager.stopScan();
             mLocalMediaManager.unregisterCallback(mDeviceCallback);
+            mLocalMediaManager = null;
         }
         super.onDetachedFromWindow();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index aa64449..3b3d9dd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -17,7 +17,6 @@
 package com.android.systemui.recents;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 
 import static com.android.systemui.Prefs.Key.DISMISSED_RECENTS_SWIPE_UP_ONBOARDING_COUNT;
@@ -27,8 +26,7 @@
 import static com.android.systemui.Prefs.Key.OVERVIEW_OPENED_COUNT;
 import static com.android.systemui.Prefs.Key.OVERVIEW_OPENED_FROM_HOME_COUNT;
 import static com.android.systemui.shared.system.LauncherEventUtil.DISMISS;
-import static com.android.systemui.shared.system.LauncherEventUtil
-        .RECENTS_QUICK_SCRUB_ONBOARDING_TIP;
+import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_QUICK_SCRUB_ONBOARDING_TIP;
 import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_SWIPE_UP_ONBOARDING_TIP;
 import static com.android.systemui.shared.system.LauncherEventUtil.VISIBLE;
 
@@ -139,7 +137,7 @@
 
         private void onAppLaunch() {
             ActivityManager.RunningTaskInfo info = ActivityManagerWrapper.getInstance()
-                    .getRunningTask(ACTIVITY_TYPE_UNDEFINED /* ignoreActivityType */);
+                    .getRunningTask();
             if (info == null) {
                 return;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java
index 3cdc01d..dea8c32 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserObservable.java
@@ -38,7 +38,7 @@
         @Override
         protected void onInactive() {
             super.onInactive();
-            mTracker.startTracking();
+            mTracker.stopTracking();
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 3879c16..1aa7831 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -25,6 +25,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -129,6 +130,7 @@
 
     private int mDividerInsets;
     private final Display mDefaultDisplay;
+    private boolean mSupportSplitScreenMultiWindow;
 
     private int mDividerSize;
     private int mTouchElevation;
@@ -282,6 +284,8 @@
         final DisplayManager displayManager =
                 (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
         mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+        mSupportSplitScreenMultiWindow =
+                ActivityTaskManager.supportsSplitScreenMultiWindow(mContext);
     }
 
     @Override
@@ -354,6 +358,11 @@
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        if (!mSupportSplitScreenMultiWindow) {
+            super.onLayout(changed, left, top, right, bottom);
+            return;
+        }
+
         if (mFirstLayout) {
             // Wait for first layout so that the ViewRootImpl surface has been created.
             initializeSurfaceState();
@@ -1085,6 +1094,13 @@
         crop.offsetTo(-(otherTaskRect.left - otherRect.left),
                 -(otherTaskRect.top - otherRect.top));
         t.setWindowCrop(mTiles.mSecondarySurface, crop);
+        // Reposition home and recents surfaces or they would be positioned relatively to its
+        // parent (split-screen secondary task) position.
+        for (int i = mTiles.mHomeAndRecentsSurfaces.size() - 1; i >= 0; --i) {
+            t.setPosition(mTiles.mHomeAndRecentsSurfaces.get(i),
+                    mTiles.mHomeBounds.left - otherTaskRect.left,
+                    mTiles.mHomeBounds.top - otherTaskRect.top);
+        }
         final SurfaceControl dividerCtrl = getWindowSurfaceControl();
         if (dividerCtrl != null) {
             if (isHorizontalDivision()) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index c4089e5..6cb7f4f 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -26,12 +26,15 @@
 
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.WindowConfiguration;
+import android.graphics.Rect;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.Display;
-import android.window.ITaskOrganizer;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
+import android.window.ITaskOrganizer;
+
+import java.util.ArrayList;
 
 class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub {
     private static final String TAG = "SplitScreenTaskOrganizer";
@@ -43,6 +46,8 @@
     SurfaceControl mSecondarySurface;
     SurfaceControl mPrimaryDim;
     SurfaceControl mSecondaryDim;
+    ArrayList<SurfaceControl> mHomeAndRecentsSurfaces = new ArrayList<>();
+    Rect mHomeBounds = new Rect();
     final Divider mDivider;
 
     SplitScreenTaskOrganizer(Divider divider) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 8724e49..6ed7afe 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -29,9 +29,9 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.Display;
+import android.view.WindowManagerGlobal;
 import android.window.IWindowContainer;
 import android.window.WindowContainerTransaction;
-import android.view.WindowManagerGlobal;
 import android.window.WindowOrganizer;
 
 import com.android.internal.annotations.GuardedBy;
@@ -157,6 +157,7 @@
         for (int i = homeStacks.size() - 1; i >= 0; --i) {
             wct.setBounds(homeStacks.get(i), homeBounds);
         }
+        layout.mTiles.mHomeBounds.set(homeBounds);
         return isHomeResizable;
     }
 
@@ -180,13 +181,17 @@
             if (rootTasks.isEmpty()) {
                 return false;
             }
+            tiles.mHomeAndRecentsSurfaces.clear();
             for (int i = rootTasks.size() - 1; i >= 0; --i) {
-                if (rootTasks.get(i).configuration.windowConfiguration.getWindowingMode()
+                final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i);
+                if (isHomeOrRecentTask(rootTask)) {
+                    tiles.mHomeAndRecentsSurfaces.add(rootTask.token.getLeash());
+                }
+                if (rootTask.configuration.windowConfiguration.getWindowingMode()
                         != WINDOWING_MODE_FULLSCREEN) {
                     continue;
                 }
-                wct.reparent(rootTasks.get(i).token, tiles.mSecondary.token,
-                        true /* onTop */);
+                wct.reparent(rootTask.token, tiles.mSecondary.token, true /* onTop */);
             }
             boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct);
             WindowOrganizer.applyTransaction(wct);
@@ -213,6 +218,7 @@
             // Set launch root first so that any task created after getChildContainers and
             // before reparent (pretty unlikely) are put into fullscreen.
             TaskOrganizer.setLaunchRoot(Display.DEFAULT_DISPLAY, null);
+            tiles.mHomeAndRecentsSurfaces.clear();
             // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished
             //                 plus specific APIs to clean this up.
             List<ActivityManager.RunningTaskInfo> primaryChildren =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 8945f36..a3faa80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -67,32 +67,9 @@
     private var updateScheduled: Boolean = false
     private var shadeExpansion = 0f
     @VisibleForTesting
-    var shadeSpring = SpringAnimation(this, object :
-            FloatPropertyCompat<NotificationShadeDepthController>("shadeBlurRadius") {
-        override fun setValue(rect: NotificationShadeDepthController?, value: Float) {
-            shadeBlurRadius = value.toInt()
-        }
-
-        override fun getValue(rect: NotificationShadeDepthController?): Float {
-            return shadeBlurRadius.toFloat()
-        }
-    })
-    private val zoomInterpolator = Interpolators.ACCELERATE_DECELERATE
-
-    /**
-     * Radius that we're animating to.
-     */
-    private var pendingShadeBlurRadius = -1
-
-    /**
-     * Shade blur radius on the current frame.
-     */
-    private var shadeBlurRadius = 0
-        set(value) {
-            if (field == value) return
-            field = value
-            scheduleUpdate()
-        }
+    var shadeSpring = DepthAnimation()
+    @VisibleForTesting
+    var globalActionsSpring = DepthAnimation()
 
     /**
      * Blur radius of the wake-up animation on this frame.
@@ -103,7 +80,6 @@
             field = value
             scheduleUpdate()
         }
-    private var globalDialogVisibility = 0f
 
     /**
      * Callback that updates the window blur value and is called only once per frame.
@@ -111,12 +87,9 @@
     private val updateBlurCallback = Choreographer.FrameCallback {
         updateScheduled = false
 
-        val blur = max(shadeBlurRadius,
-                max(wakeAndUnlockBlurRadius, blurUtils.blurRadiusOfRatio(globalDialogVisibility)))
+        val blur = max(max(shadeSpring.radius, wakeAndUnlockBlurRadius), globalActionsSpring.radius)
         blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur)
-        val rawZoom = max(blurUtils.ratioOfBlurRadius(blur), globalDialogVisibility)
-        wallpaperManager.setWallpaperZoomOut(root.windowToken,
-                zoomInterpolator.getInterpolation(rawZoom))
+        wallpaperManager.setWallpaperZoomOut(root.windowToken, blurUtils.ratioOfBlurRadius(blur))
         notificationShadeWindowController.setBackgroundBlurRadius(blur)
     }
 
@@ -163,8 +136,9 @@
         }
 
         override fun onDozingChanged(isDozing: Boolean) {
-            if (isDozing && shadeSpring.isRunning) {
-                shadeSpring.skipToEnd()
+            if (isDozing) {
+                shadeSpring.finishIfRunning()
+                globalActionsSpring.finishIfRunning()
             }
         }
     }
@@ -174,10 +148,6 @@
         if (WAKE_UP_ANIMATION_ENABLED) {
             keyguardStateController.addCallback(keyguardStateCallback)
         }
-        shadeSpring.spring = SpringForce(0.0f)
-        shadeSpring.spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
-        shadeSpring.spring.stiffness = SpringForce.STIFFNESS_LOW
-        shadeSpring.addEndListener { _, _, _, _ -> pendingShadeBlurRadius = -1 }
         statusBarStateController.addCallback(statusBarStateCallback)
     }
 
@@ -198,11 +168,7 @@
             newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion)
         }
 
-        if (pendingShadeBlurRadius == newBlur) {
-            return
-        }
-        pendingShadeBlurRadius = newBlur
-        shadeSpring.animateToFinalPosition(newBlur.toFloat())
+        shadeSpring.animateTo(newBlur)
     }
 
     private fun scheduleUpdate(viewToBlur: View? = null) {
@@ -215,19 +181,72 @@
     }
 
     fun updateGlobalDialogVisibility(visibility: Float, dialogView: View) {
-        if (visibility == globalDialogVisibility) {
-            return
-        }
-        globalDialogVisibility = visibility
-        scheduleUpdate(dialogView)
+        globalActionsSpring.animateTo(blurUtils.blurRadiusOfRatio(visibility), dialogView)
     }
 
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
         IndentingPrintWriter(pw, "  ").let {
             it.println("StatusBarWindowBlurController:")
             it.increaseIndent()
-            it.println("shadeBlurRadius: $shadeBlurRadius")
+            it.println("shadeRadius: ${shadeSpring.radius}")
+            it.println("globalActionsRadius: ${globalActionsSpring.radius}")
             it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius")
         }
     }
+
+    /**
+     * Animation helper that smoothly animates the depth using a spring and deals with frame
+     * invalidation.
+     */
+    inner class DepthAnimation() {
+        /**
+         * Blur radius visible on the UI, in pixels.
+         */
+        var radius = 0
+            private set
+
+        /**
+         * Radius that we're animating to.
+         */
+        private var pendingRadius = -1
+
+        /**
+         * View on {@link Surface} that wants depth.
+         */
+        private var view: View? = null
+
+        private var springAnimation = SpringAnimation(this, object :
+            FloatPropertyCompat<DepthAnimation>("blurRadius") {
+            override fun setValue(rect: DepthAnimation?, value: Float) {
+                radius = value.toInt()
+                scheduleUpdate(view)
+            }
+
+            override fun getValue(rect: DepthAnimation?): Float {
+                return radius.toFloat()
+            }
+        })
+
+        init {
+            springAnimation.spring = SpringForce(0.0f)
+            springAnimation.spring.dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
+            springAnimation.spring.stiffness = SpringForce.STIFFNESS_MEDIUM
+            springAnimation.addEndListener { _, _, _, _ -> pendingRadius = -1 }
+        }
+
+        fun animateTo(newRadius: Int, viewToBlur: View? = null) {
+            if (pendingRadius == newRadius && view == viewToBlur) {
+                return
+            }
+            view = viewToBlur
+            pendingRadius = newRadius
+            springAnimation.animateToFinalPosition(newRadius.toFloat())
+        }
+
+        fun finishIfRunning() {
+            if (springAnimation.isRunning) {
+                springAnimation.skipToEnd()
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index c6d84ff..d2f781d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -651,7 +651,7 @@
      */
     public void updateNotifications(String reason) {
         reapplyFilterAndSort(reason);
-        if (mPresenter != null) {
+        if (mPresenter != null && !mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
             mPresenter.updateNotificationViews();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
index 0437877..cf670bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifViewManager.kt
@@ -142,9 +142,11 @@
         // To attach rows we can use _this one weird trick_: if the intended view to add does not
         // have a parent, then simply add it (and its children).
         entries.forEach { entry ->
-            val listItem = rowRegistry.requireView(entry)
+            // TODO: We should eventually map GroupEntry's themselves to views so that we don't
+            // depend on representativeEntry here which may actually be null in the future
+            val listItem = rowRegistry.requireView(entry.representativeEntry!!)
 
-            if (listItem.view.parent != null) {
+            if (listItem.view.parent == null) {
                 listContainer.addListItem(listItem)
                 stabilityManager.notifyViewAddition(listItem.view)
             }
@@ -153,7 +155,8 @@
                 for ((idx, childEntry) in entry.children.withIndex()) {
                     val childListItem = rowRegistry.requireView(childEntry)
                     // Child hasn't been added yet. add it!
-                    if (!listItem.notificationChildren.contains(childListItem)) {
+                    if (listItem.notificationChildren == null ||
+                            !listItem.notificationChildren.contains(childListItem)) {
                         // TODO: old code here just Log.wtf()'d here. This might wreak havoc
                         if (childListItem.view.parent != null) {
                             throw IllegalStateException("trying to add a notification child that " +
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 7019b5b..808e1b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -108,6 +108,12 @@
     /** If this notification was filtered out, then the filter that did the filtering. */
     @Nullable NotifFilter mExcludingFilter;
 
+    /**
+     * The NotifFilter, if any, that was active on this notification during the previous run of
+     * the list builder.
+     */
+    @Nullable NotifFilter mPreviousExcludingFilter;
+
     /** If this was a group child that was promoted to the top level, then who did the promoting. */
     @Nullable NotifPromoter mNotifPromoter;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index f7d6cef..19f7cef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -316,6 +316,7 @@
 
         // Step 7: Lock in our group structure and log anything that's changed since the last run
         mPipelineState.incrementTo(STATE_FINALIZING);
+        logFilterChanges();
         logParentingChanges();
         freeEmptyGroups();
 
@@ -363,6 +364,9 @@
             entry.setPreviousParent(entry.getParent());
             entry.setParent(null);
 
+            entry.mPreviousExcludingFilter = entry.mExcludingFilter;
+            entry.mExcludingFilter = null;
+
             if (entry.mFirstAddedIteration == -1) {
                 entry.mFirstAddedIteration = mIterationCount;
             }
@@ -371,8 +375,10 @@
         mNotifList.clear();
     }
 
-    private void filterNotifs(Collection<? extends ListEntry> entries,
-            List<ListEntry> out, List<NotifFilter> filters) {
+    private void filterNotifs(
+            Collection<? extends ListEntry> entries,
+            List<ListEntry> out,
+            List<NotifFilter> filters) {
         final long now = mSystemClock.uptimeMillis();
         for (ListEntry entry : entries)  {
             if (entry instanceof GroupEntry) {
@@ -585,8 +591,9 @@
      * filtered out during any of the filtering steps.
      */
     private void annulAddition(ListEntry entry) {
-        entry.setSection(-1);
-        entry.mNotifSection = null;
+        // TODO: We should null out the entry's section and promoter here. However, if we do that,
+        //  future runs will think that the section changed. We need a mPreviousNotifSection,
+        //  similar to what we do for parents.
         entry.setParent(null);
         if (entry.mFirstAddedIteration == mIterationCount) {
             entry.mFirstAddedIteration = -1;
@@ -615,6 +622,17 @@
         mGroups.values().removeIf(ge -> ge.getSummary() == null && ge.getChildren().isEmpty());
     }
 
+    private void logFilterChanges() {
+        for (NotificationEntry entry : mAllEntries) {
+            if (entry.mExcludingFilter != entry.mPreviousExcludingFilter) {
+                mLogger.logFilterChanged(
+                        entry.getKey(),
+                        entry.mPreviousExcludingFilter,
+                        entry.mExcludingFilter);
+            }
+        }
+    }
+
     private void logParentingChanges() {
         for (NotificationEntry entry : mAllEntries) {
             if (entry.getParent() != entry.getPreviousParent()) {
@@ -680,21 +698,8 @@
     };
 
     private boolean applyFilters(NotificationEntry entry, long now, List<NotifFilter> filters) {
-        NotifFilter filter = findRejectingFilter(entry, now, filters);
-
-        if (filter != entry.mExcludingFilter) {
-            mLogger.logFilterChanged(
-                    entry.getKey(),
-                    entry.mExcludingFilter != null ? entry.mExcludingFilter.getName() : null,
-                    filter != null ? filter.getName() : null);
-
-            // Note that groups and summaries can also be filtered out later if they're part of a
-            // malformed group. We currently don't have a great way to track that beyond parenting
-            // change logs. Consider adding something similar to mExcludingFilter for them.
-            entry.mExcludingFilter = filter;
-        }
-
-        return filter != null;
+        entry.mExcludingFilter = findRejectingFilter(entry, now, filters);
+        return entry.mExcludingFilter != null;
     }
 
     @Nullable private static NotifFilter findRejectingFilter(NotificationEntry entry, long now,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java
new file mode 100644
index 0000000..261ae07
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 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.systemui.statusbar.notification.collection.coordinator;
+
+import android.content.pm.UserInfo;
+import android.util.SparseArray;
+
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+
+import javax.inject.Inject;
+
+/**
+ * A coordinator that filters out notifications for other users
+ *
+ * The NotifCollection contains the notifs for ALL users, so we need to remove any notifications
+ * that have been posted specifically to other users. Note that some system notifications are not
+ * posted to any particular user, and so must be shown to everyone.
+ *
+ * TODO: The NotificationLockscreenUserManager currently maintains the list of active user profiles.
+ *  We should spin that off into a standalone section at some point.
+ */
+public class HideNotifsForOtherUsersCoordinator implements Coordinator {
+    private final NotificationLockscreenUserManager mLockscreenUserManager;
+
+    @Inject
+    public HideNotifsForOtherUsersCoordinator(
+            NotificationLockscreenUserManager lockscreenUserManager) {
+        mLockscreenUserManager = lockscreenUserManager;
+    }
+
+    @Override
+    public void attach(NotifPipeline pipeline) {
+        pipeline.addPreGroupFilter(mFilter);
+        mLockscreenUserManager.addUserChangedListener(mUserChangedListener);
+    }
+
+    private final NotifFilter mFilter = new NotifFilter("NotCurrentUserFilter") {
+        @Override
+        public boolean shouldFilterOut(NotificationEntry entry, long now) {
+            return !mLockscreenUserManager
+                    .isCurrentProfile(entry.getSbn().getUser().getIdentifier());
+        }
+    };
+
+    private final UserChangedListener mUserChangedListener = new UserChangedListener() {
+        @Override
+        public void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles) {
+            mFilter.invalidateList();
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index aaf71f5..b773856 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -95,11 +95,6 @@
         public boolean shouldFilterOut(NotificationEntry entry, long now) {
             final StatusBarNotification sbn = entry.getSbn();
 
-            // FILTER OUT the notification when the notification isn't for the current profile
-            if (!mLockscreenUserManager.isCurrentProfile(sbn.getUserId())) {
-                return true;
-            }
-
             // FILTER OUT the notification when the keyguard is showing and...
             if (mKeyguardStateController.isShowing()) {
                 // ... user settings or the device policy manager doesn't allow lockscreen
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 98104f8..03c0ae6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -49,15 +49,17 @@
     public NotifCoordinators(
             DumpManager dumpManager,
             FeatureFlags featureFlags,
-            HeadsUpCoordinator headsUpCoordinator,
+            HideNotifsForOtherUsersCoordinator hideNotifsForOtherUsersCoordinator,
             KeyguardCoordinator keyguardCoordinator,
             RankingCoordinator rankingCoordinator,
             ForegroundCoordinator foregroundCoordinator,
             DeviceProvisionedCoordinator deviceProvisionedCoordinator,
             BubbleCoordinator bubbleCoordinator,
+            HeadsUpCoordinator headsUpCoordinator,
             PreparationCoordinator preparationCoordinator) {
         dumpManager.registerDumpable(TAG, this);
         mCoordinators.add(new HideLocallyDismissedNotifsCoordinator());
+        mCoordinators.add(hideNotifsForOtherUsersCoordinator);
         mCoordinators.add(keyguardCoordinator);
         mCoordinators.add(rankingCoordinator);
         mCoordinators.add(foregroundCoordinator);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
index 7e9e760..e9cbf32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -45,7 +45,8 @@
     public void attach(NotifPipeline pipeline) {
         mStatusBarStateController.addCallback(mStatusBarStateCallback);
 
-        pipeline.addPreGroupFilter(mNotifFilter);
+        pipeline.addPreGroupFilter(mSuspendedFilter);
+        pipeline.addPreGroupFilter(mDozingFilter);
     }
 
     /**
@@ -53,33 +54,30 @@
      * NotifListBuilder invalidates the notification list each time the ranking is updated,
      * so we don't need to explicitly invalidate this filter on ranking update.
      */
-    private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+    private final NotifFilter mSuspendedFilter = new NotifFilter("IsSuspendedFilter") {
         @Override
         public boolean shouldFilterOut(NotificationEntry entry, long now) {
-            // App suspended from Ranking
-            if (entry.getRanking().isSuspended()) {
-                return true;
-            }
+            return entry.getRanking().isSuspended();
+        }
+    };
 
+    private final NotifFilter mDozingFilter = new NotifFilter("IsDozingFilter") {
+        @Override
+        public boolean shouldFilterOut(NotificationEntry entry, long now) {
             // Dozing + DND Settings from Ranking object
             if (mStatusBarStateController.isDozing() && entry.shouldSuppressAmbient()) {
                 return true;
             }
 
-            if (!mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList()) {
-                return true;
-            }
-
-            return false;
+            return !mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList();
         }
     };
 
-
     private final StatusBarStateController.StateListener mStatusBarStateCallback =
             new StatusBarStateController.StateListener() {
                 @Override
                 public void onDozingChanged(boolean isDozing) {
-                    mNotifFilter.invalidateList();
+                    mDozingFilter.invalidateList();
                 }
             };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index 763547c..e946cf1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.log.dagger.NotificationLog
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
 import javax.inject.Inject
 
 class ShadeListBuilderLogger @Inject constructor(
@@ -126,13 +127,13 @@
 
     fun logFilterChanged(
         key: String,
-        prevFilter: String?,
-        newFilter: String?
+        prevFilter: NotifFilter?,
+        newFilter: NotifFilter?
     ) {
         buffer.log(TAG, INFO, {
             str1 = key
-            str2 = prevFilter
-            str3 = newFilter
+            str2 = prevFilter?.name
+            str3 = newFilter?.name
         }, {
             "Filter changed for $str1: $str2 -> $str3"
         })
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index be8af82..823b186 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -6478,7 +6478,7 @@
 
     private boolean hasActiveNotifications() {
         if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
-            return mNotifPipeline.getShadeList().isEmpty();
+            return !mNotifPipeline.getShadeList().isEmpty();
         } else {
             return mEntryManager.hasActiveNotifications();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index adca10f..8efda21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -112,8 +112,10 @@
     private final int mLongPressTimeout;
 
     private final PointF mDownPoint = new PointF();
+    private final PointF mEndPoint = new PointF();
     private boolean mThresholdCrossed = false;
     private boolean mAllowGesture = false;
+    private boolean mLogGesture = false;
     private boolean mInRejectedExclusion = false;
     private boolean mIsOnLeftEdge;
 
@@ -141,24 +143,16 @@
 
                     mOverviewProxyService.notifyBackAction(true, (int) mDownPoint.x,
                             (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
-                    int backtype = (mInRejectedExclusion
-                            ? SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED_REJECTED :
-                            SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED);
-                    SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, backtype,
-                            (int) mDownPoint.y, mIsOnLeftEdge
-                                    ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT :
-                                    SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT);
+                    logGesture(mInRejectedExclusion
+                            ? SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED_REJECTED
+                            : SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED);
                 }
 
                 @Override
                 public void cancelBack() {
+                    logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE);
                     mOverviewProxyService.notifyBackAction(false, (int) mDownPoint.x,
                             (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
-                    int backtype = SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE;
-                    SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, backtype,
-                            (int) mDownPoint.y, mIsOnLeftEdge
-                                    ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT :
-                                    SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT);
                 }
             };
 
@@ -331,39 +325,55 @@
     }
 
     private boolean isWithinTouchRegion(int x, int y) {
-        // Disallow if too far from the edge
-        if (x > mEdgeWidthLeft + mLeftInset
-                && x < (mDisplaySize.x - mEdgeWidthRight - mRightInset)) {
-            return false;
-        }
-
         // Disallow if we are in the bottom gesture area
         if (y >= (mDisplaySize.y - mBottomGestureHeight)) {
             return false;
         }
 
-        // Always allow if the user is in a transient sticky immersive state
-        if (mIsNavBarShownTransiently) {
-            return true;
+        // If the point is way too far (twice the margin), it is
+        // not interesting to us for logging purposes, nor we
+        // should process it.  Simply return false and keep
+        // mLogGesture = false.
+        if (x > 2 * (mEdgeWidthLeft + mLeftInset)
+                && x < (mDisplaySize.x - 2 * (mEdgeWidthRight + mRightInset))) {
+            return false;
         }
 
-        boolean isInExcludedRegion = mExcludeRegion.contains(x, y);
-        if (isInExcludedRegion) {
-            mOverviewProxyService.notifyBackAction(false /* completed */, -1, -1,
-                    false /* isButton */, !mIsOnLeftEdge);
-            SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED,
-                    SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_EXCLUDED, y,
-                    mIsOnLeftEdge ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT :
-                            SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT);
-        } else {
-            mInRejectedExclusion = mUnrestrictedExcludeRegion.contains(x, y);
+        // Denotes whether we should proceed with the gesture.
+        // Even if it is false, we may want to log it assuming
+        // it is not invalid due to exclusion.
+        boolean withinRange = x <= mEdgeWidthLeft + mLeftInset
+                || x >= (mDisplaySize.x - mEdgeWidthRight - mRightInset);
+
+        // Always allow if the user is in a transient sticky immersive state
+        if (mIsNavBarShownTransiently) {
+            mLogGesture = true;
+            return withinRange;
         }
-        return !isInExcludedRegion;
+
+        if (mExcludeRegion.contains(x, y)) {
+            if (withinRange) {
+                // Log as exclusion only if it is in acceptable range in the first place.
+                mOverviewProxyService.notifyBackAction(
+                        false /* completed */, -1, -1, false /* isButton */, !mIsOnLeftEdge);
+                // We don't have the end point for logging purposes.
+                mEndPoint.x = -1;
+                mEndPoint.y = -1;
+                mLogGesture = true;
+                logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_EXCLUDED);
+            }
+            return false;
+        }
+
+        mInRejectedExclusion = mUnrestrictedExcludeRegion.contains(x, y);
+        mLogGesture = true;
+        return withinRange;
     }
 
     private void cancelGesture(MotionEvent ev) {
         // Send action cancel to reset all the touch events
         mAllowGesture = false;
+        mLogGesture = false;
         mInRejectedExclusion = false;
         MotionEvent cancelEv = MotionEvent.obtain(ev);
         cancelEv.setAction(MotionEvent.ACTION_CANCEL);
@@ -371,51 +381,86 @@
         cancelEv.recycle();
     }
 
+    private void logGesture(int backType) {
+        if (!mLogGesture) {
+            return;
+        }
+        mLogGesture = false;
+        SysUiStatsLog.write(SysUiStatsLog.BACK_GESTURE_REPORTED_REPORTED, backType,
+                (int) mDownPoint.y, mIsOnLeftEdge
+                        ? SysUiStatsLog.BACK_GESTURE__X_LOCATION__LEFT
+                        : SysUiStatsLog.BACK_GESTURE__X_LOCATION__RIGHT,
+                (int) mDownPoint.x, (int) mDownPoint.y,
+                (int) mEndPoint.x, (int) mEndPoint.y,
+                mEdgeWidthLeft + mLeftInset,
+                mDisplaySize.x - (mEdgeWidthRight + mRightInset));
+    }
+
     private void onMotionEvent(MotionEvent ev) {
         int action = ev.getActionMasked();
         if (action == MotionEvent.ACTION_DOWN) {
             // Verify if this is in within the touch region and we aren't in immersive mode, and
             // either the bouncer is showing or the notification panel is hidden
             mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset;
+            mLogGesture = false;
             mInRejectedExclusion = false;
             mAllowGesture = !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
                     && isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
             if (mAllowGesture) {
                 mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
                 mEdgeBackPlugin.onMotionEvent(ev);
-
+            }
+            if (mLogGesture) {
                 mDownPoint.set(ev.getX(), ev.getY());
+                mEndPoint.set(-1, -1);
                 mThresholdCrossed = false;
             }
-
-        } else if (mAllowGesture) {
+        } else if (mAllowGesture || mLogGesture) {
             if (!mThresholdCrossed) {
+                mEndPoint.x = (int) ev.getX();
+                mEndPoint.y = (int) ev.getY();
                 if (action == MotionEvent.ACTION_POINTER_DOWN) {
-                    // We do not support multi touch for back gesture
-                    cancelGesture(ev);
+                    if (mAllowGesture) {
+                        logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_MULTI_TOUCH);
+                        // We do not support multi touch for back gesture
+                        cancelGesture(ev);
+                    }
+                    mLogGesture = false;
                     return;
                 } else if (action == MotionEvent.ACTION_MOVE) {
                     if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) {
-                        cancelGesture(ev);
+                        if (mAllowGesture) {
+                            logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_LONG_PRESS);
+                            cancelGesture(ev);
+                        }
+                        mLogGesture = false;
                         return;
                     }
                     float dx = Math.abs(ev.getX() - mDownPoint.x);
                     float dy = Math.abs(ev.getY() - mDownPoint.y);
                     if (dy > dx && dy > mTouchSlop) {
-                        cancelGesture(ev);
+                        if (mAllowGesture) {
+                            logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_VERTICAL_MOVE);
+                            cancelGesture(ev);
+                        }
+                        mLogGesture = false;
                         return;
-
                     } else if (dx > dy && dx > mTouchSlop) {
-                        mThresholdCrossed = true;
-                        // Capture inputs
-                        mInputMonitor.pilferPointers();
+                        if (mAllowGesture) {
+                            mThresholdCrossed = true;
+                            // Capture inputs
+                            mInputMonitor.pilferPointers();
+                        } else {
+                            logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_FAR_FROM_EDGE);
+                        }
                     }
                 }
-
             }
 
-            // forward touch
-            mEdgeBackPlugin.onMotionEvent(ev);
+            if (mAllowGesture) {
+                // forward touch
+                mEdgeBackPlugin.onMotionEvent(ev);
+            }
         }
 
         Dependency.get(ProtoTracer.class).update();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 3074e33..ceefff1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -203,6 +203,7 @@
             Log.wtf(TAG, "onFullyShown when view was null");
         } else {
             mKeyguardView.onResume();
+            mRoot.announceForAccessibility(mKeyguardView.getAccessibilityTitleForCurrentMode());
         }
     }
 
@@ -438,7 +439,6 @@
         mStatusBarHeight = mRoot.getResources().getDimensionPixelOffset(
                 com.android.systemui.R.dimen.status_bar_height);
         mRoot.setVisibility(View.INVISIBLE);
-        mRoot.setAccessibilityPaneTitle(mKeyguardView.getAccessibilityTitleForCurrentMode());
 
         final WindowInsets rootInsets = mRoot.getRootWindowInsets();
         if (rootInsets != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index cf9d43e..d70484e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
-
 import android.annotation.IntDef;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -30,241 +28,98 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.view.ViewTreeObserver;
-import android.view.accessibility.AccessibilityNodeInfo;
+import android.util.SparseArray;
+import android.view.ViewTreeObserver.OnPreDrawListener;
 
 import com.android.internal.graphics.ColorUtils;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.phone.ScrimController.ScrimVisibility;
-import com.android.systemui.statusbar.policy.AccessibilityController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-import javax.inject.Inject;
-import javax.inject.Named;
-
 /**
  * Manages the different states and animations of the unlock icon.
  */
-public class LockIcon extends KeyguardAffordanceView implements
-        ViewTreeObserver.OnPreDrawListener {
+public class LockIcon extends KeyguardAffordanceView {
 
-    private static final int STATE_LOCKED = 0;
-    private static final int STATE_LOCK_OPEN = 1;
-    private static final int STATE_SCANNING_FACE = 2;
-    private static final int STATE_BIOMETRICS_ERROR = 3;
-    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    private final AccessibilityController mAccessibilityController;
-    private final KeyguardStateController mKeyguardStateController;
-    private final KeyguardBypassController mBypassController;
-    private final NotificationWakeUpCoordinator mWakeUpCoordinator;
-    private final HeadsUpManagerPhone mHeadsUpManager;
-
-    private int mLastState = 0;
-    private boolean mForceUpdate;
-    private boolean mTransientBiometricsError;
-    private boolean mIsFaceUnlockState;
-    private boolean mSimLocked;
-    private int mDensity;
+    static final int STATE_LOCKED = 0;
+    static final int STATE_LOCK_OPEN = 1;
+    static final int STATE_SCANNING_FACE = 2;
+    static final int STATE_BIOMETRICS_ERROR = 3;
+    private float mDozeAmount;
+    private int mIconColor;
+    private StateProvider mStateProvider;
+    private int mOldState;
     private boolean mPulsing;
     private boolean mDozing;
-    private boolean mDocked;
-    private boolean mBlockUpdates;
-    private int mIconColor;
-    private float mDozeAmount;
-    private boolean mBouncerShowingScrimmed;
-    private boolean mWakeAndUnlockRunning;
-    private boolean mKeyguardShowing;
-    private boolean mShowingLaunchAffordance;
     private boolean mKeyguardJustShown;
-    private boolean mUpdatePending;
-    private boolean mBouncerPreHideAnimation;
-    private int mStatusBarState = StatusBarState.SHADE;
+    private final SparseArray<Drawable> mDrawableCache = new SparseArray<>();
 
-    private final KeyguardStateController.Callback mKeyguardMonitorCallback =
-            new KeyguardStateController.Callback() {
-                @Override
-                public void onKeyguardShowingChanged() {
-                    boolean force = false;
-                    boolean wasShowing = mKeyguardShowing;
-                    mKeyguardShowing = mKeyguardStateController.isShowing();
-                    if (!wasShowing && mKeyguardShowing && mBlockUpdates) {
-                        mBlockUpdates = false;
-                        force = true;
-                    }
-                    if (!wasShowing && mKeyguardShowing) {
-                        mKeyguardJustShown = true;
-                    }
-                    update(force);
-                }
+    private final OnPreDrawListener mOnPreDrawListener = new OnPreDrawListener() {
+        @Override
+        public boolean onPreDraw() {
+            getViewTreeObserver().removeOnPreDrawListener(this);
 
-                @Override
-                public void onKeyguardFadingAwayChanged() {
-                    if (!mKeyguardStateController.isKeyguardFadingAway()) {
-                        mBouncerPreHideAnimation = false;
-                        if (mBlockUpdates) {
-                            mBlockUpdates = false;
-                            update(true /* force */);
-                        }
-                    }
-                }
+            int newState = mStateProvider.getState();
+            Drawable icon = getIcon(newState);
+            setImageDrawable(icon, false);
 
-                @Override
-                public void onUnlockedChanged() {
-                    update();
-                }
-            };
+            if (newState == STATE_SCANNING_FACE) {
+                announceForAccessibility(getResources().getString(
+                        R.string.accessibility_scanning_face));
+            }
 
-    @Inject
-    public LockIcon(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
-            AccessibilityController accessibilityController,
-            KeyguardBypassController bypassController,
-            NotificationWakeUpCoordinator wakeUpCoordinator,
-            KeyguardStateController keyguardStateController,
-            HeadsUpManagerPhone headsUpManager) {
+            if (icon instanceof AnimatedVectorDrawable) {
+                final AnimatedVectorDrawable animation = (AnimatedVectorDrawable) icon;
+                animation.forceAnimationOnUI();
+                animation.clearAnimationCallbacks();
+                animation.registerAnimationCallback(
+                        new Animatable2.AnimationCallback() {
+                            @Override
+                            public void onAnimationEnd(Drawable drawable) {
+                                if (getDrawable() == animation
+                                        && newState == mStateProvider.getState()
+                                        && newState == STATE_SCANNING_FACE) {
+                                    animation.start();
+                                } else {
+                                    Trace.endAsyncSection("LockIcon#Animation", newState);
+                                }
+                            }
+                        });
+                Trace.beginAsyncSection("LockIcon#Animation", newState);
+                animation.start();
+            }
+
+            return true;
+        }
+    };
+
+    public LockIcon(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mContext = context;
-        mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
-        mAccessibilityController = accessibilityController;
-        mBypassController = bypassController;
-        mWakeUpCoordinator = wakeUpCoordinator;
-        mKeyguardStateController = keyguardStateController;
-        mHeadsUpManager = headsUpManager;
     }
 
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mKeyguardStateController.addCallback(mKeyguardMonitorCallback);
-        mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
-        update();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mKeyguardStateController.removeCallback(mKeyguardMonitorCallback);
-    }
-
-    /**
-     * If we're currently presenting an authentication error message.
-     */
-    public void setTransientBiometricsError(boolean transientBiometricsError) {
-        mTransientBiometricsError = transientBiometricsError;
-        update();
+    void setStateProvider(StateProvider stateProvider) {
+        mStateProvider = stateProvider;
     }
 
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        final int density = newConfig.densityDpi;
-        if (density != mDensity) {
-            mDensity = density;
-            update();
-        }
-    }
-
-    public void update() {
-        update(false /* force */);
-    }
-
-    public void update(boolean force) {
-        if (force) {
-            mForceUpdate = true;
-        }
-        if (!mUpdatePending) {
-            mUpdatePending = true;
-            getViewTreeObserver().addOnPreDrawListener(this);
-        }
-    }
-
-    @Override
-    public boolean onPreDraw() {
-        mUpdatePending = false;
-        getViewTreeObserver().removeOnPreDrawListener(this);
-
-        int state = getState();
-        int lastState = mLastState;
-        boolean keyguardJustShown = mKeyguardJustShown;
-        mIsFaceUnlockState = state == STATE_SCANNING_FACE;
-        mLastState = state;
-        mKeyguardJustShown = false;
-
-        boolean shouldUpdate = lastState != state || mForceUpdate;
-        if (mBlockUpdates && canBlockUpdates()) {
-            shouldUpdate = false;
-        }
-        if (shouldUpdate) {
-            mForceUpdate = false;
-            @LockAnimIndex final int lockAnimIndex = getAnimationIndexForTransition(lastState,
-                    state, mPulsing, mDozing, keyguardJustShown);
-            boolean isAnim = lockAnimIndex != -1;
-            int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(state);
-
-            Drawable icon = mContext.getDrawable(iconRes);
-            final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
-                    ? (AnimatedVectorDrawable) icon
-                    : null;
-            setImageDrawable(icon, false);
-            if (mIsFaceUnlockState) {
-                announceForAccessibility(getContext().getString(
-                        R.string.accessibility_scanning_face));
-            }
-
-            if (animation != null && isAnim) {
-                animation.forceAnimationOnUI();
-                animation.clearAnimationCallbacks();
-                animation.registerAnimationCallback(new Animatable2.AnimationCallback() {
-                    @Override
-                    public void onAnimationEnd(Drawable drawable) {
-                        if (getDrawable() == animation && state == getState()
-                                && doesAnimationLoop(lockAnimIndex)) {
-                            animation.start();
-                        } else {
-                            Trace.endAsyncSection("LockIcon#Animation", state);
-                        }
-                    }
-                });
-                Trace.beginAsyncSection("LockIcon#Animation", state);
-                animation.start();
-            }
-        }
-        updateDarkTint();
-
-        updateIconVisibility();
-        updateClickability();
-
-        return true;
+        mDrawableCache.clear();
     }
 
     /**
      * Update the icon visibility
      * @return true if the visibility changed
      */
-    boolean updateIconVisibility() {
-        boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked);
-        boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning
-                || mShowingLaunchAffordance;
-        if (mBypassController.getBypassEnabled() && !mBouncerShowingScrimmed) {
-            if ((mHeadsUpManager.isHeadsUpGoingAway() || mHeadsUpManager.hasPinnedHeadsUp()
-                    || mStatusBarState == StatusBarState.KEYGUARD)
-                    && !mWakeUpCoordinator.getNotificationsFullyHidden()) {
-                invisible = true;
-            }
-        }
-        boolean wasInvisible = getVisibility() == INVISIBLE;
-        if (invisible != wasInvisible) {
-            setVisibility(invisible ? INVISIBLE : VISIBLE);
+    boolean updateIconVisibility(boolean visible) {
+        boolean wasVisible = getVisibility() == VISIBLE;
+        if (visible != wasVisible) {
+            setVisibility(visible ? VISIBLE : INVISIBLE);
             animate().cancel();
-            if (!invisible) {
+            if (visible) {
                 setScaleX(0);
                 setScaleY(0);
                 animate()
@@ -280,49 +135,47 @@
         return false;
     }
 
-    private boolean canBlockUpdates() {
-        return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway();
+    void update(int oldState, boolean pulsing, boolean dozing, boolean keyguardJustShown) {
+        mOldState = oldState;
+        mPulsing = pulsing;
+        mDozing = dozing;
+        mKeyguardJustShown = keyguardJustShown;
+
+        getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener);
     }
 
-    private void updateClickability() {
-        if (mAccessibilityController == null) {
-            return;
+    void setDozeAmount(float dozeAmount) {
+        mDozeAmount = dozeAmount;
+        updateDarkTint();
+    }
+
+    void onThemeChange(int iconColor) {
+        mDrawableCache.clear();
+        mIconColor = iconColor;
+        updateDarkTint();
+    }
+
+    private void updateDarkTint() {
+        int color = ColorUtils.blendARGB(mIconColor, Color.WHITE, mDozeAmount);
+        setImageTintList(ColorStateList.valueOf(color));
+    }
+
+    private Drawable getIcon(int newState) {
+        @LockAnimIndex final int lockAnimIndex =
+                getAnimationIndexForTransition(mOldState, newState, mPulsing, mDozing,
+                        mKeyguardJustShown);
+
+        boolean isAnim = lockAnimIndex != -1;
+        int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(newState);
+
+        if (!mDrawableCache.contains(iconRes)) {
+            mDrawableCache.put(iconRes, getResources().getDrawable(iconRes));
         }
-        boolean canLock = mKeyguardStateController.isMethodSecure()
-                && mKeyguardStateController.canDismissLockScreen();
-        boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled();
-        setClickable(clickToUnlock);
-        setLongClickable(canLock && !clickToUnlock);
-        setFocusable(mAccessibilityController.isAccessibilityEnabled());
+
+        return mDrawableCache.get(iconRes);
     }
 
-    @Override
-    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfo(info);
-        boolean fingerprintRunning = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
-        // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
-        // as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to
-        // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
-        // check of whether non-strong biometric is allowed
-        boolean unlockingAllowed = mKeyguardUpdateMonitor
-                        .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */);
-        if (fingerprintRunning && unlockingAllowed) {
-            AccessibilityNodeInfo.AccessibilityAction unlock
-                    = new AccessibilityNodeInfo.AccessibilityAction(
-                    AccessibilityNodeInfo.ACTION_CLICK,
-                    getContext().getString(R.string.accessibility_unlock_without_fingerprint));
-            info.addAction(unlock);
-            info.setHintText(getContext().getString(
-                    R.string.accessibility_waiting_for_fingerprint));
-        } else if (mIsFaceUnlockState) {
-            //Avoid 'button' to be spoken for scanning face
-            info.setClassName(LockIcon.class.getName());
-            info.setContentDescription(getContext().getString(
-                R.string.accessibility_scanning_face));
-        }
-    }
-
-    private int getIconForState(int state) {
+    static int getIconForState(int state) {
         int iconRes;
         switch (state) {
             case STATE_LOCKED:
@@ -343,11 +196,7 @@
         return iconRes;
     }
 
-    private boolean doesAnimationLoop(@LockAnimIndex int lockAnimIndex) {
-        return lockAnimIndex == SCANNING;
-    }
-
-    private static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing,
+    static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing,
             boolean dozing, boolean keyguardJustShown) {
 
         // Never animate when screen is off
@@ -367,42 +216,10 @@
         return -1;
     }
 
-    public void setBouncerShowingScrimmed(boolean bouncerShowing) {
-        mBouncerShowingScrimmed = bouncerShowing;
-        if (mBypassController.getBypassEnabled()) {
-            update();
-        }
-    }
-
-    /**
-     * Animate padlock opening when bouncer challenge is solved.
-     */
-    public void onBouncerPreHideAnimation() {
-        mBouncerPreHideAnimation = true;
-        update();
-    }
-
-    void setIconColor(int iconColor) {
-        mIconColor = iconColor;
-        updateDarkTint();
-    }
-
-    void setSimLocked(boolean simLocked) {
-        mSimLocked = simLocked;
-    }
-
-    /** Set if the device is docked. */
-    public void setDocked(boolean docked) {
-        if (mDocked != docked) {
-            mDocked = docked;
-            update();
-        }
-    }
-
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({ERROR, UNLOCK, LOCK, SCANNING})
     @interface LockAnimIndex {}
-    private static final int ERROR = 0, UNLOCK = 1, LOCK = 2, SCANNING = 3;
+    static final int ERROR = 0, UNLOCK = 1, LOCK = 2, SCANNING = 3;
     private static final int[][] LOCK_ANIM_RES_IDS = new int[][] {
             {
                     R.anim.lock_to_error,
@@ -433,7 +250,7 @@
     private int getThemedAnimationResId(@LockAnimIndex int lockAnimIndex) {
         final String setting = TextUtils.emptyIfNull(
                 Settings.Secure.getString(getContext().getContentResolver(),
-                Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES));
+                        Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES));
         if (setting.contains("com.android.theme.icon_pack.circular.android")) {
             return LOCK_ANIM_RES_IDS[1][lockAnimIndex];
         } else if (setting.contains("com.android.theme.icon_pack.filled.android")) {
@@ -444,83 +261,8 @@
         return LOCK_ANIM_RES_IDS[0][lockAnimIndex];
     }
 
-    private int getState() {
-        KeyguardUpdateMonitor updateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
-        if ((mKeyguardStateController.canDismissLockScreen() || !mKeyguardShowing
-                || mKeyguardStateController.isKeyguardGoingAway()) && !mSimLocked) {
-            return STATE_LOCK_OPEN;
-        } else if (mTransientBiometricsError) {
-            return STATE_BIOMETRICS_ERROR;
-        } else if (updateMonitor.isFaceDetectionRunning() && !mPulsing) {
-            return STATE_SCANNING_FACE;
-        } else {
-            return STATE_LOCKED;
-        }
+    interface StateProvider {
+        int getState();
     }
 
-    /**
-     * When keyguard is in pulsing (AOD2) state.
-     * @param pulsing {@code true} when pulsing.
-     */
-    public void setPulsing(boolean pulsing) {
-        mPulsing = pulsing;
-        update();
-    }
-
-    private void updateDarkTint() {
-        int color = ColorUtils.blendARGB(mIconColor, Color.WHITE, mDozeAmount);
-        setImageTintList(ColorStateList.valueOf(color));
-    }
-
-    /**
-     * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the
-     * icon on top of the black front scrim.
-     * @param wakeAndUnlock are we wake and unlocking
-     * @param isUnlock are we currently unlocking
-     */
-    public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) {
-        if (wakeAndUnlock) {
-            mWakeAndUnlockRunning = true;
-        }
-        if (isUnlock && mBypassController.getBypassEnabled() && canBlockUpdates()) {
-            // We don't want the icon to change while we are unlocking
-            mBlockUpdates = true;
-        }
-        update();
-    }
-
-    /**
-     * When we're launching an affordance, like double pressing power to open camera.
-     */
-    public void onShowingLaunchAffordanceChanged(boolean showing) {
-        mShowingLaunchAffordance = showing;
-        update();
-    }
-
-    /**
-     * Called whenever the scrims become opaque, transparent or semi-transparent.
-     */
-    public void onScrimVisibilityChanged(@ScrimVisibility int scrimsVisible) {
-        if (mWakeAndUnlockRunning
-                && scrimsVisible == ScrimController.TRANSPARENT) {
-            mWakeAndUnlockRunning = false;
-            update();
-        }
-    }
-
-    void setDozing(boolean dozing) {
-        mDozing = dozing;
-        update();
-    }
-
-    void setDozeAmount(float dozeAmount) {
-        mDozeAmount = dozeAmount;
-        updateDarkTint();
-    }
-
-    /** Set the StatusBarState. */
-    public void setStatusBarState(int statusBarState) {
-        mStatusBarState = statusBarState;
-        updateIconVisibility();
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index 2b1a8a4..f7c861b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -16,11 +16,19 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.statusbar.phone.LockIcon.STATE_BIOMETRICS_ERROR;
+import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCKED;
+import static com.android.systemui.statusbar.phone.LockIcon.STATE_LOCK_OPEN;
+import static com.android.systemui.statusbar.phone.LockIcon.STATE_SCANNING_FACE;
+
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.hardware.biometrics.BiometricSourceType;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import androidx.annotation.Nullable;
 
@@ -29,15 +37,18 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator.WakeUpListener;
 import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import java.util.Optional;
 
@@ -59,6 +70,21 @@
     private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
     private final KeyguardBypassController mKeyguardBypassController;
     private final Optional<DockManager> mDockManager;
+    private final KeyguardStateController mKeyguardStateController;
+    private final Resources mResources;
+    private final HeadsUpManagerPhone mHeadsUpManagerPhone;
+    private boolean mKeyguardShowing;
+    private boolean mKeyguardJustShown;
+    private boolean mBlockUpdates;
+    private boolean mPulsing;
+    private boolean mDozing;
+    private boolean mSimLocked;
+    private boolean mTransientBiometricsError;
+    private boolean mDocked;
+    private boolean mWakeAndUnlockRunning;
+    private boolean mShowingLaunchAffordance;
+    private boolean mBouncerShowingScrimmed;
+    private int mStatusBarState = StatusBarState.SHADE;
     private LockIcon mLockIcon;
 
     private View.OnAttachStateChangeListener mOnAttachStateChangeListener =
@@ -69,10 +95,13 @@
             mConfigurationController.addCallback(mConfigurationListener);
             mNotificationWakeUpCoordinator.addListener(mWakeUpListener);
             mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback);
+            mKeyguardStateController.addCallback(mKeyguardMonitorCallback);
 
             mDockManager.ifPresent(dockManager -> dockManager.addListener(mDockEventListener));
 
+            mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
             mConfigurationListener.onThemeChanged();
+            update();
         }
 
         @Override
@@ -81,7 +110,7 @@
             mConfigurationController.removeCallback(mConfigurationListener);
             mNotificationWakeUpCoordinator.removeListener(mWakeUpListener);
             mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
-
+            mKeyguardStateController.removeCallback(mKeyguardMonitorCallback);
 
             mDockManager.ifPresent(dockManager -> dockManager.removeListener(mDockEventListener));
         }
@@ -91,32 +120,44 @@
             new StatusBarStateController.StateListener() {
                 @Override
                 public void onDozingChanged(boolean isDozing) {
-                    mLockIcon.setDozing(isDozing);
+                    setDozing(isDozing);
                 }
 
                 @Override
                 public void onDozeAmountChanged(float linear, float eased) {
-                    mLockIcon.setDozeAmount(eased);
+                    if (mLockIcon != null) {
+                        mLockIcon.setDozeAmount(eased);
+                    }
                 }
 
                 @Override
                 public void onStateChanged(int newState) {
-                    mLockIcon.setStatusBarState(newState);
+                    setStatusBarState(newState);
                 }
             };
 
     private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
+        private int mDensity;
+
         @Override
         public void onThemeChanged() {
+            if (mLockIcon == null) {
+                return;
+            }
+
             TypedArray typedArray = mLockIcon.getContext().getTheme().obtainStyledAttributes(
                     null, new int[]{ R.attr.wallpaperTextColor }, 0, 0);
             int iconColor = typedArray.getColor(0, Color.WHITE);
             typedArray.recycle();
-            mLockIcon.setIconColor(iconColor);
+            mLockIcon.onThemeChange(iconColor);
         }
 
         @Override
         public void onDensityOrFontScaleChanged() {
+            if (mLockIcon == null) {
+                return;
+            }
+
             ViewGroup.LayoutParams lp = mLockIcon.getLayoutParams();
             if (lp == null) {
                 return;
@@ -125,24 +166,41 @@
             lp.height = mLockIcon.getResources().getDimensionPixelSize(
                     R.dimen.keyguard_lock_height);
             mLockIcon.setLayoutParams(lp);
-            mLockIcon.update(true /* force */);
+            update(true /* force */);
         }
 
         @Override
         public void onLocaleListChanged() {
+            if (mLockIcon == null) {
+                return;
+            }
+
             mLockIcon.setContentDescription(
                     mLockIcon.getResources().getText(R.string.accessibility_unlock_button));
-            mLockIcon.update(true /* force */);
+            update(true /* force */);
+        }
+
+        @Override
+        public void onConfigChanged(Configuration newConfig) {
+            final int density = newConfig.densityDpi;
+            if (density != mDensity) {
+                mDensity = density;
+                update();
+            }
         }
     };
 
     private final WakeUpListener mWakeUpListener = new WakeUpListener() {
         @Override
+        public void onPulseExpansionChanged(boolean expandingChanged) {
+        }
+
+        @Override
         public void onFullyHiddenChanged(boolean isFullyHidden) {
             if (mKeyguardBypassController.getBypassEnabled()) {
-                boolean changed = mLockIcon.updateIconVisibility();
+                boolean changed = updateIconVisibility();
                 if (changed) {
-                    mLockIcon.update();
+                    update();
                 }
             }
         }
@@ -152,30 +210,103 @@
             new KeyguardUpdateMonitorCallback() {
                 @Override
                 public void onSimStateChanged(int subId, int slotId, int simState) {
-                    mLockIcon.setSimLocked(mKeyguardUpdateMonitor.isSimPinSecure());
-                    mLockIcon.update();
+                    mSimLocked = mKeyguardUpdateMonitor.isSimPinSecure();
+                    update();
                 }
 
                 @Override
                 public void onKeyguardVisibilityChanged(boolean showing) {
-                    mLockIcon.update();
+                    update();
                 }
 
                 @Override
                 public void onBiometricRunningStateChanged(boolean running,
                         BiometricSourceType biometricSourceType) {
-                    mLockIcon.update();
+                    update();
                 }
 
                 @Override
                 public void onStrongAuthStateChanged(int userId) {
-                    mLockIcon.update();
+                    update();
                 }
             };
 
     private final DockManager.DockEventListener mDockEventListener =
-            event -> mLockIcon.setDocked(event == DockManager.STATE_DOCKED
-                    || event == DockManager.STATE_DOCKED_HIDE);
+            event -> {
+                boolean docked =
+                        event == DockManager.STATE_DOCKED || event == DockManager.STATE_DOCKED_HIDE;
+                if (docked != mDocked) {
+                    mDocked = docked;
+                    update();
+                }
+            };
+
+    private final KeyguardStateController.Callback mKeyguardMonitorCallback =
+            new KeyguardStateController.Callback() {
+                @Override
+                public void onKeyguardShowingChanged() {
+                    boolean force = false;
+                    boolean wasShowing = mKeyguardShowing;
+                    mKeyguardShowing = mKeyguardStateController.isShowing();
+                    if (!wasShowing && mKeyguardShowing && mBlockUpdates) {
+                        mBlockUpdates = false;
+                        force = true;
+                    }
+                    if (!wasShowing && mKeyguardShowing) {
+                        mKeyguardJustShown = true;
+                    }
+                    update(force);
+                }
+
+                @Override
+                public void onKeyguardFadingAwayChanged() {
+                    if (!mKeyguardStateController.isKeyguardFadingAway()) {
+                        if (mBlockUpdates) {
+                            mBlockUpdates = false;
+                            update(true /* force */);
+                        }
+                    }
+                }
+
+                @Override
+                public void onUnlockedChanged() {
+                    update();
+                }
+            };
+
+    private final View.AccessibilityDelegate mAccessibilityDelegate =
+            new View.AccessibilityDelegate() {
+                @Override
+                public void onInitializeAccessibilityNodeInfo(View host,
+                        AccessibilityNodeInfo info) {
+                    super.onInitializeAccessibilityNodeInfo(host, info);
+                    boolean fingerprintRunning =
+                            mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
+                    // Only checking if unlocking with Biometric is allowed (no matter strong or
+                    // non-strong as long as primary auth, i.e. PIN/pattern/password, is not
+                    // required), so it's ok to pass true for isStrongBiometric to
+                    // isUnlockingWithBiometricAllowed() to bypass the check of whether non-strong
+                    // biometric is allowed
+                    boolean unlockingAllowed = mKeyguardUpdateMonitor
+                            .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */);
+                    if (fingerprintRunning && unlockingAllowed) {
+                        AccessibilityNodeInfo.AccessibilityAction unlock =
+                                new AccessibilityNodeInfo.AccessibilityAction(
+                                AccessibilityNodeInfo.ACTION_CLICK,
+                                mResources.getString(
+                                        R.string.accessibility_unlock_without_fingerprint));
+                        info.addAction(unlock);
+                        info.setHintText(mResources.getString(
+                                R.string.accessibility_waiting_for_fingerprint));
+                    } else if (getState() == STATE_SCANNING_FACE) {
+                        //Avoid 'button' to be spoken for scanning face
+                        info.setClassName(LockIcon.class.getName());
+                        info.setContentDescription(mResources.getString(
+                                R.string.accessibility_scanning_face));
+                    }
+                }
+            };
+    private int mLastState;
 
     @Inject
     public LockscreenLockIconController(LockscreenGestureLogger lockscreenGestureLogger,
@@ -188,7 +319,10 @@
             ConfigurationController configurationController,
             NotificationWakeUpCoordinator notificationWakeUpCoordinator,
             KeyguardBypassController keyguardBypassController,
-            @Nullable DockManager dockManager) {
+            @Nullable DockManager dockManager,
+            KeyguardStateController keyguardStateController,
+            @Main Resources resources,
+            HeadsUpManagerPhone headsUpManagerPhone) {
         mLockscreenGestureLogger = lockscreenGestureLogger;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mLockPatternUtils = lockPatternUtils;
@@ -200,24 +334,31 @@
         mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
         mKeyguardBypassController = keyguardBypassController;
         mDockManager = dockManager == null ? Optional.empty() : Optional.of(dockManager);
+        mKeyguardStateController = keyguardStateController;
+        mResources = resources;
+        mHeadsUpManagerPhone = headsUpManagerPhone;
 
         mKeyguardIndicationController.setLockIconController(this);
     }
 
     /**
      * Associate the controller with a {@link LockIcon}
+     *
+     * TODO: change to an init method and inject the view.
      */
     public void attach(LockIcon lockIcon) {
         mLockIcon = lockIcon;
 
         mLockIcon.setOnClickListener(this::handleClick);
         mLockIcon.setOnLongClickListener(this::handleLongClick);
+        mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate);
+        mLockIcon.setStateProvider(this::getState);
 
         if (mLockIcon.isAttachedToWindow()) {
             mOnAttachStateChangeListener.onViewAttachedToWindow(mLockIcon);
         }
         mLockIcon.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
-        mLockIcon.setStatusBarState(mStatusBarStateController.getState());
+        setStatusBarState(mStatusBarStateController.getState());
     }
 
     public LockIcon getView() {
@@ -228,8 +369,10 @@
      * Called whenever the scrims become opaque, transparent or semi-transparent.
      */
     public void onScrimVisibilityChanged(Integer scrimsVisible) {
-        if (mLockIcon != null) {
-            mLockIcon.onScrimVisibilityChanged(scrimsVisible);
+        if (mWakeAndUnlockRunning
+                && scrimsVisible == ScrimController.TRANSPARENT) {
+            mWakeAndUnlockRunning = false;
+            update();
         }
     }
 
@@ -237,55 +380,56 @@
      * Propagate {@link StatusBar} pulsing state.
      */
     public void setPulsing(boolean pulsing) {
-        if (mLockIcon != null) {
-            mLockIcon.setPulsing(pulsing);
-        }
+        mPulsing = pulsing;
+        update();
     }
 
     /**
-     * Called when the biometric authentication mode changes.
-     *
-     * @param wakeAndUnlock If the type is {@link BiometricUnlockController#isWakeAndUnlock()}
-     * @param isUnlock      If the type is {@link BiometricUnlockController#isBiometricUnlock()} ()
+     * We need to hide the lock whenever there's a fingerprint unlock, otherwise you'll see the
+     * icon on top of the black front scrim.
+     * @param wakeAndUnlock are we wake and unlocking
+     * @param isUnlock are we currently unlocking
      */
     public void onBiometricAuthModeChanged(boolean wakeAndUnlock, boolean isUnlock) {
-        if (mLockIcon != null) {
-            mLockIcon.onBiometricAuthModeChanged(wakeAndUnlock, isUnlock);
+        if (wakeAndUnlock) {
+            mWakeAndUnlockRunning = true;
         }
+        if (isUnlock && mKeyguardBypassController.getBypassEnabled() && canBlockUpdates()) {
+            // We don't want the icon to change while we are unlocking
+            mBlockUpdates = true;
+        }
+        update();
     }
 
     /**
      * When we're launching an affordance, like double pressing power to open camera.
      */
     public void onShowingLaunchAffordanceChanged(Boolean showing) {
-        if (mLockIcon != null) {
-            mLockIcon.onShowingLaunchAffordanceChanged(showing);
-        }
+        mShowingLaunchAffordance = showing;
+        update();
     }
 
     /** Sets whether the bouncer is showing. */
     public void setBouncerShowingScrimmed(boolean bouncerShowing) {
-        if (mLockIcon != null) {
-            mLockIcon.setBouncerShowingScrimmed(bouncerShowing);
+        mBouncerShowingScrimmed = bouncerShowing;
+        if (mKeyguardBypassController.getBypassEnabled()) {
+            update();
         }
     }
 
     /**
-     * When {@link KeyguardBouncer} starts to be dismissed and starts to play its animation.
+     * Animate padlock opening when bouncer challenge is solved.
      */
     public void onBouncerPreHideAnimation() {
-        if (mLockIcon != null) {
-            mLockIcon.onBouncerPreHideAnimation();
-        }
+        update();
     }
 
     /**
      * If we're currently presenting an authentication error message.
      */
     public void setTransientBiometricsError(boolean transientBiometricsError) {
-        if (mLockIcon != null) {
-            mLockIcon.setTransientBiometricsError(transientBiometricsError);
-        }
+        mTransientBiometricsError = transientBiometricsError;
+        update();
     }
 
     private boolean handleLongClick(View view) {
@@ -306,4 +450,90 @@
         }
         mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
     }
+
+    private void update() {
+        update(false /* force */);
+    }
+
+    private void update(boolean force) {
+        int state = getState();
+        boolean shouldUpdate = mLastState != state || force;
+        if (mBlockUpdates && canBlockUpdates()) {
+            shouldUpdate = false;
+        }
+        if (shouldUpdate && mLockIcon != null) {
+            mLockIcon.update(mLastState, mPulsing, mDozing, mKeyguardJustShown);
+        }
+        mLastState = state;
+        mKeyguardJustShown = false;
+        updateIconVisibility();
+        updateClickability();
+    }
+
+    private int getState() {
+        if ((mKeyguardStateController.canDismissLockScreen() || !mKeyguardShowing
+                || mKeyguardStateController.isKeyguardGoingAway()) && !mSimLocked) {
+            return STATE_LOCK_OPEN;
+        } else if (mTransientBiometricsError) {
+            return STATE_BIOMETRICS_ERROR;
+        } else if (mKeyguardUpdateMonitor.isFaceDetectionRunning() && !mPulsing) {
+            return STATE_SCANNING_FACE;
+        } else {
+            return STATE_LOCKED;
+        }
+    }
+
+    private boolean canBlockUpdates() {
+        return mKeyguardShowing || mKeyguardStateController.isKeyguardFadingAway();
+    }
+
+    private void setDozing(boolean isDozing) {
+        mDozing = isDozing;
+        update();
+    }
+
+    /** Set the StatusBarState. */
+    private void setStatusBarState(int statusBarState) {
+        mStatusBarState = statusBarState;
+        updateIconVisibility();
+    }
+
+    /**
+     * Update the icon visibility
+     * @return true if the visibility changed
+     */
+    private boolean updateIconVisibility() {
+        boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked);
+        boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning
+                || mShowingLaunchAffordance;
+        if (mKeyguardBypassController.getBypassEnabled() && !mBouncerShowingScrimmed) {
+            if ((mHeadsUpManagerPhone.isHeadsUpGoingAway()
+                    || mHeadsUpManagerPhone.hasPinnedHeadsUp()
+                    || mStatusBarState == StatusBarState.KEYGUARD)
+                    && !mNotificationWakeUpCoordinator.getNotificationsFullyHidden()) {
+                invisible = true;
+            }
+        }
+
+        if (mLockIcon == null) {
+            return false;
+        }
+
+        return mLockIcon.updateIconVisibility(!invisible);
+    }
+
+    private void updateClickability() {
+        if (mAccessibilityController == null) {
+            return;
+        }
+        boolean canLock = mKeyguardStateController.isMethodSecure()
+                && mKeyguardStateController.canDismissLockScreen();
+        boolean clickToUnlock = mAccessibilityController.isAccessibilityEnabled();
+        if (mLockIcon != null) {
+            mLockIcon.setClickable(clickToUnlock);
+            mLockIcon.setLongClickable(canLock && !clickToUnlock);
+            mLockIcon.setFocusable(mAccessibilityController.isAccessibilityEnabled());
+        }
+    }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 1c1e7c4..977a307 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -66,7 +66,7 @@
         }
     };
     private DarkReceiver mBattery;
-    private int mRotationOrientation;
+    private int mRotationOrientation = -1;
     @Nullable
     private View mCenterIconSpace;
     @Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 760a6d6..c6f7983 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -99,11 +99,12 @@
         }
 
         // padding needed for corner cutout.
-        int leftCornerCutoutPadding = 0;
-        int rightCornerCutoutPadding = 0;
+        int leftCornerCutoutPadding = cutout.getSafeInsetLeft();
+        int rightCornerCutoutPadding = cutout.getSafeInsetRight();
         if (cornerCutoutPadding != null) {
-            leftCornerCutoutPadding = cornerCutoutPadding.first;
-            rightCornerCutoutPadding = cornerCutoutPadding.second;
+            leftCornerCutoutPadding = Math.max(leftCornerCutoutPadding, cornerCutoutPadding.first);
+            rightCornerCutoutPadding = Math.max(rightCornerCutoutPadding,
+                    cornerCutoutPadding.second);
         }
 
         return new Pair<>(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 496bf68..bf5900f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -67,9 +67,9 @@
     private final Handler mBgHandler;
     protected final Context mContext;
 
-    private int mLevel;
-    private boolean mPluggedIn;
-    private boolean mCharging;
+    protected int mLevel;
+    protected boolean mPluggedIn;
+    protected boolean mCharging;
     private boolean mCharged;
     private boolean mPowerSave;
     private boolean mAodPowerSave;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 7c96386..c2fc18f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -37,10 +37,10 @@
 
 public class WifiSignalController extends
         SignalController<WifiSignalController.WifiState, SignalController.IconGroup> {
-    private final boolean mHasMobileData;
+    private final boolean mHasMobileDataFeature;
     private final WifiStatusTracker mWifiTracker;
 
-    public WifiSignalController(Context context, boolean hasMobileData,
+    public WifiSignalController(Context context, boolean hasMobileDataFeature,
             CallbackHandler callbackHandler, NetworkControllerImpl networkController,
             WifiManager wifiManager) {
         super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI,
@@ -52,7 +52,7 @@
         mWifiTracker = new WifiStatusTracker(mContext, wifiManager, networkScoreManager,
                 connectivityManager, this::handleStatusUpdated);
         mWifiTracker.setListening(true);
-        mHasMobileData = hasMobileData;
+        mHasMobileDataFeature = hasMobileDataFeature;
         if (wifiManager != null) {
             wifiManager.registerTrafficStateCallback(context.getMainExecutor(),
                     new WifiTrafficStateCallback());
@@ -85,9 +85,10 @@
         // only show wifi in the cluster if connected or if wifi-only
         boolean visibleWhenEnabled = mContext.getResources().getBoolean(
                 R.bool.config_showWifiIndicatorWhenEnabled);
-        boolean wifiVisible = mCurrentState.enabled
-                && ((mCurrentState.connected && mCurrentState.inetCondition == 1)
-                    || !mHasMobileData || visibleWhenEnabled);
+        boolean wifiVisible = mCurrentState.enabled && (
+                (mCurrentState.connected && mCurrentState.inetCondition == 1)
+                        || !mHasMobileDataFeature || mWifiTracker.isDefaultNetwork
+                        || visibleWhenEnabled);
         String wifiDesc = mCurrentState.connected ? mCurrentState.ssid : null;
         boolean ssidPresent = wifiVisible && mCurrentState.ssid != null;
         String contentDescription = getTextIfExists(getContentDescription()).toString();
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 11885c5..442c7ea 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -187,8 +187,9 @@
     }
 
     private void updateMissingPrivateVolumes() {
-        if (isTv()) {
+        if (isTv() || isAutomotive()) {
             // On TV, TvSettings displays a modal full-screen activity in this case.
+            // Not applicable for automotive.
             return;
         }
 
@@ -595,6 +596,9 @@
         if (isTv()) {
             intent.setPackage("com.android.tv.settings");
             intent.setAction("com.android.tv.settings.action.NEW_STORAGE");
+        } else if (isAutomotive()) {
+            // TODO(b/151671685): add intent to handle unsupported usb
+            return null;
         } else {
             intent.setClassName("com.android.settings",
                     "com.android.settings.deviceinfo.StorageWizardInit");
@@ -611,6 +615,9 @@
         if (isTv()) {
             intent.setPackage("com.android.tv.settings");
             intent.setAction("com.android.tv.settings.action.NEW_STORAGE");
+        } else if (isAutomotive()) {
+            // TODO(b/151671685): add intent to handle unmountable usb
+            return null;
         } else {
             intent.setClassName("com.android.settings",
                     "com.android.settings.deviceinfo.StorageWizardInit");
@@ -669,6 +676,9 @@
         if (isTv()) {
             intent.setPackage("com.android.tv.settings");
             intent.setAction(Settings.ACTION_INTERNAL_STORAGE_SETTINGS);
+        } else if (isAutomotive()) {
+            // TODO(b/151671685): add volume settings intent for automotive
+            return null;
         } else {
             switch (vol.getType()) {
                 case VolumeInfo.TYPE_PRIVATE:
@@ -700,7 +710,7 @@
     }
 
     private PendingIntent buildForgetPendingIntent(VolumeRecord rec) {
-        // Not used on TV
+        // Not used on TV and Automotive
         final Intent intent = new Intent();
         intent.setClassName("com.android.settings",
                 "com.android.settings.Settings$PrivateVolumeForgetActivity");
@@ -716,6 +726,9 @@
         if (isTv()) {
             intent.setPackage("com.android.tv.settings");
             intent.setAction("com.android.tv.settings.action.MIGRATE_STORAGE");
+        } else if (isAutomotive()) {
+            // TODO(b/151671685): add storage migrate intent for automotive
+            return null;
         } else {
             intent.setClassName("com.android.settings",
                     "com.android.settings.deviceinfo.StorageWizardMigrateProgress");
@@ -735,6 +748,9 @@
         if (isTv()) {
             intent.setPackage("com.android.tv.settings");
             intent.setAction("com.android.tv.settings.action.MOVE_APP");
+        } else if (isAutomotive()) {
+            // TODO(b/151671685): add storage move intent for automotive
+            return null;
         } else {
             intent.setClassName("com.android.settings",
                     "com.android.settings.deviceinfo.StorageWizardMoveProgress");
@@ -750,6 +766,9 @@
         if (isTv()) {
             intent.setPackage("com.android.tv.settings");
             intent.setAction(Settings.ACTION_INTERNAL_STORAGE_SETTINGS);
+        } else if (isAutomotive()) {
+            // TODO(b/151671685): add storage ready intent for automotive
+            return null;
         } else {
             intent.setClassName("com.android.settings",
                     "com.android.settings.deviceinfo.StorageWizardReady");
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index 56aae17..c637123 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -34,7 +34,6 @@
 import com.android.systemui.qs.customize.QSCustomizer;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.phone.LockIcon;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -148,11 +147,6 @@
         KeyguardMessageArea createKeyguardMessageArea();
 
         /**
-         * Creates the keyguard LockIcon.
-         */
-        LockIcon createLockIcon();
-
-        /**
          * Creates the QSPanel.
          */
         QSPanel createQSPanel();
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 5411839..bae5bb4 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -20,6 +20,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.content.res.Configuration;
+import android.graphics.Point;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -97,7 +98,7 @@
         }
         if (mSystemWindows.mDisplayController.getDisplayLayout(displayId).rotation()
                 != pd.mRotation && isImeShowing(displayId)) {
-            pd.startAnimation(true);
+            pd.startAnimation(true, false /* forceRestart */);
         }
     }
 
@@ -200,7 +201,15 @@
                         continue;
                     }
                     if (activeControl.getType() == InsetsState.ITYPE_IME) {
-                        mImeSourceControl = activeControl;
+                        mHandler.post(() -> {
+                            final Point lastSurfacePosition = mImeSourceControl != null
+                                    ? mImeSourceControl.getSurfacePosition() : null;
+                            mImeSourceControl = activeControl;
+                            if (!activeControl.getSurfacePosition().equals(lastSurfacePosition)
+                                    && mAnimation != null) {
+                                startAnimation(mImeShowing, true /* forceRestart */);
+                            }
+                        });
                     }
                 }
             }
@@ -212,7 +221,7 @@
                 return;
             }
             if (DEBUG) Slog.d(TAG, "Got showInsets for ime");
-            startAnimation(true /* show */);
+            startAnimation(true /* show */, false /* forceRestart */);
         }
 
         @Override
@@ -221,7 +230,7 @@
                 return;
             }
             if (DEBUG) Slog.d(TAG, "Got hideInsets for ime");
-            startAnimation(false /* show */);
+            startAnimation(false /* show */, false /* forceRestart */);
         }
 
         /**
@@ -239,7 +248,7 @@
             return imeSource.getFrame().top + (int) surfaceOffset;
         }
 
-        private void startAnimation(final boolean show) {
+        private void startAnimation(final boolean show, final boolean forceRestart) {
             final InsetsSource imeSource = mInsetsState.getSource(InsetsState.ITYPE_IME);
             if (imeSource == null || mImeSourceControl == null) {
                 return;
@@ -250,7 +259,7 @@
                             + (mAnimationDirection == DIRECTION_SHOW ? "SHOW"
                             : (mAnimationDirection == DIRECTION_HIDE ? "HIDE" : "NONE")));
                 }
-                if ((mAnimationDirection == DIRECTION_SHOW && show)
+                if (!forceRestart && (mAnimationDirection == DIRECTION_SHOW && show)
                         || (mAnimationDirection == DIRECTION_HIDE && !show)) {
                     return;
                 }
@@ -270,11 +279,6 @@
                 final float shownY = defaultY;
                 final float startY = show ? hiddenY : shownY;
                 final float endY = show ? shownY : hiddenY;
-                if (mImeShowing && show) {
-                    // IME is already showing, so set seek to end
-                    seekValue = shownY;
-                    seek = true;
-                }
                 mImeShowing = show;
                 mAnimation = ValueAnimator.ofFloat(startY, endY);
                 mAnimation.setDuration(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
index 464a740..072bc44 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMediaPlayerTest.kt
@@ -22,6 +22,8 @@
 import android.testing.TestableLooper
 import android.view.View
 import android.widget.TextView
+import androidx.arch.core.executor.ArchTaskExecutor
+import androidx.arch.core.executor.TaskExecutor
 import androidx.test.filters.SmallTest
 
 import com.android.systemui.R
@@ -50,25 +52,46 @@
     private lateinit var mediaMetadata: MediaMetadata.Builder
     private lateinit var entry: NotificationEntryBuilder
     @Mock private lateinit var mockView: View
-    private lateinit var textView: TextView
+    private lateinit var songView: TextView
+    private lateinit var artistView: TextView
     @Mock private lateinit var mockIcon: Icon
 
+    private val taskExecutor: TaskExecutor = object : TaskExecutor() {
+        public override fun executeOnDiskIO(runnable: Runnable) {
+            runnable.run()
+        }
+        public override fun postToMainThread(runnable: Runnable) {
+            runnable.run()
+        }
+        public override fun isMainThread(): Boolean {
+            return true
+        }
+    }
+
     @Before
     public fun setup() {
         fakeExecutor = FakeExecutor(FakeSystemClock())
         keyguardMediaPlayer = KeyguardMediaPlayer(context, fakeExecutor)
-        mockView = mock(View::class.java)
-        textView = TextView(context)
         mockIcon = mock(Icon::class.java)
+
+        mockView = mock(View::class.java)
+        songView = TextView(context)
+        artistView = TextView(context)
+        whenever<TextView>(mockView.findViewById(R.id.header_title)).thenReturn(songView)
+        whenever<TextView>(mockView.findViewById(R.id.header_artist)).thenReturn(artistView)
+
         mediaMetadata = MediaMetadata.Builder()
         entry = NotificationEntryBuilder()
 
+        ArchTaskExecutor.getInstance().setDelegate(taskExecutor)
+
         keyguardMediaPlayer.bindView(mockView)
     }
 
     @After
     public fun tearDown() {
         keyguardMediaPlayer.unbindView()
+        ArchTaskExecutor.getInstance().setDelegate(null)
     }
 
     @Test
@@ -87,34 +110,36 @@
     @Test
     public fun testUpdateControls() {
         keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
+        FakeExecutor.exhaustExecutors(fakeExecutor)
         verify(mockView).setVisibility(View.VISIBLE)
     }
 
     @Test
     public fun testClearControls() {
         keyguardMediaPlayer.clearControls()
+        FakeExecutor.exhaustExecutors(fakeExecutor)
         verify(mockView).setVisibility(View.GONE)
     }
 
     @Test
     public fun testSongName() {
-        whenever<TextView>(mockView.findViewById(R.id.header_title)).thenReturn(textView)
         val song: String = "Song"
         mediaMetadata.putText(MediaMetadata.METADATA_KEY_TITLE, song)
 
         keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
 
-        assertThat(textView.getText()).isEqualTo(song)
+        assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
+        assertThat(songView.getText()).isEqualTo(song)
     }
 
     @Test
     public fun testArtistName() {
-        whenever<TextView>(mockView.findViewById(R.id.header_artist)).thenReturn(textView)
         val artist: String = "Artist"
         mediaMetadata.putText(MediaMetadata.METADATA_KEY_ARTIST, artist)
 
         keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
 
-        assertThat(textView.getText()).isEqualTo(artist)
+        assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
+        assertThat(artistView.getText()).isEqualTo(artist)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 9d9ba1b..eecde72 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -52,6 +52,7 @@
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IRemoteCallback;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.telephony.ServiceState;
@@ -63,6 +64,7 @@
 import android.testing.TestableLooper;
 
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.keyguard.KeyguardUpdateMonitor.BiometricAuthenticated;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
@@ -506,6 +508,24 @@
     }
 
     @Test
+    public void testBiometricsCleared_whenUserSwitches() throws Exception {
+        final IRemoteCallback reply = new IRemoteCallback.Stub() {
+            @Override
+            public void sendResult(Bundle data) {} // do nothing
+        };
+        final BiometricAuthenticated dummyAuthentication =
+                new BiometricAuthenticated(true /* authenticated */, true /* strong */);
+        mKeyguardUpdateMonitor.mUserFaceAuthenticated.put(0 /* user */, dummyAuthentication);
+        mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.put(0 /* user */, dummyAuthentication);
+        assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(1);
+        assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(1);
+
+        mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, reply);
+        assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(0);
+        assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(0);
+    }
+
+    @Test
     public void testGetUserCanSkipBouncer_whenTrust() {
         int user = KeyguardUpdateMonitor.getCurrentUser();
         mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, user, 0 /* flags */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 6e612d7..6f3fbb9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -62,7 +62,9 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationRemoveInterceptor;
@@ -90,6 +92,8 @@
 import com.android.systemui.util.FloatingContentCoordinator;
 import com.android.systemui.util.InjectionInflationController;
 
+import com.google.common.collect.ImmutableList;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -136,6 +140,9 @@
     @Mock
     private FloatingContentCoordinator mFloatingContentCoordinator;
 
+    private SysUiState mSysUiState;
+    private boolean mSysUiStateBubblesExpanded;
+
     @Captor
     private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
     @Captor
@@ -229,6 +236,11 @@
         mZenModeConfig.suppressedVisualEffects = 0;
         when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
 
+        mSysUiState = new SysUiState();
+        mSysUiState.addCallback(sysUiFlags ->
+                mSysUiStateBubblesExpanded =
+                        (sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0);
+
         TestableNotificationInterruptStateProviderImpl interruptionStateProvider =
                 new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(),
                         mock(PowerManager.class),
@@ -257,7 +269,8 @@
                 mNotifPipeline,
                 mFeatureFlagsOldPipeline,
                 mDumpManager,
-                mFloatingContentCoordinator);
+                mFloatingContentCoordinator,
+                mSysUiState);
         mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
         mBubbleController.setExpandListener(mBubbleExpandListener);
 
@@ -277,6 +290,7 @@
         assertTrue(mBubbleController.hasBubbles());
 
         verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -284,6 +298,7 @@
         assertFalse(mBubbleController.hasBubbles());
         mBubbleController.updateBubble(mRow.getEntry());
         assertTrue(mBubbleController.hasBubbles());
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -300,6 +315,25 @@
         assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
         verify(mNotificationEntryManager, times(2)).updateNotifications(anyString());
         verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
+
+        assertFalse(mSysUiStateBubblesExpanded);
+    }
+
+    @Test
+    public void testPromoteBubble_autoExpand() {
+        mBubbleController.updateBubble(mRow2.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+        mBubbleController.removeBubble(
+                mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+
+        Bubble b = mBubbleData.getOverflowBubbleWithKey(mRow.getEntry().getKey());
+        assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(b));
+
+        Bubble b2 = mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey());
+        assertThat(mBubbleData.getSelectedBubble()).isEqualTo(b2);
+
+        mBubbleController.promoteBubbleFromOverflow(b);
+        assertThat(mBubbleData.getSelectedBubble()).isEqualTo(b);
     }
 
     @Test
@@ -323,6 +357,8 @@
         verify(mNotificationEntryManager, times(1)).performRemoveNotification(
                 eq(mRow.getEntry().getSbn()), anyInt());
         assertFalse(mBubbleController.hasBubbles());
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -340,6 +376,8 @@
         verify(mNotificationEntryManager, times(3)).updateNotifications(any());
         assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
         assertNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -363,6 +401,8 @@
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
         assertTrue(mNotificationShadeWindowController.getBubbleExpanded());
 
+        assertTrue(mSysUiStateBubblesExpanded);
+
         // Make sure the notif is suppressed
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow.getEntry()));
@@ -372,6 +412,8 @@
         verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
         assertFalse(mBubbleController.isStackExpanded());
         assertFalse(mNotificationShadeWindowController.getBubbleExpanded());
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -395,6 +437,8 @@
         assertTrue(mBubbleController.isStackExpanded());
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
 
+        assertTrue(mSysUiStateBubblesExpanded);
+
         // Last added is the one that is expanded
         assertEquals(mRow2.getEntry(), mBubbleData.getSelectedBubble().getEntry());
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
@@ -416,6 +460,8 @@
         // Collapse
         mBubbleController.collapseStack();
         assertFalse(mBubbleController.isStackExpanded());
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -437,6 +483,8 @@
         assertTrue(mBubbleController.isStackExpanded());
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
 
+        assertTrue(mSysUiStateBubblesExpanded);
+
         // Notif is suppressed after expansion
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow.getEntry()));
@@ -463,6 +511,8 @@
         assertTrue(mBubbleController.isStackExpanded());
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
 
+        assertTrue(mSysUiStateBubblesExpanded);
+
         // Notif is suppressed after expansion
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow.getEntry()));
@@ -493,6 +543,8 @@
         BubbleStackView stackView = mBubbleController.getStackView();
         mBubbleController.expandStack();
 
+        assertTrue(mSysUiStateBubblesExpanded);
+
         assertTrue(mBubbleController.isStackExpanded());
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
 
@@ -522,6 +574,8 @@
         verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
         verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
         assertFalse(mBubbleController.hasBubbles());
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -541,6 +595,8 @@
 
         // # of bubbles should change
         verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -559,6 +615,8 @@
 
         // # of bubbles should change
         verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+        assertTrue(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -579,6 +637,8 @@
 
         // # of bubbles should change
         verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -605,6 +665,8 @@
 
         // # of bubbles should change
         verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+
+        assertFalse(mSysUiStateBubblesExpanded);
     }
 
     @Test
@@ -619,6 +681,8 @@
                 mRow.getEntry().getKey(), mRow.getEntry(), REASON_APP_CANCEL);
 
         mBubbleController.expandStackAndSelectBubble(key);
+
+        assertTrue(mSysUiStateBubblesExpanded);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 6244644..a31e3f8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -58,6 +58,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -132,6 +133,9 @@
     private KeyguardBypassController mKeyguardBypassController;
     @Mock
     private FloatingContentCoordinator mFloatingContentCoordinator;
+
+    private SysUiState mSysUiState = new SysUiState();
+
     @Captor
     private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
     private TestableBubbleController mBubbleController;
@@ -242,7 +246,8 @@
                 mNotifPipeline,
                 mFeatureFlagsNewPipeline,
                 mDumpManager,
-                mFloatingContentCoordinator);
+                mFloatingContentCoordinator,
+                mSysUiState);
         mBubbleController.addNotifCallback(mNotifCallback);
         mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
         mBubbleController.setExpandListener(mBubbleExpandListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
index d3d90c4..f486102 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -52,12 +53,13 @@
             NotifPipeline notifPipeline,
             FeatureFlags featureFlags,
             DumpManager dumpManager,
-            FloatingContentCoordinator floatingContentCoordinator) {
+            FloatingContentCoordinator floatingContentCoordinator,
+            SysUiState sysUiState) {
         super(context,
                 notificationShadeWindowController, statusBarStateController, shadeController,
                 data, Runnable::run, configurationController, interruptionStateProvider,
                 zenModeController, lockscreenUserManager, groupManager, entryManager,
-                notifPipeline, featureFlags, dumpManager, floatingContentCoordinator);
+                notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, sysUiState);
         setInflateSynchronously(true);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index d5a654d..93aee33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -161,7 +161,7 @@
         verify(listingController).addCallback(capture(listingCallbackCaptor))
     }
 
-    private fun builderFromInfo(
+    private fun statelessBuilderFromInfo(
         controlInfo: ControlInfo,
         structure: CharSequence = ""
     ): Control.StatelessBuilder {
@@ -170,6 +170,15 @@
                 .setSubtitle(controlInfo.controlSubtitle).setStructure(structure)
     }
 
+    private fun statefulBuilderFromInfo(
+        controlInfo: ControlInfo,
+        structure: CharSequence = ""
+    ): Control.StatefulBuilder {
+        return Control.StatefulBuilder(controlInfo.controlId, pendingIntent)
+                .setDeviceType(controlInfo.deviceType).setTitle(controlInfo.controlTitle)
+                .setSubtitle(controlInfo.controlSubtitle).setStructure(structure)
+    }
+
     @Test
     fun testStartOnUser() {
         assertEquals(user, controller.currentUserId)
@@ -236,7 +245,7 @@
     @Test
     fun testLoadForComponent_noFavorites() {
         var loaded = false
-        val control = builderFromInfo(TEST_CONTROL_INFO).build()
+        val control = statelessBuilderFromInfo(TEST_CONTROL_INFO).build()
 
         controller.loadForComponent(TEST_COMPONENT, Consumer { data ->
             val controls = data.allControls
@@ -263,8 +272,8 @@
     @Test
     fun testLoadForComponent_favorites() {
         var loaded = false
-        val control = builderFromInfo(TEST_CONTROL_INFO).build()
-        val control2 = builderFromInfo(TEST_CONTROL_INFO_2).build()
+        val control = statelessBuilderFromInfo(TEST_CONTROL_INFO).build()
+        val control2 = statelessBuilderFromInfo(TEST_CONTROL_INFO_2).build()
         controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO)
         controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO_2)
         delayableExecutor.runAllReady()
@@ -307,6 +316,7 @@
             assertEquals(1, controls.size)
             val controlStatus = controls[0]
             assertEquals(TEST_CONTROL_ID, controlStatus.control.controlId)
+            assertEquals(TEST_STRUCTURE_INFO.structure, controlStatus.control.structure)
             assertTrue(controlStatus.favorite)
             assertTrue(controlStatus.removed)
 
@@ -337,6 +347,7 @@
             assertEquals(1, controls.size)
             val controlStatus = controls[0]
             assertEquals(TEST_CONTROL_ID, controlStatus.control.controlId)
+            assertEquals(TEST_STRUCTURE_INFO.structure, controlStatus.control.structure)
             assertTrue(controlStatus.favorite)
             assertFalse(controlStatus.removed)
 
@@ -443,7 +454,7 @@
         delayableExecutor.runAllReady()
 
         val newControlInfo = TEST_CONTROL_INFO.copy(controlTitle = TEST_CONTROL_TITLE_2)
-        val control = builderFromInfo(newControlInfo).build()
+        val control = statelessBuilderFromInfo(newControlInfo).build()
 
         controller.loadForComponent(TEST_COMPONENT, Consumer {})
 
@@ -459,11 +470,11 @@
     }
 
     @Test
-    fun testFavoriteInformationModifiedOnRefresh() {
+    fun testFavoriteInformationModifiedOnRefreshWithOkStatus() {
         controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO)
 
         val newControlInfo = TEST_CONTROL_INFO.copy(controlTitle = TEST_CONTROL_TITLE_2)
-        val control = builderFromInfo(newControlInfo).build()
+        val control = statefulBuilderFromInfo(newControlInfo).setStatus(Control.STATUS_OK).build()
 
         controller.refreshStatus(TEST_COMPONENT, control)
 
@@ -475,6 +486,23 @@
     }
 
     @Test
+    fun testFavoriteInformationNotModifiedOnRefreshWithNonOkStatus() {
+        controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO)
+
+        val newControlInfo = TEST_CONTROL_INFO.copy(controlTitle = TEST_CONTROL_TITLE_2)
+        val control = statefulBuilderFromInfo(newControlInfo).setStatus(Control.STATUS_ERROR)
+            .build()
+
+        controller.refreshStatus(TEST_COMPONENT, control)
+
+        delayableExecutor.runAllReady()
+
+        val favorites = controller.getFavorites().flatMap { it.controls }
+        assertEquals(1, favorites.size)
+        assertEquals(TEST_CONTROL_INFO, favorites[0])
+    }
+
+    @Test
     fun testSwitchUsers() {
         controller.replaceFavoritesForStructure(TEST_STRUCTURE_INFO)
         delayableExecutor.runAllReady()
@@ -760,7 +788,8 @@
     @Test
     fun testSeedFavoritesForComponent() {
         var succeeded = false
-        val control = builderFromInfo(TEST_CONTROL_INFO, TEST_STRUCTURE_INFO.structure).build()
+        val control = statelessBuilderFromInfo(TEST_CONTROL_INFO, TEST_STRUCTURE_INFO.structure)
+            .build()
 
         controller.seedFavoritesForComponent(TEST_COMPONENT, Consumer { accepted ->
             succeeded = accepted
@@ -801,7 +830,8 @@
     fun testSeedFavoritesForComponent_inProgressCallback() {
         var succeeded = false
         var seeded = false
-        val control = builderFromInfo(TEST_CONTROL_INFO, TEST_STRUCTURE_INFO.structure).build()
+        val control = statelessBuilderFromInfo(TEST_CONTROL_INFO, TEST_STRUCTURE_INFO.structure)
+            .build()
 
         controller.seedFavoritesForComponent(TEST_COMPONENT, Consumer { accepted ->
             succeeded = accepted
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index f061f34..f4583f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -22,7 +22,6 @@
 import android.view.Choreographer
 import android.view.View
 import android.view.ViewRootImpl
-import androidx.dynamicanimation.animation.SpringAnimation
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
@@ -35,10 +34,14 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
-import org.mockito.Mockito.*
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
 
 @RunWith(AndroidTestingRunner::class)
@@ -56,7 +59,8 @@
     @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var root: View
     @Mock private lateinit var viewRootImpl: ViewRootImpl
-    @Mock private lateinit var shadeSpring: SpringAnimation
+    @Mock private lateinit var shadeSpring: NotificationShadeDepthController.DepthAnimation
+    @Mock private lateinit var globalActionsSpring: NotificationShadeDepthController.DepthAnimation
     @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
 
     private lateinit var statusBarStateListener: StatusBarStateController.StateListener
@@ -76,6 +80,7 @@
                 keyguardStateController, choreographer, wallpaperManager,
                 notificationShadeWindowController, dumpManager)
         notificationShadeDepthController.shadeSpring = shadeSpring
+        notificationShadeDepthController.globalActionsSpring = globalActionsSpring
         notificationShadeDepthController.root = root
 
         val captor = ArgumentCaptor.forClass(StatusBarStateController.StateListener::class.java)
@@ -92,7 +97,7 @@
     fun onPanelExpansionChanged_apliesBlur_ifShade() {
         notificationShadeDepthController.onPanelExpansionChanged(1f /* expansion */,
                 false /* tracking */)
-        verify(shadeSpring).animateToFinalPosition(eq(maxBlur.toFloat()))
+        verify(shadeSpring).animateTo(eq(maxBlur), any())
     }
 
     @Test
@@ -102,13 +107,13 @@
 
         statusBarState = StatusBarState.KEYGUARD
         statusBarStateListener.onStateChanged(statusBarState)
-        verify(shadeSpring).animateToFinalPosition(eq(0f))
+        verify(shadeSpring).animateTo(eq(0), any())
     }
 
     @Test
-    fun updateGlobalDialogVisibility_schedulesUpdate() {
+    fun updateGlobalDialogVisibility_appliesBlur() {
         notificationShadeDepthController.updateGlobalDialogVisibility(0.5f, root)
-        verify(choreographer).postFrameCallback(any())
+        verify(globalActionsSpring).animateTo(eq(maxBlur / 2), safeEq(root))
     }
 
     private fun <T : Any> safeEq(value: T): T {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index 261dc82..f4fbd7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -48,7 +48,7 @@
 
     /* ListEntry properties */
     private GroupEntry mParent;
-    private int mSection;
+    private int mSection = -1;
 
     public NotificationEntry build() {
         StatusBarNotification sbn = mSbn != null ? mSbn : mSbnBuilder.build();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java
new file mode 100644
index 0000000..87fc020
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 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.systemui.statusbar.notification.collection.coordinator;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.util.SparseArray;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable.PluggableListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class HideNotifsForOtherUsersCoordinatorTest extends SysuiTestCase {
+
+    @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+    @Mock private NotifPipeline mNotifPipeline;
+    @Mock private PluggableListener<NotifFilter> mInvalidationListener;
+
+    @Captor private ArgumentCaptor<UserChangedListener> mUserChangedListenerCaptor;
+    @Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
+
+    private UserChangedListener mCapturedUserChangeListener;
+    private NotifFilter mCapturedNotifFilter;
+
+    private NotificationEntry mEntry = new NotificationEntryBuilder().build();
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        HideNotifsForOtherUsersCoordinator coordinator =
+                new HideNotifsForOtherUsersCoordinator(mLockscreenUserManager);
+        coordinator.attach(mNotifPipeline);
+
+        verify(mLockscreenUserManager).addUserChangedListener(mUserChangedListenerCaptor.capture());
+        verify(mNotifPipeline).addPreGroupFilter(mNotifFilterCaptor.capture());
+
+        mCapturedUserChangeListener = mUserChangedListenerCaptor.getValue();
+        mCapturedNotifFilter = mNotifFilterCaptor.getValue();
+
+        mCapturedNotifFilter.setInvalidationListener(mInvalidationListener);
+    }
+
+    @Test
+    public void testFilterOutNotifsFromOtherProfiles() {
+        // GIVEN that all notifs are NOT for the current user
+        when(mLockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(false);
+
+        // THEN they should all be filtered out
+        assertTrue(mCapturedNotifFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void testPreserveNotifsFromThisProfile() {
+        // GIVEN that all notifs ARE for the current user
+        when(mLockscreenUserManager.isCurrentProfile(anyInt())).thenReturn(true);
+
+        // THEN none should be filtered out
+        assertFalse(mCapturedNotifFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void testFilterIsInvalidatedWhenProfilesChange() {
+        // WHEN the current user profiles change
+        mCapturedUserChangeListener.onCurrentProfilesChanged(new SparseArray<>());
+
+        // THEN the filter is invalidated
+        verify(mInvalidationListener).onPluggableInvalidated(mCapturedNotifFilter);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
index c4f3a16..4f48108 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
@@ -102,16 +102,6 @@
     }
 
     @Test
-    public void notificationNotForCurrentProfile() {
-        // GIVEN the notification isn't for the given user
-        setupUnfilteredState(mEntry);
-        when(mLockscreenUserManager.isCurrentProfile(NOTIF_USER_ID)).thenReturn(false);
-
-        // THEN filter out the entry
-        assertTrue(mKeyguardFilter.shouldFilterOut(mEntry, 0));
-    }
-
-    @Test
     public void keyguardNotShowing() {
         // GIVEN the lockscreen isn't showing
         setupUnfilteredState(mEntry);
@@ -229,9 +219,6 @@
      * KeyguardNotificationCoordinator when the keyguard is showing.
      */
     private void setupUnfilteredState(NotificationEntry entry) {
-        // notification is for current profile
-        when(mLockscreenUserManager.isCurrentProfile(NOTIF_USER_ID)).thenReturn(true);
-
         // keyguard is showing
         when(mKeyguardStateController.isShowing()).thenReturn(true);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index e84f9cf..85acbe6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -19,9 +19,8 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
 
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -42,6 +41,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -51,20 +51,23 @@
 
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private NotifPipeline mNotifPipeline;
+
+    @Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor;
+
     private NotificationEntry mEntry;
-    private RankingCoordinator mRankingCoordinator;
-    private NotifFilter mRankingFilter;
+    private NotifFilter mCapturedSuspendedFilter;
+    private NotifFilter mCapturedDozingFilter;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mRankingCoordinator = new RankingCoordinator(mStatusBarStateController);
+        RankingCoordinator rankingCoordinator = new RankingCoordinator(mStatusBarStateController);
         mEntry = new NotificationEntryBuilder().build();
 
-        ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
-        mRankingCoordinator.attach(mNotifPipeline);
-        verify(mNotifPipeline, times(1)).addPreGroupFilter(filterCaptor.capture());
-        mRankingFilter = filterCaptor.getValue();
+        rankingCoordinator.attach(mNotifPipeline);
+        verify(mNotifPipeline, times(2)).addPreGroupFilter(mNotifFilterCaptor.capture());
+        mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0);
+        mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1);
     }
 
     @Test
@@ -73,7 +76,7 @@
         mEntry.setRanking(getRankingForUnfilteredNotif().build());
 
         // THEN don't filter out the notification
-        assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0));
+        assertFalse(mCapturedSuspendedFilter.shouldFilterOut(mEntry, 0));
     }
 
     @Test
@@ -84,7 +87,7 @@
                 .build());
 
         // THEN filter out the notification
-        assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0));
+        assertTrue(mCapturedSuspendedFilter.shouldFilterOut(mEntry, 0));
     }
 
     @Test
@@ -98,13 +101,13 @@
         when(mStatusBarStateController.isDozing()).thenReturn(true);
 
         // THEN filter out the notification
-        assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0));
+        assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
 
         // WHEN it's not dozing (showing the notification list)
         when(mStatusBarStateController.isDozing()).thenReturn(false);
 
         // THEN don't filter out the notification
-        assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0));
+        assertFalse(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
     }
 
     @Test
@@ -118,13 +121,13 @@
         when(mStatusBarStateController.isDozing()).thenReturn(true);
 
         // THEN don't filter out the notification
-        assertFalse(mRankingFilter.shouldFilterOut(mEntry, 0));
+        assertFalse(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
 
         // WHEN it's not dozing (showing the notification list)
         when(mStatusBarStateController.isDozing()).thenReturn(false);
 
         // THEN filter out the notification
-        assertTrue(mRankingFilter.shouldFilterOut(mEntry, 0));
+        assertTrue(mCapturedDozingFilter.shouldFilterOut(mEntry, 0));
     }
 
     private RankingBuilder getRankingForUnfilteredNotif() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 35971bd..e052ae2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
@@ -56,11 +57,13 @@
 
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -91,13 +94,14 @@
     private Handler mHandler;
     @Mock
     private KeyguardSecurityModel mKeyguardSecurityModel;
-
+    @Rule
+    public MockitoRule mRule = MockitoJUnit.rule();
+    private ViewGroup mRootView;
     private KeyguardBouncer mBouncer;
 
     @Before
     public void setup() {
         allowTestableLooperAsMainThread();
-        MockitoAnnotations.initMocks(this);
         mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor);
         mDependency.injectTestDependency(KeyguardSecurityModel.class, mKeyguardSecurityModel);
         mDependency.injectMockDependency(KeyguardStateController.class);
@@ -115,6 +119,8 @@
             protected void inflateView() {
                 super.inflateView();
                 mKeyguardView = mKeyguardHostView;
+                mRoot = spy(mRoot);
+                mRootView = mRoot;
             }
         };
     }
@@ -217,6 +223,7 @@
 
         mBouncer.setExpansion(0);
         verify(mKeyguardHostView).onResume();
+        verify(mRootView).announceForAccessibility(any());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
index 487885a..85b5d70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenIconControllerTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.res.Resources;
 import android.view.View;
 
 import androidx.test.filters.SmallTest;
@@ -35,6 +36,7 @@
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.policy.AccessibilityController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -71,6 +73,12 @@
     private KeyguardBypassController mKeyguardBypassController;
     @Mock
     private DockManager mDockManager;
+    @Mock
+    private KeyguardStateController mKeyguardStateController;
+    @Mock
+    private Resources mResources;
+    @Mock
+    private HeadsUpManagerPhone mHeadsUpManagerPhone;
 
 
     @Before
@@ -81,7 +89,8 @@
                 mLockscreenGestureLogger, mKeyguardUpdateMonitor, mLockPatternUtils,
                 mShadeController, mAccessibilityController, mKeyguardIndicationController,
                 mStatusBarStateController, mConfigurationController, mNotificationWakeUpCoordinator,
-                mKeyguardBypassController, mDockManager);
+                mKeyguardBypassController, mDockManager, mKeyguardStateController, mResources,
+                mHeadsUpManagerPhone);
 
         mLockIconController.attach(mLockIcon);
     }
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index 2fbba68..6af5fe5 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -60,6 +60,7 @@
     hostdex: true, // for hiddenapi check
     visibility: ["//frameworks/base/packages/Tethering:__subpackages__"],
     apex_available: ["com.android.tethering"],
+    permitted_packages: ["android.net"],
 }
 
 stubs_defaults {
@@ -125,17 +126,17 @@
 java_library {
     name: "framework-tethering-stubs-publicapi",
     srcs: [":framework-tethering-stubs-srcs-publicapi"],
-    sdk_version: "current",
+    defaults: ["framework-module-stubs-lib-defaults-publicapi"],
 }
 
 java_library {
     name: "framework-tethering-stubs-systemapi",
     srcs: [":framework-tethering-stubs-srcs-systemapi"],
-    sdk_version: "system_current",
+    defaults: ["framework-module-stubs-lib-defaults-systemapi"],
 }
 
 java_library {
     name: "framework-tethering-stubs-module_libs_api",
     srcs: [":framework-tethering-stubs-srcs-module_libs_api"],
-    sdk_version: "module_current",
+    defaults: ["framework-module-stubs-lib-defaults-systemapi"],
 }
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index 36113ac..4b2c921 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -303,31 +303,26 @@
 
         final UserManager userManager = (UserManager) mContext.getSystemService(
                 Context.USER_SERVICE);
-        mTetheringRestriction = new UserRestrictionActionListener(userManager, this);
+        mTetheringRestriction = new UserRestrictionActionListener(
+                userManager, this, mNotificationUpdater);
         mExecutor = new TetheringThreadExecutor(mHandler);
         mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
+        mNetdCallback = new NetdCallback();
 
         // Load tethering configuration.
         updateConfiguration();
-        // NetdCallback should be registered after updateConfiguration() to ensure
-        // TetheringConfiguration is created.
-        mNetdCallback = new NetdCallback();
+    }
+
+    /**
+     * Start to register callbacks.
+     * Call this function when tethering is ready to handle callback events.
+     */
+    public void startStateMachineUpdaters() {
         try {
             mNetd.registerUnsolicitedEventListener(mNetdCallback);
         } catch (RemoteException e) {
             mLog.e("Unable to register netd UnsolicitedEventListener");
         }
-
-        startStateMachineUpdaters(mHandler);
-        startTrackDefaultNetwork();
-
-        final WifiManager wifiManager = getWifiManager();
-        if (wifiManager != null) {
-            wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback());
-        }
-    }
-
-    private void startStateMachineUpdaters(Handler handler) {
         mCarrierConfigChange.startListening();
         mContext.getSystemService(TelephonyManager.class).listen(mActiveDataSubIdListener,
                 PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
@@ -340,7 +335,14 @@
         filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
         filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);
         filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED);
-        mContext.registerReceiver(mStateReceiver, filter, null, handler);
+        mContext.registerReceiver(mStateReceiver, filter, null, mHandler);
+
+        final WifiManager wifiManager = getWifiManager();
+        if (wifiManager != null) {
+            wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback());
+        }
+
+        startTrackDefaultNetwork();
     }
 
     private class TetheringThreadExecutor implements Executor {
@@ -369,9 +371,10 @@
 
             mActiveDataSubId = subId;
             updateConfiguration();
+            mNotificationUpdater.onActiveDataSubscriptionIdChanged(subId);
             // To avoid launching unexpected provisioning checks, ignore re-provisioning
             // when no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning()
-            // ill be triggered again when CarrierConfig is loaded.
+            // will be triggered again when CarrierConfig is loaded.
             if (mEntitlementMgr.getCarrierConfig(mConfig) != null) {
                 mEntitlementMgr.reevaluateSimCardProvisioning(mConfig);
             } else {
@@ -431,9 +434,7 @@
         // Called by wifi when the number of soft AP clients changed.
         @Override
         public void onConnectedClientsChanged(final List<WifiClient> clients) {
-            if (mConnectedClientsTracker.updateConnectedClients(mForwardedDownstreams, clients)) {
-                reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients());
-            }
+            updateConnectedClients(clients);
         }
     }
 
@@ -635,7 +636,10 @@
                 Context.ETHERNET_SERVICE);
         synchronized (mPublicSync) {
             if (enable) {
-                if (mEthernetCallback != null) return TETHER_ERROR_NO_ERROR;
+                if (mEthernetCallback != null) {
+                    Log.d(TAG, "Ethernet tethering already started");
+                    return TETHER_ERROR_NO_ERROR;
+                }
 
                 mEthernetCallback = new EthernetCallback();
                 mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback);
@@ -996,11 +1000,14 @@
     protected static class UserRestrictionActionListener {
         private final UserManager mUserManager;
         private final Tethering mWrapper;
+        private final TetheringNotificationUpdater mNotificationUpdater;
         public boolean mDisallowTethering;
 
-        public UserRestrictionActionListener(UserManager um, Tethering wrapper) {
+        public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper,
+                @NonNull TetheringNotificationUpdater updater) {
             mUserManager = um;
             mWrapper = wrapper;
+            mNotificationUpdater = updater;
             mDisallowTethering = false;
         }
 
@@ -1019,13 +1026,21 @@
                 return;
             }
 
-            // TODO: Add user restrictions notification.
-            final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0);
-
-            if (newlyDisallowed && isTetheringActiveOnDevice) {
-                mWrapper.untetherAll();
-                // TODO(b/148139325): send tetheringSupported on restriction change
+            if (!newlyDisallowed) {
+                // Clear the restricted notification when user is allowed to have tethering
+                // function.
+                mNotificationUpdater.tetheringRestrictionLifted();
+                return;
             }
+
+            // Restricted notification is shown when tethering function is disallowed on
+            // user's device.
+            mNotificationUpdater.notifyTetheringDisabledByRestriction();
+
+            // Untether from all downstreams since tethering is disallowed.
+            mWrapper.untetherAll();
+
+            // TODO(b/148139325): send tetheringSupported on restriction change
         }
     }
 
@@ -1559,6 +1574,7 @@
             mIPv6TetheringCoordinator.removeActiveDownstream(who);
             mOffload.excludeDownstreamInterface(who.interfaceName());
             mForwardedDownstreams.remove(who);
+            updateConnectedClients(null /* wifiClients */);
 
             // If this is a Wi-Fi interface, tell WifiManager of any errors
             // or the inactive serving state.
@@ -2141,6 +2157,12 @@
         return false;
     }
 
+    private void updateConnectedClients(final List<WifiClient> wifiClients) {
+        if (mConnectedClientsTracker.updateConnectedClients(mForwardedDownstreams, wifiClients)) {
+            reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients());
+        }
+    }
+
     private IpServer.Callback makeControlCallback() {
         return new IpServer.Callback() {
             @Override
@@ -2155,10 +2177,7 @@
 
             @Override
             public void dhcpLeasesChanged() {
-                if (mConnectedClientsTracker.updateConnectedClients(
-                        mForwardedDownstreams, null /* wifiClients */)) {
-                    reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients());
-                }
+                updateConnectedClients(null /* wifiClients */);
             }
         };
     }
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java
index b97f752..992cdd8 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java
@@ -29,12 +29,14 @@
 import android.content.res.Resources;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.SparseArray;
 
 import androidx.annotation.ArrayRes;
 import androidx.annotation.DrawableRes;
+import androidx.annotation.IntDef;
 import androidx.annotation.IntRange;
 import androidx.annotation.NonNull;
 
@@ -54,10 +56,15 @@
 public class TetheringNotificationUpdater {
     private static final String TAG = TetheringNotificationUpdater.class.getSimpleName();
     private static final String CHANNEL_ID = "TETHERING_STATUS";
+    private static final String WIFI_DOWNSTREAM = "WIFI";
+    private static final String USB_DOWNSTREAM = "USB";
+    private static final String BLUETOOTH_DOWNSTREAM = "BT";
     private static final boolean NOTIFY_DONE = true;
     private static final boolean NO_NOTIFY = false;
     // Id to update and cancel tethering notification. Must be unique within the tethering app.
-    private static final int NOTIFY_ID = 20191115;
+    private static final int ENABLE_NOTIFICATION_ID = 1000;
+    // Id to update and cancel restricted notification. Must be unique within the tethering app.
+    private static final int RESTRICTED_NOTIFICATION_ID = 1001;
     @VisibleForTesting
     static final int NO_ICON_ID = 0;
     @VisibleForTesting
@@ -65,14 +72,25 @@
     private final Context mContext;
     private final NotificationManager mNotificationManager;
     private final NotificationChannel mChannel;
-    // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
-    // This value has to be made 1 2 and 4, and OR'd with the others.
+
     // WARNING : the constructor is called on a different thread. Thread safety therefore
     // relies on this value being initialized to 0, and not any other value. If you need
     // to change this, you will need to change the thread where the constructor is invoked,
     // or to introduce synchronization.
+    // Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
+    // This value has to be made 1 2 and 4, and OR'd with the others.
     private int mDownstreamTypesMask = DOWNSTREAM_NONE;
 
+    // WARNING : this value is not able to being initialized to 0 and must have volatile because
+    // telephony service is not guaranteed that is up before tethering service starts. If telephony
+    // is up later than tethering, TetheringNotificationUpdater will use incorrect and valid
+    // subscription id(0) to query resources. Therefore, initialized subscription id must be
+    // INVALID_SUBSCRIPTION_ID.
+    private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+    @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID})
+    @interface NotificationId {}
+
     public TetheringNotificationUpdater(@NonNull final Context context) {
         mContext = context;
         mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0)
@@ -88,19 +106,46 @@
     public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) {
         if (mDownstreamTypesMask == downstreamTypesMask) return;
         mDownstreamTypesMask = downstreamTypesMask;
-        updateNotification();
+        updateEnableNotification();
     }
 
-    private void updateNotification() {
+    /** Called when active data subscription id changed */
+    public void onActiveDataSubscriptionIdChanged(final int subId) {
+        if (mActiveDataSubId == subId) return;
+        mActiveDataSubId = subId;
+        updateEnableNotification();
+    }
+
+    @VisibleForTesting
+    Resources getResourcesForSubId(@NonNull final Context c, final int subId) {
+        return SubscriptionManager.getResourcesForSubId(c, subId);
+    }
+
+    private void updateEnableNotification() {
         final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE;
 
         if (tetheringInactive || setupNotification() == NO_NOTIFY) {
-            clearNotification();
+            clearNotification(ENABLE_NOTIFICATION_ID);
         }
     }
 
-    private void clearNotification() {
-        mNotificationManager.cancel(null /* tag */, NOTIFY_ID);
+    @VisibleForTesting
+    void tetheringRestrictionLifted() {
+        clearNotification(RESTRICTED_NOTIFICATION_ID);
+    }
+
+    private void clearNotification(@NotificationId final int id) {
+        mNotificationManager.cancel(null /* tag */, id);
+    }
+
+    @VisibleForTesting
+    void notifyTetheringDisabledByRestriction() {
+        final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
+        final String title = res.getString(R.string.disable_tether_notification_title);
+        final String message = res.getString(R.string.disable_tether_notification_message);
+
+        showNotification(R.drawable.stat_sys_tether_general, title, message,
+                RESTRICTED_NOTIFICATION_ID);
     }
 
     /**
@@ -110,16 +155,17 @@
      *
      * @return downstream types mask value.
      */
+    @VisibleForTesting
     @IntRange(from = 0, to = 7)
-    private int getDownstreamTypesMask(@NonNull final String types) {
+    int getDownstreamTypesMask(@NonNull final String types) {
         int downstreamTypesMask = DOWNSTREAM_NONE;
         final String[] downstreams = types.split("\\|");
         for (String downstream : downstreams) {
-            if ("USB".equals(downstream.trim())) {
+            if (USB_DOWNSTREAM.equals(downstream.trim())) {
                 downstreamTypesMask |= (1 << TETHERING_USB);
-            } else if ("WIFI".equals(downstream.trim())) {
+            } else if (WIFI_DOWNSTREAM.equals(downstream.trim())) {
                 downstreamTypesMask |= (1 << TETHERING_WIFI);
-            } else if ("BT".equals(downstream.trim())) {
+            } else if (BLUETOOTH_DOWNSTREAM.equals(downstream.trim())) {
                 downstreamTypesMask |= (1 << TETHERING_BLUETOOTH);
             }
         }
@@ -134,9 +180,8 @@
      *
      * @return {@link android.util.SparseArray} with downstream types and icon id info.
      */
-    @NonNull
-    private SparseArray<Integer> getIcons(@ArrayRes int id) {
-        final Resources res = mContext.getResources();
+    @VisibleForTesting
+    SparseArray<Integer> getIcons(@ArrayRes int id, @NonNull Resources res) {
         final String[] array = res.getStringArray(id);
         final SparseArray<Integer> icons = new SparseArray<>();
         for (String config : array) {
@@ -161,8 +206,9 @@
     }
 
     private boolean setupNotification() {
-        final Resources res = mContext.getResources();
-        final SparseArray<Integer> downstreamIcons = getIcons(R.array.tethering_notification_icons);
+        final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
+        final SparseArray<Integer> downstreamIcons =
+                getIcons(R.array.tethering_notification_icons, res);
 
         final int iconId = downstreamIcons.get(mDownstreamTypesMask, NO_ICON_ID);
         if (iconId == NO_ICON_ID) return NO_NOTIFY;
@@ -170,12 +216,12 @@
         final String title = res.getString(R.string.tethering_notification_title);
         final String message = res.getString(R.string.tethering_notification_message);
 
-        showNotification(iconId, title, message);
+        showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID);
         return NOTIFY_DONE;
     }
 
     private void showNotification(@DrawableRes final int iconId, @NonNull final String title,
-            @NonNull final String message) {
+            @NonNull final String message, @NotificationId final int id) {
         final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS);
         final PendingIntent pi = PendingIntent.getActivity(
                 mContext.createContextAsUser(UserHandle.CURRENT, 0),
@@ -193,6 +239,6 @@
                         .setContentIntent(pi)
                         .build();
 
-        mNotificationManager.notify(null /* tag */, NOTIFY_ID, notification);
+        mNotificationManager.notify(null /* tag */, id, notification);
     }
 }
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index c5329d8..c30be25 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -80,6 +80,7 @@
         mContext = mDeps.getContext();
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         mTethering = makeTethering(mDeps);
+        mTethering.startStateMachineUpdaters();
     }
 
     /**
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt
new file mode 100644
index 0000000..b869491
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2020 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.connectivity.tethering
+
+import android.app.Notification
+import android.app.NotificationManager
+import android.content.Context
+import android.content.res.Resources
+import android.net.ConnectivityManager.TETHERING_BLUETOOTH
+import android.net.ConnectivityManager.TETHERING_USB
+import android.net.ConnectivityManager.TETHERING_WIFI
+import android.os.UserHandle
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.internal.util.test.BroadcastInterceptingContext
+import com.android.networkstack.tethering.R
+import com.android.server.connectivity.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+const val TEST_SUBID = 1
+const val WIFI_ICON_ID = 1
+const val USB_ICON_ID = 2
+const val BT_ICON_ID = 3
+const val GENERAL_ICON_ID = 4
+const val WIFI_MASK = 1 shl TETHERING_WIFI
+const val USB_MASK = 1 shl TETHERING_USB
+const val BT_MASK = 1 shl TETHERING_BLUETOOTH
+const val TITTLE = "Tethering active"
+const val MESSAGE = "Tap here to set up."
+const val TEST_TITTLE = "Hotspot active"
+const val TEST_MESSAGE = "Tap to set up hotspot."
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class TetheringNotificationUpdaterTest {
+    // lateinit used here for mocks as they need to be reinitialized between each test and the test
+    // should crash if they are used before being initialized.
+    @Mock private lateinit var mockContext: Context
+    @Mock private lateinit var notificationManager: NotificationManager
+    @Mock private lateinit var defaultResources: Resources
+    @Mock private lateinit var testResources: Resources
+
+    // lateinit for this class under test, as it should be reset to a different instance for every
+    // tests but should always be initialized before use (or the test should crash).
+    private lateinit var notificationUpdater: TetheringNotificationUpdater
+
+    private val ENABLE_ICON_CONFIGS = arrayOf(
+            "USB;android.test:drawable/usb", "BT;android.test:drawable/bluetooth",
+            "WIFI|BT;android.test:drawable/general", "WIFI|USB;android.test:drawable/general",
+            "USB|BT;android.test:drawable/general", "WIFI|USB|BT;android.test:drawable/general")
+
+    private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) {
+        override fun createContextAsUser(user: UserHandle, flags: Int) =
+                if (user == UserHandle.ALL) mockContext else this
+    }
+
+    private inner class WrappedNotificationUpdater(c: Context) : TetheringNotificationUpdater(c) {
+        override fun getResourcesForSubId(context: Context, subId: Int) =
+                if (subId == TEST_SUBID) testResources else defaultResources
+    }
+
+    private fun setupResources() {
+        doReturn(ENABLE_ICON_CONFIGS).`when`(defaultResources)
+                .getStringArray(R.array.tethering_notification_icons)
+        doReturn(arrayOf("WIFI;android.test:drawable/wifi")).`when`(testResources)
+                .getStringArray(R.array.tethering_notification_icons)
+        doReturn(TITTLE).`when`(defaultResources).getString(R.string.tethering_notification_title)
+        doReturn(MESSAGE).`when`(defaultResources)
+                .getString(R.string.tethering_notification_message)
+        doReturn(TEST_TITTLE).`when`(testResources).getString(R.string.tethering_notification_title)
+        doReturn(TEST_MESSAGE).`when`(testResources)
+                .getString(R.string.tethering_notification_message)
+        doReturn(USB_ICON_ID).`when`(defaultResources)
+                .getIdentifier(eq("android.test:drawable/usb"), any(), any())
+        doReturn(BT_ICON_ID).`when`(defaultResources)
+                .getIdentifier(eq("android.test:drawable/bluetooth"), any(), any())
+        doReturn(GENERAL_ICON_ID).`when`(defaultResources)
+                .getIdentifier(eq("android.test:drawable/general"), any(), any())
+        doReturn(WIFI_ICON_ID).`when`(testResources)
+                .getIdentifier(eq("android.test:drawable/wifi"), any(), any())
+    }
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        val context = TestContext(InstrumentationRegistry.getInstrumentation().context)
+        doReturn(notificationManager).`when`(mockContext)
+                .getSystemService(Context.NOTIFICATION_SERVICE)
+        notificationUpdater = WrappedNotificationUpdater(context)
+        setupResources()
+    }
+
+    private fun Notification.title() = this.extras.getString(Notification.EXTRA_TITLE)
+    private fun Notification.text() = this.extras.getString(Notification.EXTRA_TEXT)
+
+    private fun verifyNotification(iconId: Int = 0, title: String = "", text: String = "") {
+        verify(notificationManager, never()).cancel(any(), anyInt())
+
+        val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java)
+        verify(notificationManager, times(1))
+                .notify(any(), anyInt(), notificationCaptor.capture())
+
+        val notification = notificationCaptor.getValue()
+        assertEquals(iconId, notification.smallIcon.resId)
+        assertEquals(title, notification.title())
+        assertEquals(text, notification.text())
+
+        reset(notificationManager)
+    }
+
+    private fun verifyNoNotification() {
+        verify(notificationManager, times(1)).cancel(any(), anyInt())
+        verify(notificationManager, never()).notify(any(), anyInt(), any())
+
+        reset(notificationManager)
+    }
+
+    @Test
+    fun testNotificationWithDownstreamChanged() {
+        // Wifi downstream. No notification.
+        notificationUpdater.onDownstreamChanged(WIFI_MASK)
+        verifyNoNotification()
+
+        // Same downstream changed. Nothing happened.
+        notificationUpdater.onDownstreamChanged(WIFI_MASK)
+        verifyZeroInteractions(notificationManager)
+
+        // Wifi and usb downstreams. Show enable notification
+        notificationUpdater.onDownstreamChanged(WIFI_MASK or USB_MASK)
+        verifyNotification(GENERAL_ICON_ID, TITTLE, MESSAGE)
+
+        // Usb downstream. Still show enable notification.
+        notificationUpdater.onDownstreamChanged(USB_MASK)
+        verifyNotification(USB_ICON_ID, TITTLE, MESSAGE)
+
+        // No downstream. No notification.
+        notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
+        verifyNoNotification()
+    }
+
+    @Test
+    fun testNotificationWithActiveDataSubscriptionIdChanged() {
+        // Usb downstream. Showed enable notification with default resource.
+        notificationUpdater.onDownstreamChanged(USB_MASK)
+        verifyNotification(USB_ICON_ID, TITTLE, MESSAGE)
+
+        // Same subId changed. Nothing happened.
+        notificationUpdater.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID)
+        verifyZeroInteractions(notificationManager)
+
+        // Set test sub id. Clear notification with test resource.
+        notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
+        verifyNoNotification()
+
+        // Wifi downstream. Show enable notification with test resource.
+        notificationUpdater.onDownstreamChanged(WIFI_MASK)
+        verifyNotification(WIFI_ICON_ID, TEST_TITTLE, TEST_MESSAGE)
+
+        // No downstream. No notification.
+        notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
+        verifyNoNotification()
+    }
+
+    private fun assertIconNumbers(number: Int, configs: Array<String?>) {
+        doReturn(configs).`when`(defaultResources)
+                .getStringArray(R.array.tethering_notification_icons)
+        assertEquals(number, notificationUpdater.getIcons(
+                R.array.tethering_notification_icons, defaultResources).size())
+    }
+
+    @Test
+    fun testGetIcons() {
+        assertIconNumbers(0, arrayOfNulls<String>(0))
+        assertIconNumbers(0, arrayOf(null, ""))
+        assertIconNumbers(3, arrayOf(
+                // These configurations are invalid with wrong strings or symbols.
+                ";", ",", "|", "|,;", "WIFI", "1;2", " U SB; ", "bt;", "WIFI;USB;BT", "WIFI|USB|BT",
+                "WIFI,BT,USB", " WIFI| |  | USB, test:drawable/test",
+                // This configuration is valid with two downstream types (USB, BT).
+                "USB|,,,,,|BT;drawable/test ",
+                // This configuration is valid with one downstream types (WIFI).
+                "     WIFI     ; android.test:drawable/xxx "))
+    }
+
+    @Test
+    fun testGetDownstreamTypesMask() {
+        assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask(""))
+        assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("1"))
+        assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("WIFI_P2P"))
+        assertEquals(DOWNSTREAM_NONE, notificationUpdater.getDownstreamTypesMask("usb"))
+        assertEquals(WIFI_MASK, notificationUpdater.getDownstreamTypesMask(" WIFI "))
+        assertEquals(USB_MASK, notificationUpdater.getDownstreamTypesMask("USB | B T"))
+        assertEquals(BT_MASK, notificationUpdater.getDownstreamTypesMask(" WIFI: | BT"))
+        assertEquals(WIFI_MASK or USB_MASK,
+                notificationUpdater.getDownstreamTypesMask("1|2|USB|WIFI|BLUETOOTH||"))
+    }
+
+    @Test
+    fun testSetupRestrictedNotification() {
+        val title = InstrumentationRegistry.getInstrumentation().context.resources
+                .getString(R.string.disable_tether_notification_title)
+        val message = InstrumentationRegistry.getInstrumentation().context.resources
+                .getString(R.string.disable_tether_notification_message)
+        val disallowTitle = "Tether function is disallowed"
+        val disallowMessage = "Please contact your admin"
+        doReturn(title).`when`(defaultResources)
+                .getString(R.string.disable_tether_notification_title)
+        doReturn(message).`when`(defaultResources)
+                .getString(R.string.disable_tether_notification_message)
+        doReturn(disallowTitle).`when`(testResources)
+                .getString(R.string.disable_tether_notification_title)
+        doReturn(disallowMessage).`when`(testResources)
+                .getString(R.string.disable_tether_notification_message)
+
+        // User restrictions on. Show restricted notification.
+        notificationUpdater.notifyTetheringDisabledByRestriction()
+        verifyNotification(R.drawable.stat_sys_tether_general, title, message)
+
+        // User restrictions off. Clear notification.
+        notificationUpdater.tetheringRestrictionLifted()
+        verifyNoNotification()
+
+        // Set test sub id. No notification.
+        notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
+        verifyNoNotification()
+
+        // User restrictions on again. Show restricted notification with test resource.
+        notificationUpdater.notifyTetheringDisabledByRestriction()
+        verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage)
+    }
+}
\ No newline at end of file
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index 60d7ad1..a59c6fd 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -210,7 +210,6 @@
     private PhoneStateListener mPhoneStateListener;
     private InterfaceConfigurationParcel mInterfaceConfiguration;
 
-
     private class TestContext extends BroadcastInterceptingContext {
         TestContext(Context base) {
             super(base);
@@ -485,6 +484,7 @@
         mServiceContext.registerReceiver(mBroadcastReceiver,
                 new IntentFilter(ACTION_TETHER_STATE_CHANGED));
         mTethering = makeTethering();
+        mTethering.startStateMachineUpdaters();
         verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any());
         verify(mNetd).registerUnsolicitedEventListener(any());
         final ArgumentCaptor<PhoneStateListener> phoneListenerCaptor =
@@ -1073,13 +1073,15 @@
         when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions);
 
         final Tethering.UserRestrictionActionListener ural =
-                new Tethering.UserRestrictionActionListener(mUserManager, mockTethering);
+                new Tethering.UserRestrictionActionListener(
+                        mUserManager, mockTethering, mNotificationUpdater);
         ural.mDisallowTethering = currentDisallow;
 
         ural.onUserRestrictionsChanged();
 
-        verify(mockTethering, times(expectedInteractionsWithShowNotification))
-                .untetherAll();
+        verify(mNotificationUpdater, times(expectedInteractionsWithShowNotification))
+                .notifyTetheringDisabledByRestriction();
+        verify(mockTethering, times(expectedInteractionsWithShowNotification)).untetherAll();
     }
 
     @Test
@@ -1087,7 +1089,7 @@
         final String[] emptyActiveIfacesList = new String[]{};
         final boolean currDisallow = false;
         final boolean nextDisallow = true;
-        final int expectedInteractionsWithShowNotification = 0;
+        final int expectedInteractionsWithShowNotification = 1;
 
         runUserRestrictionsChange(currDisallow, nextDisallow, emptyActiveIfacesList,
                 expectedInteractionsWithShowNotification);
@@ -1399,6 +1401,7 @@
         mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId);
         final TetheringConfiguration newConfig = mTethering.getTetheringConfiguration();
         assertEquals(fakeSubId, newConfig.activeDataSubId);
+        verify(mNotificationUpdater, times(1)).onActiveDataSubscriptionIdChanged(eq(fakeSubId));
     }
 
     @Test
diff --git a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
index 6112da5..fe9f60f 100644
--- a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
+++ b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
@@ -37,7 +37,6 @@
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.util.Log;
-import android.util.Size;
 import android.view.Display;
 import android.view.View;
 import android.widget.Toast;
@@ -358,8 +357,8 @@
         // Get the crop
         boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
 
-        Size windowSize = getWindowManager().getCurrentWindowMetrics().getSize();
-        boolean isPortrait = windowSize.getWidth() < windowSize.getHeight();
+        Rect windowBounds = getWindowManager().getCurrentWindowMetrics().getBounds();
+        boolean isPortrait = windowBounds.width() < windowBounds.height();
 
         Point defaultWallpaperSize = getDefaultWallpaperSize(getResources(),
                 getDisplay());
diff --git a/services/Android.bp b/services/Android.bp
index 490481c..1ce7dcf 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -126,6 +126,7 @@
         " --hide DeprecationMismatch" +
         " --hide HiddenTypedefConstant",
     visibility: ["//visibility:private"],
+    filter_packages: ["com.android."],
     check_api: {
         current: {
             api_file: "api/current.txt",
@@ -142,6 +143,11 @@
             baseline_file: "api/lint-baseline.txt",
         },
     },
+    dist: {
+        targets: ["sdk", "win_sdk"],
+        dir: "apistubs/android/system-server/api",
+        dest: "android.txt",
+    },
 }
 
 java_library {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 60c3d78..1b180e3 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2001,17 +2001,6 @@
                 Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
                 userState.mUserId, currentTargets, str -> str);
         scheduleNotifyClientsOfServicesStateChangeLocked(userState);
-
-        // Disable accessibility shortcut key if there's no shortcut installed.
-        if (currentTargets.isEmpty()) {
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                        Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 0, userState.mUserId);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
     }
 
     private boolean canRequestAndRequestsTouchExplorationLocked(
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index e49c1ed..c6a54fc 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -50,6 +50,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.DeviceConfig;
@@ -63,6 +64,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.util.TimeUtils;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillManager.SmartSuggestionMode;
@@ -151,6 +153,7 @@
     private final LocalLog mWtfHistory = new LocalLog(50);
 
     private final AutofillCompatState mAutofillCompatState = new AutofillCompatState();
+    private final DisabledInfoCache mDisabledInfoCache = new DisabledInfoCache();
 
     private final LocalService mLocalService = new LocalService();
     private final ActivityManagerInternal mAm;
@@ -302,14 +305,15 @@
     @Override // from AbstractMasterSystemService
     protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId,
             boolean disabled) {
-        return new AutofillManagerServiceImpl(this, mLock, mUiLatencyHistory,
-                mWtfHistory, resolvedUserId, mUi, mAutofillCompatState, disabled);
+        return new AutofillManagerServiceImpl(this, mLock, mUiLatencyHistory, mWtfHistory,
+                resolvedUserId, mUi, mAutofillCompatState, disabled, mDisabledInfoCache);
     }
 
     @Override // AbstractMasterSystemService
     protected void onServiceRemoved(@NonNull AutofillManagerServiceImpl service,
             @UserIdInt int userId) {
         service.destroyLocked();
+        mDisabledInfoCache.remove(userId);
         mAutofillCompatState.removeCompatibilityModeRequests(userId);
     }
 
@@ -835,15 +839,10 @@
 
         private void injectDisableAppInfo(@NonNull AutofillOptions options, int userId,
                 String packageName) {
-            synchronized (mLock) {
-                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
-                if (service != null) {
-                    options.appDisabledExpiration = service.getAppDisabledExpirationLocked(
-                            packageName);
-                    options.disabledActivities = service.getAppDisabledActivitiesLocked(
-                            packageName);
-                }
-            }
+            options.appDisabledExpiration =
+                    mDisabledInfoCache.getAppDisabledExpiration(userId, packageName);
+            options.disabledActivities =
+                    mDisabledInfoCache.getAppDisabledActivities(userId, packageName);
         }
     }
 
@@ -867,6 +866,234 @@
     }
 
     /**
+     * Stores autofill disable information, i.e. {@link AutofillDisabledInfo},  keyed by user id.
+     * The information is cleaned up when the service is removed.
+     */
+    static final class DisabledInfoCache {
+
+        private final Object mLock = new Object();
+
+        @GuardedBy("mLock")
+        private final SparseArray<AutofillDisabledInfo> mCache = new SparseArray<>();
+
+        void remove(@UserIdInt int userId) {
+            synchronized (mLock) {
+                mCache.remove(userId);
+            }
+        }
+
+        void addDisabledAppLocked(@UserIdInt int userId, @NonNull String packageName,
+                long expiration) {
+            Preconditions.checkNotNull(packageName);
+            synchronized (mLock) {
+                AutofillDisabledInfo info =
+                        getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
+                info.putDisableAppsLocked(packageName, expiration);
+            }
+        }
+
+        void addDisabledActivityLocked(@UserIdInt int userId, @NonNull ComponentName componentName,
+                long expiration) {
+            Preconditions.checkNotNull(componentName);
+            synchronized (mLock) {
+                AutofillDisabledInfo info =
+                        getOrCreateAutofillDisabledInfoByUserIdLocked(userId);
+                info.putDisableActivityLocked(componentName, expiration);
+            }
+        }
+
+        boolean isAutofillDisabledLocked(@UserIdInt int userId,
+                @NonNull ComponentName componentName) {
+            Preconditions.checkNotNull(componentName);
+            final boolean disabled;
+            synchronized (mLock) {
+                final AutofillDisabledInfo info = mCache.get(userId);
+                disabled = info != null ? info.isAutofillDisabledLocked(componentName) : false;
+            }
+            return disabled;
+        }
+
+        long getAppDisabledExpiration(@UserIdInt int userId, @NonNull String packageName) {
+            Preconditions.checkNotNull(packageName);
+            final Long expiration;
+            synchronized (mLock) {
+                final AutofillDisabledInfo info = mCache.get(userId);
+                expiration = info != null ? info.getAppDisabledExpirationLocked(packageName) : 0;
+            }
+            return expiration;
+        }
+
+        @Nullable
+        ArrayMap<String, Long> getAppDisabledActivities(@UserIdInt int userId,
+                @NonNull String packageName) {
+            Preconditions.checkNotNull(packageName);
+            final ArrayMap<String, Long> disabledList;
+            synchronized (mLock) {
+                final AutofillDisabledInfo info = mCache.get(userId);
+                disabledList =
+                        info != null ? info.getAppDisabledActivitiesLocked(packageName) : null;
+            }
+            return disabledList;
+        }
+
+        void dump(@UserIdInt int userId, String prefix, PrintWriter pw) {
+            synchronized (mLock) {
+                final AutofillDisabledInfo info = mCache.get(userId);
+                if (info != null) {
+                    info.dumpLocked(prefix, pw);
+                }
+            }
+        }
+
+        @NonNull
+        private AutofillDisabledInfo getOrCreateAutofillDisabledInfoByUserIdLocked(
+                @UserIdInt int userId) {
+            AutofillDisabledInfo info = mCache.get(userId);
+            if (info == null) {
+                info = new AutofillDisabledInfo();
+                mCache.put(userId, info);
+            }
+            return info;
+        }
+    }
+
+    /**
+     * The autofill disable information.
+     * <p>
+     * This contains disable information set by the AutofillService, e.g. disabled application
+     * expiration, disable activity expiration.
+     */
+    private static final class AutofillDisabledInfo {
+        /**
+         * Apps disabled by the service; key is package name, value is when they will be enabled
+         * again.
+         */
+        private ArrayMap<String, Long> mDisabledApps;
+        /**
+         * Activities disabled by the service; key is component name, value is when they will be
+         * enabled again.
+         */
+        private ArrayMap<ComponentName, Long> mDisabledActivities;
+
+        void putDisableAppsLocked(@NonNull String packageName, long expiration) {
+            if (mDisabledApps == null) {
+                mDisabledApps = new ArrayMap<>(1);
+            }
+            mDisabledApps.put(packageName, expiration);
+        }
+
+        void putDisableActivityLocked(@NonNull ComponentName componentName, long expiration) {
+            if (mDisabledActivities == null) {
+                mDisabledActivities = new ArrayMap<>(1);
+            }
+            mDisabledActivities.put(componentName, expiration);
+        }
+
+        long getAppDisabledExpirationLocked(@NonNull String packageName) {
+            if (mDisabledApps == null) {
+                return 0;
+            }
+            final Long expiration = mDisabledApps.get(packageName);
+            return expiration != null ? expiration : 0;
+        }
+
+        ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) {
+            if (mDisabledActivities != null) {
+                final int size = mDisabledActivities.size();
+                ArrayMap<String, Long> disabledList = null;
+                for (int i = 0; i < size; i++) {
+                    final ComponentName component = mDisabledActivities.keyAt(i);
+                    if (packageName.equals(component.getPackageName())) {
+                        if (disabledList == null) {
+                            disabledList = new ArrayMap<>();
+                        }
+                        final long expiration = mDisabledActivities.valueAt(i);
+                        disabledList.put(component.flattenToShortString(), expiration);
+                    }
+                }
+                return disabledList;
+            }
+            return null;
+        }
+
+        boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
+            // Check activities first.
+            long elapsedTime = 0;
+            if (mDisabledActivities != null) {
+                elapsedTime = SystemClock.elapsedRealtime();
+                final Long expiration = mDisabledActivities.get(componentName);
+                if (expiration != null) {
+                    if (expiration >= elapsedTime) return true;
+                    // Restriction expired - clean it up.
+                    if (sVerbose) {
+                        Slog.v(TAG, "Removing " + componentName.toShortString()
+                                + " from disabled list");
+                    }
+                    mDisabledActivities.remove(componentName);
+                }
+            }
+
+            // Then check apps.
+            final String packageName = componentName.getPackageName();
+            if (mDisabledApps == null) return false;
+
+            final Long expiration = mDisabledApps.get(packageName);
+            if (expiration == null) return false;
+
+            if (elapsedTime == 0) {
+                elapsedTime = SystemClock.elapsedRealtime();
+            }
+
+            if (expiration >= elapsedTime) return true;
+
+            // Restriction expired - clean it up.
+            if (sVerbose)  Slog.v(TAG, "Removing " + packageName + " from disabled list");
+            mDisabledApps.remove(packageName);
+            return false;
+        }
+
+        void dumpLocked(String prefix, PrintWriter pw) {
+            pw.print(prefix); pw.print("Disabled apps: ");
+            if (mDisabledApps == null) {
+                pw.println("N/A");
+            } else {
+                final int size = mDisabledApps.size();
+                pw.println(size);
+                final StringBuilder builder = new StringBuilder();
+                final long now = SystemClock.elapsedRealtime();
+                for (int i = 0; i < size; i++) {
+                    final String packageName = mDisabledApps.keyAt(i);
+                    final long expiration = mDisabledApps.valueAt(i);
+                    builder.append(prefix).append(prefix)
+                            .append(i).append(". ").append(packageName).append(": ");
+                    TimeUtils.formatDuration((expiration - now), builder);
+                    builder.append('\n');
+                }
+                pw.println(builder);
+            }
+
+            pw.print(prefix); pw.print("Disabled activities: ");
+            if (mDisabledActivities == null) {
+                pw.println("N/A");
+            } else {
+                final int size = mDisabledActivities.size();
+                pw.println(size);
+                final StringBuilder builder = new StringBuilder();
+                final long now = SystemClock.elapsedRealtime();
+                for (int i = 0; i < size; i++) {
+                    final ComponentName component = mDisabledActivities.keyAt(i);
+                    final long expiration = mDisabledActivities.valueAt(i);
+                    builder.append(prefix).append(prefix)
+                            .append(i).append(". ").append(component).append(": ");
+                    TimeUtils.formatDuration((expiration - now), builder);
+                    builder.append('\n');
+                }
+                pw.println(builder);
+            }
+        }
+    }
+
+    /**
      * Compatibility mode metadata associated with all services.
      *
      * <p>This object is defined here instead of on each {@link AutofillManagerServiceImpl} because
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 6fbe141..d1805d9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -67,7 +67,6 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.TimeUtils;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillManager.SmartSuggestionMode;
@@ -80,6 +79,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.server.LocalServices;
 import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
+import com.android.server.autofill.AutofillManagerService.DisabledInfoCache;
 import com.android.server.autofill.RemoteAugmentedAutofillService.RemoteAugmentedAutofillServiceCallbacks;
 import com.android.server.autofill.ui.AutoFillUI;
 import com.android.server.contentcapture.ContentCaptureManagerInternal;
@@ -90,7 +90,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
-
 /**
  * Bridge between the {@code system_server}'s {@link AutofillManagerService} and the
  * app's {@link IAutoFillService} implementation.
@@ -125,19 +124,6 @@
     private RemoteInlineSuggestionRenderService mRemoteInlineSuggestionRenderService;
 
     /**
-     * Apps disabled by the service; key is package name, value is when they will be enabled again.
-     */
-    @GuardedBy("mLock")
-    private ArrayMap<String, Long> mDisabledApps;
-
-    /**
-     * Activities disabled by the service; key is component name, value is when they will be enabled
-     * again.
-     */
-    @GuardedBy("mLock")
-    private ArrayMap<ComponentName, Long> mDisabledActivities;
-
-    /**
      * Data used for field classification.
      */
     @GuardedBy("mLock")
@@ -186,10 +172,12 @@
 
     private final ContentCaptureManagerInternal mContentCaptureManagerInternal;
 
+    private final DisabledInfoCache mDisabledInfoCache;
+
     AutofillManagerServiceImpl(AutofillManagerService master, Object lock,
             LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
             AutofillCompatState autofillCompatState,
-            boolean disabled) {
+            boolean disabled, DisabledInfoCache disableCache) {
         super(master, lock, userId);
 
         mUiLatencyHistory = uiLatencyHistory;
@@ -200,7 +188,7 @@
         mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
         mContentCaptureManagerInternal = LocalServices.getService(
                 ContentCaptureManagerInternal.class);
-
+        mDisabledInfoCache = disableCache;
         updateLocked(disabled);
     }
 
@@ -1045,45 +1033,7 @@
         pw.println(isInlineSuggestionsEnabled());
         pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
 
-        pw.print(prefix); pw.print("Disabled apps: ");
-
-        if (mDisabledApps == null) {
-            pw.println("N/A");
-        } else {
-            final int size = mDisabledApps.size();
-            pw.println(size);
-            final StringBuilder builder = new StringBuilder();
-            final long now = SystemClock.elapsedRealtime();
-            for (int i = 0; i < size; i++) {
-                final String packageName = mDisabledApps.keyAt(i);
-                final long expiration = mDisabledApps.valueAt(i);
-                builder.append(prefix).append(prefix)
-                        .append(i).append(". ").append(packageName).append(": ");
-                TimeUtils.formatDuration((expiration - now), builder);
-                builder.append('\n');
-            }
-            pw.println(builder);
-        }
-
-        pw.print(prefix); pw.print("Disabled activities: ");
-
-        if (mDisabledActivities == null) {
-            pw.println("N/A");
-        } else {
-            final int size = mDisabledActivities.size();
-            pw.println(size);
-            final StringBuilder builder = new StringBuilder();
-            final long now = SystemClock.elapsedRealtime();
-            for (int i = 0; i < size; i++) {
-                final ComponentName component = mDisabledActivities.keyAt(i);
-                final long expiration = mDisabledActivities.valueAt(i);
-                builder.append(prefix).append(prefix)
-                        .append(i).append(". ").append(component).append(": ");
-                TimeUtils.formatDuration((expiration - now), builder);
-                builder.append('\n');
-            }
-            pw.println(builder);
-        }
+        mDisabledInfoCache.dump(mUserId, prefix, pw);
 
         final int size = mSessions.size();
         if (size == 0) {
@@ -1480,15 +1430,13 @@
     void disableAutofillForApp(@NonNull String packageName, long duration, int sessionId,
             boolean compatMode) {
         synchronized (mLock) {
-            if (mDisabledApps == null) {
-                mDisabledApps = new ArrayMap<>(1);
-            }
             long expiration = SystemClock.elapsedRealtime() + duration;
             // Protect it against overflow
             if (expiration < 0) {
                 expiration = Long.MAX_VALUE;
             }
-            mDisabledApps.put(packageName, expiration);
+            mDisabledInfoCache.addDisabledAppLocked(mUserId, packageName, expiration);
+
             int intDuration = duration > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) duration;
             mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_SERVICE_DISABLED_APP,
                     packageName, getServicePackageName(), sessionId, compatMode)
@@ -1502,15 +1450,12 @@
     void disableAutofillForActivity(@NonNull ComponentName componentName, long duration,
             int sessionId, boolean compatMode) {
         synchronized (mLock) {
-            if (mDisabledActivities == null) {
-                mDisabledActivities = new ArrayMap<>(1);
-            }
             long expiration = SystemClock.elapsedRealtime() + duration;
             // Protect it against overflow
             if (expiration < 0) {
                 expiration = Long.MAX_VALUE;
             }
-            mDisabledActivities.put(componentName, expiration);
+            mDisabledInfoCache.addDisabledActivityLocked(mUserId, componentName, expiration);
             final int intDuration = duration > Integer.MAX_VALUE
                     ? Integer.MAX_VALUE
                     : (int) duration;
@@ -1528,74 +1473,12 @@
         }
     }
 
-    // Called by AutofillManagerService
-    long getAppDisabledExpirationLocked(@NonNull String packageName) {
-        if (mDisabledApps == null) {
-            return 0;
-        }
-        final Long expiration = mDisabledApps.get(packageName);
-        return expiration != null ? expiration : 0;
-    }
-
-    // Called by AutofillManagerService
-    @Nullable
-    ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) {
-        if (mDisabledActivities != null) {
-            final int size = mDisabledActivities.size();
-            ArrayMap<String, Long> disabledList = null;
-            for (int i = 0; i < size; i++) {
-                final ComponentName component = mDisabledActivities.keyAt(i);
-                if (packageName.equals(component.getPackageName())) {
-                    if (disabledList == null) {
-                        disabledList = new ArrayMap<>();
-                    }
-                    final long expiration = mDisabledActivities.valueAt(i);
-                    disabledList.put(component.flattenToShortString(), expiration);
-                }
-            }
-            return disabledList;
-        }
-        return null;
-    }
-
     /**
      * Checks if autofill is disabled by service to the given activity.
      */
     @GuardedBy("mLock")
     private boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
-        // Check activities first.
-        long elapsedTime = 0;
-        if (mDisabledActivities != null) {
-            elapsedTime = SystemClock.elapsedRealtime();
-            final Long expiration = mDisabledActivities.get(componentName);
-            if (expiration != null) {
-                if (expiration >= elapsedTime) return true;
-                // Restriction expired - clean it up.
-                if (sVerbose) {
-                    Slog.v(TAG, "Removing " + componentName.toShortString()
-                        + " from disabled list");
-                }
-                mDisabledActivities.remove(componentName);
-            }
-        }
-
-        // Then check apps.
-        final String packageName = componentName.getPackageName();
-        if (mDisabledApps == null) return false;
-
-        final Long expiration = mDisabledApps.get(packageName);
-        if (expiration == null) return false;
-
-        if (elapsedTime == 0) {
-            elapsedTime = SystemClock.elapsedRealtime();
-        }
-
-        if (expiration >= elapsedTime) return true;
-
-        // Restriction expired - clean it up.
-        if (sVerbose)  Slog.v(TAG, "Removing " + packageName + " from disabled list");
-        mDisabledApps.remove(packageName);
-        return false;
+        return mDisabledInfoCache.isAutofillDisabledLocked(mUserId, componentName);
     }
 
     // Called by AutofillManager, checks UID.
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
index 5de8171..4ba2c3d 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
@@ -199,7 +199,10 @@
             return false;
         }
 
-        if (!mImeInputViewStarted || !autofillId.equalsIgnoreSession(mImeFieldId)) {
+        // TODO(b/151846600): IME doesn't have access to the virtual id of the webview, so we
+        //  only compare the view id for now.
+        if (!mImeInputViewStarted || mImeFieldId == null
+                || autofillId.getViewId() != mImeFieldId.getViewId()) {
             if (sDebug) {
                 Log.d(TAG,
                         "onInlineSuggestionsResponseLocked not sent because input view is not "
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index c39e93a..2306329 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -165,7 +165,8 @@
                 Slog.w(TAG, "InlinePresentation not found in dataset");
                 continue;
             }
-            if (!includeDataset(dataset, fieldIndex, filterText)) {
+            if (!inlinePresentation.isPinned()  // don't filter pinned suggestions
+                    && !includeDataset(dataset, fieldIndex, filterText)) {
                 continue;
             }
             InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset,
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 32bca35..9486b0d 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -17,7 +17,7 @@
 package com.android.server.contentcapture;
 
 import static android.service.contentcapture.ContentCaptureService.setClientState;
-import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID;
+import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID;
 import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED;
 import static android.view.contentcapture.ContentCaptureSession.STATE_DUPLICATED_ID;
 import static android.view.contentcapture.ContentCaptureSession.STATE_INTERNAL_ERROR;
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index aa63e40..06ab426 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -16,9 +16,9 @@
 package com.android.server.contentcapture;
 
 import static android.service.contentcapture.ContentCaptureService.setClientState;
+import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID;
 import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
 import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_TRUE;
-import static android.view.contentcapture.ContentCaptureSession.NO_SESSION_ID;
 import static android.view.contentcapture.ContentCaptureSession.STATE_ACTIVE;
 import static android.view.contentcapture.ContentCaptureSession.STATE_DISABLED;
 import static android.view.contentcapture.ContentCaptureSession.STATE_SERVICE_RESURRECTED;
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
index b54ec4e..0fdabd0 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsManagerService.java
@@ -21,13 +21,17 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.contentsuggestions.ClassificationsRequest;
+import android.app.contentsuggestions.ContentSuggestionsManager;
 import android.app.contentsuggestions.IClassificationsCallback;
 import android.app.contentsuggestions.IContentSuggestionsManager;
 import android.app.contentsuggestions.ISelectionsCallback;
 import android.app.contentsuggestions.SelectionsRequest;
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.graphics.GraphicBuffer;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -133,7 +137,7 @@
                 if (service != null) {
                     // TODO(b/147324195): Temporarily pass bitmap until we change the service API.
                     imageContextRequestExtras.putParcelable(EXTRA_BITMAP, bitmap);
-                    service.provideContextImageLocked(/* taskId = */ -1, imageContextRequestExtras);
+                    service.provideContextImageFromBitmapLocked(imageContextRequestExtras);
                 } else {
                     if (VERBOSE) {
                         Slog.v(TAG, "provideContextImageLocked: no service for " + userId);
@@ -152,10 +156,28 @@
             }
             enforceCaller(UserHandle.getCallingUserId(), "provideContextImage");
 
+            GraphicBuffer snapshotBuffer = null;
+            int colorSpaceId = 0;
+
+            // Skip taking TaskSnapshot when bitmap is provided.
+            if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) {
+                // Can block, so call before acquiring the lock.
+                ActivityManager.TaskSnapshot snapshot =
+                        mActivityTaskManagerInternal.getTaskSnapshotBlocking(taskId, false);
+                if (snapshot != null) {
+                    snapshotBuffer = snapshot.getSnapshot();
+                    ColorSpace colorSpace = snapshot.getColorSpace();
+                    if (colorSpace != null) {
+                        colorSpaceId = colorSpace.getId();
+                    }
+                }
+            }
+
             synchronized (mLock) {
                 final ContentSuggestionsPerUserService service = getServiceForUserLocked(userId);
                 if (service != null) {
-                    service.provideContextImageLocked(taskId, imageContextRequestExtras);
+                    service.provideContextImageLocked(taskId, snapshotBuffer, colorSpaceId,
+                            imageContextRequestExtras);
                 } else {
                     if (VERBOSE) {
                         Slog.v(TAG, "provideContextImageLocked: no service for " + userId);
diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
index 7828050..cf53b16 100644
--- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
+++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java
@@ -19,17 +19,14 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.contentsuggestions.ClassificationsRequest;
-import android.app.contentsuggestions.ContentSuggestionsManager;
 import android.app.contentsuggestions.IClassificationsCallback;
 import android.app.contentsuggestions.ISelectionsCallback;
 import android.app.contentsuggestions.SelectionsRequest;
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
-import android.graphics.ColorSpace;
 import android.graphics.GraphicBuffer;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -95,26 +92,17 @@
     }
 
     @GuardedBy("mLock")
-    void provideContextImageLocked(int taskId, @NonNull Bundle imageContextRequestExtras) {
+    void provideContextImageFromBitmapLocked(@NonNull Bundle bitmapContainingExtras) {
+        // No task or snapshot provided, the bitmap is contained in the extras
+        provideContextImageLocked(-1, null, 0, bitmapContainingExtras);
+    }
+
+    @GuardedBy("mLock")
+    void provideContextImageLocked(int taskId, @Nullable GraphicBuffer snapshot,
+            int colorSpaceIdForSnapshot, @NonNull Bundle imageContextRequestExtras) {
         RemoteContentSuggestionsService service = ensureRemoteServiceLocked();
         if (service != null) {
-            GraphicBuffer snapshotBuffer = null;
-            int colorSpaceId = 0;
-
-            // Skip taking TaskSnapshot when bitmap is provided.
-            if (!imageContextRequestExtras.containsKey(ContentSuggestionsManager.EXTRA_BITMAP)) {
-                ActivityManager.TaskSnapshot snapshot =
-                        mActivityTaskManagerInternal.getTaskSnapshotNoRestore(taskId, false);
-                if (snapshot != null) {
-                    snapshotBuffer = snapshot.getSnapshot();
-                    ColorSpace colorSpace = snapshot.getColorSpace();
-                    if (colorSpace != null) {
-                        colorSpaceId = colorSpace.getId();
-                    }
-                }
-            }
-
-            service.provideContextImage(taskId, snapshotBuffer, colorSpaceId,
+            service.provideContextImage(taskId, snapshot, colorSpaceIdForSnapshot,
                     imageContextRequestExtras);
         }
     }
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index f1f5005..8dd4fa6 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -1101,6 +1101,9 @@
          * Synchronize on BatteryService.
          */
         public void updateLightsLocked() {
+            if (mBatteryLight == null) {
+                return;
+            }
             final int level = mHealthInfo.batteryLevel;
             final int status = mHealthInfo.batteryStatus;
             if (level < mLowBatteryWarningLevel) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 03ca1c6..1bf559a 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -112,9 +112,13 @@
     private static final int USER_SWITCHED_TIME_MS = 200;
     // Delay for the addProxy function in msec
     private static final int ADD_PROXY_DELAY_MS = 100;
+    // Delay for retrying enable and disable in msec
+    private static final int ENABLE_DISABLE_DELAY_MS = 300;
 
     private static final int MESSAGE_ENABLE = 1;
     private static final int MESSAGE_DISABLE = 2;
+    private static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3;
+    private static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4;
     private static final int MESSAGE_REGISTER_ADAPTER = 20;
     private static final int MESSAGE_UNREGISTER_ADAPTER = 21;
     private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30;
@@ -136,6 +140,7 @@
     private static final int RESTORE_SETTING_TO_OFF = 0;
 
     private static final int MAX_ERROR_RESTART_RETRIES = 6;
+    private static final int MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES = 10;
 
     // Bluetooth persisted setting is off
     private static final int BLUETOOTH_OFF = 0;
@@ -166,6 +171,8 @@
     private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock();
     private boolean mBinding;
     private boolean mUnbinding;
+    private int mWaitForEnableRetry;
+    private int mWaitForDisableRetry;
 
     private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
 
@@ -1678,8 +1685,18 @@
                     break;
 
                 case MESSAGE_ENABLE:
+                    int quietEnable = msg.arg1;
+                    if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED)
+                            || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) {
+                        // We are handling enable or disable right now, wait for it.
+                        mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ENABLE,
+                                quietEnable, 0), ENABLE_DISABLE_DELAY_MS);
+                        break;
+                    }
+
                     if (DBG) {
-                        Slog.d(TAG, "MESSAGE_ENABLE(" + msg.arg1 + "): mBluetooth = " + mBluetooth);
+                        Slog.d(TAG, "MESSAGE_ENABLE(" + quietEnable + "): mBluetooth = "
+                                + mBluetooth);
                     }
                     mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                     mEnable = true;
@@ -1702,7 +1719,7 @@
                         mBluetoothLock.readLock().unlock();
                     }
 
-                    mQuietEnable = (msg.arg1 == 1);
+                    mQuietEnable = (quietEnable == 1);
                     if (mBluetooth == null) {
                         handleEnable(mQuietEnable);
                     } else {
@@ -1711,8 +1728,8 @@
                         // the previous Bluetooth process has exited. The
                         // waiting period has three components:
                         // (a) Wait until the local state is STATE_OFF. This
-                        //     is accomplished by
-                        //     "waitForState(Set.of(BluetoothAdapter.STATE_OFF))".
+                        //     is accomplished by sending delay a message
+                        //     MESSAGE_HANDLE_ENABLE_DELAYED
                         // (b) Wait until the STATE_OFF state is updated to
                         //     all components.
                         // (c) Wait until the Bluetooth process exits, and
@@ -1722,34 +1739,109 @@
                         // message. The delay time is backed off if Bluetooth
                         // continuously failed to turn on itself.
                         //
-                        waitForState(Set.of(BluetoothAdapter.STATE_OFF));
-                        Message restartMsg =
-                                mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
-                        mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs());
+                        mWaitForEnableRetry = 0;
+                        Message enableDelayedMsg =
+                                mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED);
+                        mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
                     }
                     break;
 
                 case MESSAGE_DISABLE:
+                    if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mBinding
+                            || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) {
+                        // We are handling enable or disable right now, wait for it.
+                        mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_DISABLE),
+                                ENABLE_DISABLE_DELAY_MS);
+                        break;
+                    }
+
                     if (DBG) {
-                        Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth);
+                        Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth
+                                + ", mBinding = " + mBinding);
                     }
                     mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
+
                     if (mEnable && mBluetooth != null) {
-                        waitForState(Set.of(BluetoothAdapter.STATE_ON));
-                        mEnable = false;
-                        handleDisable();
-                        waitForState(Set.of(BluetoothAdapter.STATE_OFF,
-                                BluetoothAdapter.STATE_TURNING_ON,
-                                BluetoothAdapter.STATE_TURNING_OFF,
-                                BluetoothAdapter.STATE_BLE_TURNING_ON,
-                                BluetoothAdapter.STATE_BLE_ON,
-                                BluetoothAdapter.STATE_BLE_TURNING_OFF));
+                        mWaitForDisableRetry = 0;
+                        Message disableDelayedMsg =
+                                mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0);
+                        mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
                     } else {
                         mEnable = false;
                         handleDisable();
                     }
                     break;
 
+                case MESSAGE_HANDLE_ENABLE_DELAYED: {
+                    // The Bluetooth is turning off, wait for STATE_OFF
+                    if (mState != BluetoothAdapter.STATE_OFF) {
+                        if (mWaitForEnableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) {
+                            mWaitForEnableRetry++;
+                            Message enableDelayedMsg =
+                                    mHandler.obtainMessage(MESSAGE_HANDLE_ENABLE_DELAYED);
+                            mHandler.sendMessageDelayed(enableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
+                            break;
+                        } else {
+                            Slog.e(TAG, "Wait for STATE_OFF timeout");
+                        }
+                    }
+                    // Either state is changed to STATE_OFF or reaches the maximum retry, we
+                    // should move forward to the next step.
+                    mWaitForEnableRetry = 0;
+                    Message restartMsg =
+                            mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
+                    mHandler.sendMessageDelayed(restartMsg, getServiceRestartMs());
+                    Slog.d(TAG, "Handle enable is finished");
+                    break;
+                }
+
+                case MESSAGE_HANDLE_DISABLE_DELAYED: {
+                    boolean disabling = (msg.arg1 == 1);
+                    Slog.d(TAG, "MESSAGE_HANDLE_DISABLE_DELAYED: disabling:" + disabling);
+                    if (!disabling) {
+                        // The Bluetooth is turning on, wait for STATE_ON
+                        if (mState != BluetoothAdapter.STATE_ON) {
+                            if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) {
+                                mWaitForDisableRetry++;
+                                Message disableDelayedMsg = mHandler.obtainMessage(
+                                        MESSAGE_HANDLE_DISABLE_DELAYED, 0, 0);
+                                mHandler.sendMessageDelayed(disableDelayedMsg,
+                                        ENABLE_DISABLE_DELAY_MS);
+                                break;
+                            } else {
+                                Slog.e(TAG, "Wait for STATE_ON timeout");
+                            }
+                        }
+                        // Either state is changed to STATE_ON or reaches the maximum retry, we
+                        // should move forward to the next step.
+                        mWaitForDisableRetry = 0;
+                        mEnable = false;
+                        handleDisable();
+                        // Wait for state exiting STATE_ON
+                        Message disableDelayedMsg =
+                                mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0);
+                        mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS);
+                    } else {
+                        // The Bluetooth is turning off, wait for exiting STATE_ON
+                        if (mState == BluetoothAdapter.STATE_ON) {
+                            if (mWaitForDisableRetry < MAX_WAIT_FOR_ENABLE_DISABLE_RETRIES) {
+                                mWaitForDisableRetry++;
+                                Message disableDelayedMsg = mHandler.obtainMessage(
+                                        MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0);
+                                mHandler.sendMessageDelayed(disableDelayedMsg,
+                                        ENABLE_DISABLE_DELAY_MS);
+                                break;
+                            } else {
+                                Slog.e(TAG, "Wait for exiting STATE_ON timeout");
+                            }
+                        }
+                        // Either state is exited from STATE_ON or reaches the maximum retry, we
+                        // should move forward to the next step.
+                        Slog.d(TAG, "Handle disable is finished");
+                    }
+                    break;
+                }
+
                 case MESSAGE_RESTORE_USER_SETTING:
                     if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) {
                         if (DBG) {
@@ -2124,6 +2216,7 @@
         try {
             mBluetoothLock.writeLock().lock();
             if ((mBluetooth == null) && (!mBinding)) {
+                Slog.d(TAG, "binding Bluetooth service");
                 //Start bind timeout and bind
                 Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
                 mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS);
@@ -2493,6 +2586,12 @@
             writer.println("  " + app.getPackageName());
         }
 
+        writer.println("\nBluetoothManagerService:");
+        writer.println("  mEnable:" + mEnable);
+        writer.println("  mQuietEnable:" + mQuietEnable);
+        writer.println("  mEnableExternal:" + mEnableExternal);
+        writer.println("  mQuietEnableExternal:" + mQuietEnableExternal);
+
         writer.println("");
         writer.flush();
         if (args.length == 0) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ec84ae7..237a961 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -40,6 +40,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkPolicyManager.RULE_NONE;
 import static android.net.NetworkPolicyManager.uidRulesToString;
@@ -50,6 +51,7 @@
 
 import static java.util.Map.Entry;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
@@ -2702,10 +2704,18 @@
 
             switch (msg.what) {
                 case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
-                    final NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
+                    NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
                     if (networkCapabilities.hasConnectivityManagedCapability()) {
                         Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
                     }
+                    if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
+                        // Make sure the original object is not mutated. NetworkAgent normally
+                        // makes a copy of the capabilities when sending the message through
+                        // the Messenger, but if this ever changes, not making a defensive copy
+                        // here will give attack vectors to clients using this code path.
+                        networkCapabilities = new NetworkCapabilities(networkCapabilities);
+                        networkCapabilities.restrictCapabilitesForTestNetwork();
+                    }
                     updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
                     break;
                 }
@@ -5778,7 +5788,16 @@
     public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
             int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
-        enforceNetworkFactoryPermission();
+        if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
+            enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS);
+            // Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in
+            // the call to mixInCapabilities below anyway, but sanitizing here means the NAI never
+            // sees capabilities that may be malicious, which might prevent mistakes in the future.
+            networkCapabilities = new NetworkCapabilities(networkCapabilities);
+            networkCapabilities.restrictCapabilitesForTestNetwork();
+        } else {
+            enforceNetworkFactoryPermission();
+        }
 
         LinkProperties lp = new LinkProperties(linkProperties);
         lp.ensureDirectlyConnectedRoutes();
@@ -7948,10 +7967,13 @@
             return false;
         }
 
+        final Network[] underlyingNetworks;
         synchronized (mVpns) {
-            if (getVpnIfOwner(callbackUid) != null) {
-                return true;
-            }
+            final Vpn vpn = getVpnIfOwner(callbackUid);
+            underlyingNetworks = (vpn == null) ? null : vpn.getUnderlyingNetworks();
+        }
+        if (underlyingNetworks != null) {
+            if (Arrays.asList(underlyingNetworks).contains(nai.network)) return true;
         }
 
         // Administrator UIDs also contains the Owner UID
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 1bb3c3a..0ddfa1c 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -886,6 +886,7 @@
 
     @Override
     public void setIPv6AddrGenMode(String iface, int mode) throws ServiceSpecificException {
+        NetworkStack.checkNetworkStackPermission(mContext);
         try {
             mNetdService.setIPv6AddrGenMode(iface, mode);
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 41a104c..d9e7c38 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -1062,7 +1062,12 @@
         public void updatePackagesLocked(List<MonitoredPackage> packages) {
             for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
                 MonitoredPackage p = packages.get(pIndex);
-                this.packages.put(p.getName(), p);
+                MonitoredPackage existingPackage = this.packages.get(p.getName());
+                if (existingPackage != null) {
+                    existingPackage.updateHealthCheckDuration(p.mDurationMs);
+                } else {
+                    this.packages.put(p.getName(), p);
+                }
             }
         }
 
@@ -1331,6 +1336,12 @@
             return updateHealthCheckStateLocked();
         }
 
+        /** Explicitly update the monitoring duration of the package. */
+        @GuardedBy("mLock")
+        public void updateHealthCheckDuration(long newDurationMs) {
+            mDurationMs = newDurationMs;
+        }
+
         /**
          * Marks the health check as passed and transitions to {@link HealthCheckState.PASSED}
          * if not yet {@link HealthCheckState.FAILED}.
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 0bf81e0..4d8c86c 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -43,6 +43,7 @@
 import static android.os.storage.OnObbStateChangeListener.MOUNTED;
 import static android.os.storage.OnObbStateChangeListener.UNMOUNTED;
 import static android.os.storage.StorageManager.PROP_FUSE;
+import static android.os.storage.StorageManager.PROP_LEGACY_OP_STICKY;
 import static android.os.storage.StorageManager.PROP_SETTINGS_FUSE;
 
 import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -155,6 +156,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.pm.Installer;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.storage.AppFuseBridge;
 import com.android.server.storage.StorageSessionController;
 import com.android.server.storage.StorageSessionController.ExternalStorageServiceException;
@@ -902,6 +904,7 @@
                     refreshIsolatedStorageSettings();
                 }
             });
+        updateLegacyStorageOpSticky();
         // For now, simply clone property when it changes
         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
                 mContext.getMainExecutor(), (properties) -> {
@@ -1778,6 +1781,13 @@
         }
     }
 
+    private void updateLegacyStorageOpSticky() {
+        final boolean propertyValue = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                "legacy_storage_op_sticky", true);
+        SystemProperties.set(PROP_LEGACY_OP_STICKY, propertyValue ? "true" : "false");
+    }
+
     private void start() {
         connectStoraged();
         connectVold();
@@ -3268,6 +3278,25 @@
         }
     }
 
+    @Override
+    public void fixupAppDir(String path) {
+        final Matcher matcher = KNOWN_APP_DIR_PATHS.matcher(path);
+        if (matcher.matches()) {
+            AndroidPackage pkg = mPmInternal.getPackage(matcher.group(3));
+            if (pkg != null) {
+                try {
+                    mVold.fixupAppDir(path + "/", pkg.getUid());
+                } catch (RemoteException | ServiceSpecificException e) {
+                    Log.e(TAG, "Failed to fixup app dir for " + pkg.getPackageName(), e);
+                }
+            } else {
+                Log.e(TAG, "Can't find package belonging to " + path);
+            }
+        } else {
+            Log.e(TAG, "Path " + path + " is not a valid application-specific directory");
+        }
+    }
+
     /** Not thread safe */
     class AppFuseMountScope extends AppFuseBridge.MountScope {
         private boolean mMounted = false;
@@ -4430,9 +4459,8 @@
                             String.format("/storage/emulated/%d/Android/data/%s/",
                                     userId, pkg);
 
-                    int appUid =
-                            UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid());
                     // Create package obb and data dir if it doesn't exist.
+                    int appUid = UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid());
                     File file = new File(packageObbDir);
                     if (!file.exists()) {
                         vold.setupAppDir(packageObbDir, appUid);
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index 8f914fe..94d6b13 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -25,6 +25,7 @@
 import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.Preconditions;
 import com.android.server.am.ActivityManagerService;
+import com.android.server.utils.TimingsTraceAndSlog;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -93,6 +94,8 @@
             mPendingTasks.add(description);
         }
         return mService.submit(() -> {
+            TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
+            traceLog.traceBegin("InitThreadPoolExec:" + description);
             if (IS_DEBUGGABLE) {
                 Slog.d(TAG, "Started executing " + description);
             }
@@ -100,6 +103,7 @@
                 runnable.run();
             } catch (RuntimeException e) {
                 Slog.e(TAG, "Failure in " + description + ": " + e, e);
+                traceLog.traceEnd();
                 throw e;
             }
             synchronized (mPendingTasks) {
@@ -108,6 +112,7 @@
             if (IS_DEBUGGABLE) {
                 Slog.d(TAG, "Finished executing " + description);
             }
+            traceLog.traceEnd();
         });
     }
 
@@ -132,7 +137,10 @@
      */
     static void shutdown() {
         synchronized (LOCK) {
+            TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+            t.traceBegin("WaitInitThreadPoolShutdown");
             if (sInstance == null) {
+                t.traceEnd();
                 Slog.wtf(TAG, "Already shutdown", new Exception());
                 return;
             }
@@ -147,6 +155,7 @@
             } catch (InterruptedException e) {
                 Thread.currentThread().interrupt();
                 dumpStackTraces();
+                t.traceEnd();
                 throw new IllegalStateException(TAG + " init interrupted");
             }
             if (!terminated) {
@@ -160,11 +169,13 @@
                 synchronized (sInstance.mPendingTasks) {
                     copy.addAll(sInstance.mPendingTasks);
                 }
+                t.traceEnd();
                 throw new IllegalStateException("Cannot shutdown. Unstarted tasks "
                         + unstartedRunnables + " Unfinished tasks " + copy);
             }
             sInstance = null; // Make eligible for GC
             Slog.d(TAG, "Shutdown successful");
+            t.traceEnd();
         }
     }
 
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index dd9cc64..ac4a42c 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -605,15 +605,16 @@
     }
 
     @Override // Binder call
-    public boolean[] areEffectsSupported(int[] effectIds) {
-        // Return null to indicate that the HAL doesn't actually tell us what effects are
-        // supported.
+    public int[] areEffectsSupported(int[] effectIds) {
+        int[] supported = new int[effectIds.length];
         if (mSupportedEffects == null) {
-            return null;
-        }
-        boolean[] supported = new boolean[effectIds.length];
-        for (int i = 0; i < effectIds.length; i++) {
-            supported[i] = mSupportedEffects.contains(effectIds[i]);
+            Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN);
+        } else {
+            for (int i = 0; i < effectIds.length; i++) {
+                supported[i] = mSupportedEffects.contains(effectIds[i])
+                        ? Vibrator.VIBRATION_EFFECT_SUPPORT_YES
+                        : Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
+            }
         }
         return supported;
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4cfcd2b..38405e1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -185,8 +185,6 @@
 import android.app.ProcessMemoryState;
 import android.app.ProfilerInfo;
 import android.app.WaitResult;
-import android.app.WindowConfiguration.ActivityType;
-import android.app.WindowConfiguration.WindowingMode;
 import android.app.backup.IBackupManager;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageEvents.Event;
@@ -6513,13 +6511,6 @@
     }
 
     @Override
-    public List<RunningTaskInfo> getFilteredTasks(int maxNum, @ActivityType int ignoreActivityType,
-            @WindowingMode int ignoreWindowingMode) {
-        return mActivityTaskManager.getFilteredTasks(
-                maxNum, ignoreActivityType, ignoreWindowingMode);
-    }
-
-    @Override
     public void cancelTaskWindowTransition(int taskId) {
         mActivityTaskManager.cancelTaskWindowTransition(taskId);
     }
@@ -8780,6 +8771,27 @@
     }
 
     @Override
+    public boolean isUidActiveOrForeground(int uid, String callingPackage) {
+        if (!hasUsageStatsPermission(callingPackage)) {
+            enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
+                    "isUidActiveOrForeground");
+        }
+        synchronized (this) {
+            final boolean isActive = isUidActiveLocked(uid);
+            if (isActive) {
+                return true;
+            }
+        }
+        final boolean isForeground = mAtmInternal.isUidForeground(uid);
+        if (isForeground) {
+            Slog.wtf(TAG, "isUidActiveOrForeground: isUidActive false but "
+                    + " isUidForeground true, uid:" + uid
+                    + " callingPackage:" + callingPackage);
+        }
+        return isForeground;
+    }
+
+    @Override
     public void setPersistentVrThread(int tid) {
         mActivityTaskManager.setPersistentVrThread(tid);
     }
diff --git a/services/core/java/com/android/server/am/BugReportHandlerUtil.java b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
index 03f4a54..ba89fce 100644
--- a/services/core/java/com/android/server/am/BugReportHandlerUtil.java
+++ b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
@@ -16,20 +16,15 @@
 
 package com.android.server.am;
 
-import static android.app.AppOpsManager.OP_NONE;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
-import android.app.Activity;
 import android.app.BroadcastOptions;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Binder;
-import android.os.BugreportManager;
-import android.os.BugreportParams;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -115,17 +110,9 @@
         options.setBackgroundActivityStartsAllowed(true);
         final long identity = Binder.clearCallingIdentity();
         try {
-            // Handler app's BroadcastReceiver should call setResultCode(Activity.RESULT_OK) to
-            // let ResultBroadcastReceiver know the handler app is available.
-            context.sendOrderedBroadcastAsUser(intent,
-                    UserHandle.of(handlerUser),
+            context.sendBroadcastAsUser(intent, UserHandle.of(handlerUser),
                     android.Manifest.permission.DUMP,
-                    OP_NONE, options.toBundle(),
-                    new ResultBroadcastReceiver(),
-                    /* scheduler= */ null,
-                    Activity.RESULT_CANCELED,
-                    /* initialData= */ null,
-                    /* initialExtras= */ null);
+                    options.toBundle());
         } catch (RuntimeException e) {
             Slog.e(TAG, "Error while trying to launch bugreport handler app.", e);
             return false;
@@ -189,19 +176,4 @@
             Binder.restoreCallingIdentity(identity);
         }
     }
-
-    private static class ResultBroadcastReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (getResultCode() == Activity.RESULT_OK) {
-                return;
-            }
-
-            Slog.w(TAG, "Request bug report because handler app seems to be not available.");
-            BugreportManager bugreportManager = context.getSystemService(BugreportManager.class);
-            bugreportManager.requestBugreport(
-                    new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE),
-                    /* shareTitle= */null, /* shareDescription= */ null);
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 4d08bd2..bee0e05 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -98,6 +98,7 @@
 import android.system.Os;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.LongSparseArray;
 import android.util.Pair;
@@ -138,6 +139,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Activity manager code dealing with processes.
@@ -2155,15 +2157,6 @@
                 result.put(packageName, Pair.create(volumeUuid, inode));
             }
         }
-        if (mAppDataIsolationWhitelistedApps != null) {
-            for (String packageName : mAppDataIsolationWhitelistedApps) {
-                String volumeUuid = pmInt.getPackage(packageName).getVolumeUuid();
-                long inode = pmInt.getCeDataInode(packageName, userId);
-                if (inode != 0) {
-                    result.put(packageName, Pair.create(volumeUuid, inode));
-                }
-            }
-        }
 
         return result;
     }
@@ -2184,34 +2177,42 @@
                 app.setHasForegroundActivities(true);
             }
 
+            final Map<String, Pair<String, Long>> pkgDataInfoMap;
+            final Map<String, Pair<String, Long>> whitelistedAppDataInfoMap;
+            boolean bindMountAppStorageDirs = false;
+            boolean bindMountAppsData = shouldIsolateAppData(app);
+
+            // Get all packages belongs to the same shared uid. sharedPackages is empty array
+            // if it doesn't have shared uid.
+            final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked();
+            final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage(
+                    app.info.packageName, app.userId);
+            final String[] targetPackagesList = sharedPackages.length == 0
+                    ? new String[]{app.info.packageName} : sharedPackages;
+            pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, targetPackagesList, uid);
+
+            // Remove all packages in pkgDataInfoMap from mAppDataIsolationWhitelistedApps, so
+            // it won't be mounted twice.
+            final Set<String> whitelistedApps = new ArraySet<>(mAppDataIsolationWhitelistedApps);
+            for (String pkg : targetPackagesList) {
+                whitelistedApps.remove(pkg);
+            }
+            whitelistedAppDataInfoMap = getPackageAppDataInfoMap(pmInt,
+                    whitelistedApps.toArray(new String[0]), uid);
+
+            int userId = UserHandle.getUserId(uid);
             StorageManagerInternal storageManagerInternal = LocalServices.getService(
                     StorageManagerInternal.class);
-            final Map<String, Pair<String, Long>> pkgDataInfoMap;
-            boolean bindMountAppStorageDirs = false;
-
-            if (shouldIsolateAppData(app)) {
-                // Get all packages belongs to the same shared uid. sharedPackages is empty array
-                // if it doesn't have shared uid.
-                final PackageManagerInternal pmInt = mService.getPackageManagerInternalLocked();
-                final String[] sharedPackages = pmInt.getSharedUserPackagesForPackage(
-                        app.info.packageName, app.userId);
-                pkgDataInfoMap = getPackageAppDataInfoMap(pmInt, sharedPackages.length == 0
-                        ? new String[]{app.info.packageName} : sharedPackages, uid);
-
-                int userId = UserHandle.getUserId(uid);
-                if (mVoldAppDataIsolationEnabled && UserHandle.isApp(app.uid) &&
-                        !storageManagerInternal.isExternalStorageService(uid)) {
-                    bindMountAppStorageDirs = true;
-                    if (!storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
-                            app.processName)) {
-                        // Cannot prepare Android/app and Android/obb directory,
-                        // so we won't mount it in zygote.
-                        app.bindMountPending = true;
-                        bindMountAppStorageDirs = false;
-                    }
+            if (mVoldAppDataIsolationEnabled && UserHandle.isApp(app.uid) &&
+                    !storageManagerInternal.isExternalStorageService(uid)) {
+                bindMountAppStorageDirs = true;
+                if (!storageManagerInternal.prepareStorageDirs(userId, pkgDataInfoMap.keySet(),
+                        app.processName)) {
+                    // Cannot prepare Android/app and Android/obb directory,
+                    // so we won't mount it in zygote.
+                    app.bindMountPending = true;
+                    bindMountAppStorageDirs = false;
                 }
-            } else {
-                pkgDataInfoMap = null;
             }
 
             final Process.ProcessStartResult startResult;
@@ -2229,7 +2230,8 @@
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, null, app.info.packageName,
                         /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
-                        app.mDisabledCompatChanges, pkgDataInfoMap, bindMountAppStorageDirs,
+                        app.mDisabledCompatChanges, pkgDataInfoMap, whitelistedAppDataInfoMap,
+                        bindMountAppsData, bindMountAppStorageDirs,
                         new String[]{PROC_START_SEQ_IDENT + app.startSeq});
             } else {
                 startResult = Process.start(entryPoint,
@@ -2237,7 +2239,7 @@
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, invokeWith, app.info.packageName, zygotePolicyFlags,
                         isTopApp, app.mDisabledCompatChanges, pkgDataInfoMap,
-                        bindMountAppStorageDirs,
+                        whitelistedAppDataInfoMap, bindMountAppsData, bindMountAppStorageDirs,
                         new String[]{PROC_START_SEQ_IDENT + app.startSeq});
             }
             checkSlow(startTime, "startProcess: returned from zygote!");
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 1f0146a..94675ab 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -776,6 +776,8 @@
 
         mDeviceBroker = new AudioDeviceBroker(mContext, this);
 
+        mRecordMonitor = new RecordingActivityMonitor(mContext);
+
         // must be called before readPersistedSettings() which needs a valid mStreamVolumeAlias[]
         // array initialized by updateStreamVolumeAlias()
         updateStreamVolumeAlias(false /*updateVolumes*/, TAG);
@@ -797,8 +799,6 @@
 
         mMediaFocusControl = new MediaFocusControl(mContext, mPlaybackMonitor);
 
-        mRecordMonitor = new RecordingActivityMonitor(mContext);
-
         readAndSetLowRamDevice();
 
         mIsCallScreeningModeSupported = AudioSystem.isCallScreeningModeSupported();
@@ -1981,8 +1981,7 @@
         }
 
         flags &= ~AudioManager.FLAG_FIXED_VOLUME;
-        if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&
-                mFixedVolumeDevices.contains(device)) {
+        if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {
             flags |= AudioManager.FLAG_FIXED_VOLUME;
 
             // Always toggle between max safe volume and 0 for fixed volume devices where safe
@@ -2059,7 +2058,7 @@
                     !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
                 Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex);
                 mVolumeController.postDisplaySafeVolumeWarning(flags);
-            } else if (!mFullVolumeDevices.contains(device)
+            } else if (!isFullVolumeDevice(device)
                     && (streamState.adjustIndex(direction * step, device, caller)
                             || streamState.mIsMuted)) {
                 // Post message to set system volume (it in turn will post a
@@ -2121,7 +2120,7 @@
                     if (mHdmiCecSink
                             && streamTypeAlias == AudioSystem.STREAM_MUSIC
                             // vol change on a full volume device
-                            && mFullVolumeDevices.contains(device)) {
+                            && isFullVolumeDevice(device)) {
                         int keyCode = KeyEvent.KEYCODE_UNKNOWN;
                         switch (direction) {
                             case AudioManager.ADJUST_RAISE:
@@ -2325,6 +2324,13 @@
 
         // For legacy reason, propagate to all streams associated to this volume group
         for (final int groupedStream : vgs.getLegacyStreamTypes()) {
+            try {
+                ensureValidStreamType(groupedStream);
+            } catch (IllegalArgumentException e) {
+                Log.d(TAG, "volume group " + volumeGroup + " has internal streams (" + groupedStream
+                        + "), do not change associated stream volume");
+                continue;
+            }
             setStreamVolume(groupedStream, index, flags, callingPackage, callingPackage,
                             Binder.getCallingUid());
         }
@@ -2590,8 +2596,7 @@
             }
 
             flags &= ~AudioManager.FLAG_FIXED_VOLUME;
-            if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&
-                    mFixedVolumeDevices.contains(device)) {
+            if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {
                 flags |= AudioManager.FLAG_FIXED_VOLUME;
 
                 // volume is either 0 or max allowed for fixed volume devices
@@ -2780,7 +2785,7 @@
 
         if (streamType == AudioSystem.STREAM_MUSIC) {
             flags = updateFlagsForTvPlatform(flags);
-            if (mFullVolumeDevices.contains(device)) {
+            if (isFullVolumeDevice(device)) {
                 flags &= ~AudioManager.FLAG_SHOW_UI;
             }
         }
@@ -2826,7 +2831,7 @@
                                     int device,
                                     boolean force,
                                     String caller) {
-        if (mFullVolumeDevices.contains(device)) {
+        if (isFullVolumeDevice(device)) {
             return;
         }
         VolumeStreamState streamState = mStreamStates[streamType];
@@ -3036,7 +3041,7 @@
                 index = 0;
             }
             if (index != 0 && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
-                    mFixedVolumeDevices.contains(device)) {
+                    isFixedVolumeDevice(device)) {
                 index = mStreamStates[streamType].getMaxIndex();
             }
             return (index + 5) / 10;
@@ -4570,6 +4575,7 @@
     public void setWiredDeviceConnectionState(int type,
             @ConnectionState int state, String address, String name,
             String caller) {
+        enforceModifyAudioRoutingPermission();
         if (state != CONNECTION_STATE_CONNECTED
                 && state != CONNECTION_STATE_DISCONNECTED) {
             throw new IllegalArgumentException("Invalid state " + state);
@@ -4882,10 +4888,6 @@
 
         public void applyAllVolumes() {
             synchronized (VolumeGroupState.class) {
-                if (mLegacyStreamType != AudioSystem.STREAM_DEFAULT) {
-                    // No-op to avoid regression with stream based volume management
-                    return;
-                }
                 // apply device specific volumes first
                 int index;
                 for (int i = 0; i < mIndexMap.size(); i++) {
@@ -5165,7 +5167,7 @@
             } else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
                     && isAvrcpAbsVolSupported) {
                 index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
-            } else if (mFullVolumeDevices.contains(device)) {
+            } else if (isFullVolumeDevice(device)) {
                 index = (mIndexMax + 5)/10;
             } else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
                 index = (mIndexMax + 5)/10;
@@ -5188,7 +5190,7 @@
                         } else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
                                 && isAvrcpAbsVolSupported) {
                             index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
-                        } else if (mFullVolumeDevices.contains(device)) {
+                        } else if (isFullVolumeDevice(device)) {
                             index = (mIndexMax + 5)/10;
                         } else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
                             index = (mIndexMax + 5)/10;
@@ -5389,8 +5391,8 @@
                     for (int i = 0; i < mIndexMap.size(); i++) {
                         int device = mIndexMap.keyAt(i);
                         int index = mIndexMap.valueAt(i);
-                        if (mFullVolumeDevices.contains(device)
-                                || (mFixedVolumeDevices.contains(device) && index != 0)) {
+                        if (isFullVolumeDevice(device)
+                                || (isFixedVolumeDevice(device) && index != 0)) {
                             mIndexMap.put(device, mIndexMax);
                         }
                         applyDeviceVolume_syncVSS(device, isAvrcpAbsVolSupported);
@@ -8235,4 +8237,23 @@
             new HashMap<IBinder, AudioPolicyProxy>();
     @GuardedBy("mAudioPolicies")
     private int mAudioPolicyCounter = 0;
+
+    //======================
+    // Helper functions for full and fixed volume device
+    //======================
+    private boolean isFixedVolumeDevice(int deviceType) {
+        if (deviceType == AudioSystem.DEVICE_OUT_REMOTE_SUBMIX
+                && mRecordMonitor.isLegacyRemoteSubmixActive()) {
+            return false;
+        }
+        return mFixedVolumeDevices.contains(deviceType);
+    }
+
+    private boolean isFullVolumeDevice(int deviceType) {
+        if (deviceType == AudioSystem.DEVICE_OUT_REMOTE_SUBMIX
+                && mRecordMonitor.isLegacyRemoteSubmixActive()) {
+            return false;
+        }
+        return mFullVolumeDevices.contains(deviceType);
+    }
 }
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 5c50962..65f2218 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.media.AudioDeviceInfo;
 import android.media.AudioFormat;
 import android.media.AudioManager;
 import android.media.AudioRecordingConfiguration;
@@ -35,6 +36,8 @@
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Class to receive and dispatch updates from AudioSystem about recording configurations.
@@ -49,6 +52,16 @@
     // playback configurations that do not contain uid/package name information.
     private boolean mHasPublicClients = false;
 
+
+    // When legacy remote submix device is active, remote submix device should not be fixed and
+    // full volume device. When legacy remote submix device is active, there will be a recording
+    // activity using device with type as {@link AudioSystem.DEVICE_OUT_REMOTE_SUBMIX} and address
+    // as {@link AudioSystem.LEGACY_REMOTE_SUBMIX_ADDRESS}. Cache riid of legacy remote submix
+    // since remote submix state is not cached in mRecordStates.
+    private AtomicInteger mLegacyRemoteSubmixRiid =
+            new AtomicInteger(AudioManager.RECORD_RIID_INVALID);
+    private AtomicBoolean mLegacyRemoteSubmixActive = new AtomicBoolean(false);
+
     static final class RecordingState {
         private final int mRiid;
         private final RecorderDeathHandler mDeathHandler;
@@ -137,6 +150,16 @@
         final AudioRecordingConfiguration config = createRecordingConfiguration(
                 uid, session, source, recordingInfo,
                 portId, silenced, activeSource, clientEffects, effects);
+        if (source == MediaRecorder.AudioSource.REMOTE_SUBMIX) {
+            final AudioDeviceInfo device = config.getAudioDevice();
+            if (AudioSystem.LEGACY_REMOTE_SUBMIX_ADDRESS.equals(device.getAddress())) {
+                mLegacyRemoteSubmixRiid.set(riid);
+                if (event == AudioManager.RECORD_CONFIG_EVENT_START
+                        || event == AudioManager.RECORD_CONFIG_EVENT_UPDATE) {
+                    mLegacyRemoteSubmixActive.set(true);
+                }
+            }
+        }
         if (MediaRecorder.isSystemOnlyAudioSource(source)) {
             // still want to log event, it just won't appear in recording configurations;
             sEventLogger.log(new RecordingEvent(event, riid, config).printLog(TAG));
@@ -170,6 +193,9 @@
      * Receive an event from the client about a tracked recorder
      */
     public void recorderEvent(int riid, int event) {
+        if (mLegacyRemoteSubmixRiid.get() == riid) {
+            mLegacyRemoteSubmixActive.set(event == AudioManager.RECORDER_STATE_STARTED);
+        }
         int configEvent = event == AudioManager.RECORDER_STATE_STARTED
                 ? AudioManager.RECORD_CONFIG_EVENT_START :
                 event == AudioManager.RECORDER_STATE_STOPPED
@@ -323,6 +349,13 @@
     }
 
     /**
+     * Return true if legacy remote submix device is active. Otherwise, return false.
+     */
+    boolean isLegacyRemoteSubmixActive() {
+        return mLegacyRemoteSubmixActive.get();
+    }
+
+    /**
      * Create a recording configuration from the provided parameters
      * @param uid
      * @param session
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index d45ffd9..ff8e3a9 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -202,8 +202,7 @@
 
             // Only allow internal clients to call canAuthenticate with a different userId.
             final int callingUserId = UserHandle.getCallingUserId();
-            Slog.d(TAG, "canAuthenticate, userId: " + userId + ", callingUserId: " + callingUserId
-                    + ", authenticators: " + authenticators);
+
             if (userId != callingUserId) {
                 checkInternalPermission();
             } else {
@@ -212,8 +211,14 @@
 
             final long identity = Binder.clearCallingIdentity();
             try {
-                return mBiometricService.canAuthenticate(
+                final int result = mBiometricService.canAuthenticate(
                         opPackageName, userId, callingUserId, authenticators);
+                Slog.d(TAG, "canAuthenticate"
+                        + ", userId: " + userId
+                        + ", callingUserId: " + callingUserId
+                        + ", authenticators: " + authenticators
+                        + ", result: " + result);
+                return result;
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 766e5c4..5d334c2 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -66,6 +66,8 @@
 
     public abstract boolean wasUserDetected();
 
+    public abstract boolean isStrongBiometric();
+
     public AuthenticationClient(Context context, Constants constants,
             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
             BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId,
@@ -167,9 +169,15 @@
                 }
                 if (isBiometricPrompt() && listener != null) {
                     // BiometricService will add the token to keystore
-                    listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken);
+                    listener.onAuthenticationSucceededInternal(mRequireConfirmation, byteToken,
+                            isStrongBiometric());
                 } else if (!isBiometricPrompt() && listener != null) {
-                    KeyStore.getInstance().addAuthToken(byteToken);
+                    if (isStrongBiometric()) {
+                        KeyStore.getInstance().addAuthToken(byteToken);
+                    } else {
+                        Slog.d(getLogTag(), "Skipping addAuthToken");
+                    }
+
                     try {
                         // Explicitly have if/else here to make it super obvious in case the code is
                         // touched in the future.
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index e7c09ba..233416d 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -266,7 +266,8 @@
                     SomeArgs args = (SomeArgs) msg.obj;
                     handleAuthenticationSucceeded(
                             (boolean) args.arg1 /* requireConfirmation */,
-                            (byte[]) args.arg2 /* token */);
+                            (byte[]) args.arg2 /* token */,
+                            (boolean) args.arg3 /* isStrongBiometric */);
                     args.recycle();
                     break;
                 }
@@ -568,10 +569,12 @@
     final IBiometricServiceReceiverInternal mInternalReceiver =
             new IBiometricServiceReceiverInternal.Stub() {
         @Override
-        public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token) {
+        public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token,
+                boolean isStrongBiometric) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = requireConfirmation;
             args.arg2 = token;
+            args.arg3 = isStrongBiometric;
             mHandler.obtainMessage(MSG_ON_AUTHENTICATION_SUCCEEDED, args).sendToTarget();
         }
 
@@ -761,8 +764,13 @@
                         + " config_biometric_sensors?");
             }
 
+            // Note that we allow BIOMETRIC_CONVENIENCE to register because BiometricService
+            // also does / will do other things such as keep track of lock screen timeout, etc.
+            // Just because a biometric is registered does not mean it can participate in
+            // the android.hardware.biometrics APIs.
             if (strength != Authenticators.BIOMETRIC_STRONG
-                    && strength != Authenticators.BIOMETRIC_WEAK) {
+                    && strength != Authenticators.BIOMETRIC_WEAK
+                    && strength != Authenticators.BIOMETRIC_CONVENIENCE) {
                 throw new IllegalStateException("Unsupported strength");
             }
 
@@ -1189,8 +1197,10 @@
                         BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL);
             }
         } else {
+            // This should not be possible via the public API surface and is here mainly for
+            // "correctness". An exception should have been thrown before getting here.
             Slog.e(TAG, "No authenticators requested");
-            return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+            return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
         }
     }
 
@@ -1286,7 +1296,8 @@
         return modality;
     }
 
-    private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token) {
+    private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token,
+            boolean isStrongBiometric) {
         try {
             // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
             // after user dismissed/canceled dialog).
@@ -1295,9 +1306,16 @@
                 return;
             }
 
-            // Store the auth token and submit it to keystore after the dialog is confirmed /
-            // animating away.
-            mCurrentAuthSession.mTokenEscrow = token;
+            if (isStrongBiometric) {
+                // Store the auth token and submit it to keystore after the dialog is confirmed /
+                // animating away.
+                mCurrentAuthSession.mTokenEscrow = token;
+            } else {
+                if (token != null) {
+                    Slog.w(TAG, "Dropping authToken for non-strong biometric");
+                }
+            }
+
             if (!requireConfirmation) {
                 mCurrentAuthSession.mState = STATE_AUTHENTICATED_PENDING_SYSUI;
             } else {
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index ebd407d..45b9383 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -413,8 +413,8 @@
             throw new UnsupportedOperationException("Stub!");
         }
 
-        default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
-                throws RemoteException {
+        default void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token,
+                boolean isStrongBiometric) throws RemoteException {
             throw new UnsupportedOperationException("Stub!");
         }
 
@@ -451,10 +451,11 @@
         }
 
         @Override
-        public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token)
-                throws RemoteException {
+        public void onAuthenticationSucceededInternal(boolean requireConfirmation, byte[] token,
+                boolean isStrongBiometric) throws RemoteException {
             if (getWrapperReceiver() != null) {
-                getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token);
+                getWrapperReceiver().onAuthenticationSucceeded(requireConfirmation, token,
+                        isStrongBiometric);
             }
         }
 
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index fd54129..3ecf87c 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -236,6 +236,11 @@
         }
 
         @Override
+        public boolean isStrongBiometric() {
+            return FaceService.this.isStrongBiometric();
+        }
+
+        @Override
         public boolean onAuthenticated(BiometricAuthenticator.Identifier identifier,
                 boolean authenticated, ArrayList<Byte> token) {
             final boolean result = super.onAuthenticated(identifier, authenticated, token);
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index acb1a2f..8520f5a 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -161,6 +161,11 @@
         }
 
         @Override
+        public boolean isStrongBiometric() {
+            return FingerprintService.this.isStrongBiometric();
+        }
+
+        @Override
         public int handleFailedAttempt() {
             final int currentUser = ActivityManager.getCurrentUser();
             mFailedAttempts.put(currentUser, mFailedAttempts.get(currentUser, 0) + 1);
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 05a757b..8eb7710 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -646,7 +646,9 @@
                                     + "id=" + physicalDisplayId
                                     + ", state=" + Display.stateToString(state) + ")");
                         }
-                        mBacklight.setVrMode(isVrEnabled);
+                        if (mBacklight != null) {
+                            mBacklight.setVrMode(isVrEnabled);
+                        }
                     }
 
                     private void setDisplayState(int state) {
@@ -708,7 +710,9 @@
                                         BrightnessSynchronizer.brightnessFloatToInt(getContext(),
                                                 brightness));
                             }
-                            mBacklight.setBrightness(brightness);
+                            if (mBacklight != null) {
+                                mBacklight.setBrightness(brightness);
+                            }
                             Trace.traceCounter(Trace.TRACE_TAG_POWER,
                                     "ScreenBrightness",
                                     BrightnessSynchronizer.brightnessFloatToInt(
diff --git a/services/core/java/com/android/server/incident/IncidentCompanionService.java b/services/core/java/com/android/server/incident/IncidentCompanionService.java
index 87fe785..ad08663 100644
--- a/services/core/java/com/android/server/incident/IncidentCompanionService.java
+++ b/services/core/java/com/android/server/incident/IncidentCompanionService.java
@@ -50,6 +50,9 @@
  */
 public class IncidentCompanionService extends SystemService {
     static final String TAG = "IncidentCompanionService";
+    // TODO(b/152289743): Expose below intent.
+    private static final String INTENT_CHECK_USER_CONSENT =
+            "com.android.internal.intent.action.CHECK_USER_CONSENT";
 
     /**
      * Dump argument for proxying restricted image dumps to the services
@@ -89,6 +92,12 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
+                Intent intent = new Intent(INTENT_CHECK_USER_CONSENT);
+                intent.setPackage(callingPackage);
+                intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+                getContext().sendBroadcast(intent, android.Manifest.permission.DUMP);
+
                 mPendingReports.authorizeReport(callingUid, callingPackage,
                         receiverClass, reportId, flags, listener);
             } finally {
diff --git a/services/core/java/com/android/server/incident/PendingReports.java b/services/core/java/com/android/server/incident/PendingReports.java
index 9fcbab7..f39bebf 100644
--- a/services/core/java/com/android/server/incident/PendingReports.java
+++ b/services/core/java/com/android/server/incident/PendingReports.java
@@ -359,6 +359,8 @@
     private void sendBroadcast(ComponentName receiver, int primaryUser) {
         final Intent intent = new Intent(Intent.ACTION_PENDING_INCIDENT_REPORTS_CHANGED);
         intent.setComponent(receiver);
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         final BroadcastOptions options = BroadcastOptions.makeBasic();
         options.setBackgroundActivityStartsAllowed(true);
 
diff --git a/services/core/java/com/android/server/lights/LightsManager.java b/services/core/java/com/android/server/lights/LightsManager.java
index 521913a..706c741 100644
--- a/services/core/java/com/android/server/lights/LightsManager.java
+++ b/services/core/java/com/android/server/lights/LightsManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.lights;
 
+import android.annotation.Nullable;
 import android.hardware.light.V2_0.Type;
 
 public abstract class LightsManager {
@@ -30,7 +31,8 @@
     public static final int LIGHT_ID_COUNT = Type.COUNT;
 
     /**
-     * Returns the logical light with the given type.
+     * Returns the logical light with the given type, if it exists, or null.
      */
+    @Nullable
     public abstract LogicalLight getLight(int id);
 }
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index a42dec8..3c6e8d2 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -52,8 +52,8 @@
     static final String TAG = "LightsService";
     static final boolean DEBUG = false;
 
-    private LightImpl[] mLights = null;
-    private SparseArray<LightImpl> mLightsById = null;
+    private final LightImpl[] mLightsByType = new LightImpl[LightsManager.LIGHT_ID_COUNT];
+    private final SparseArray<LightImpl> mLightsById = new SparseArray<>();
 
     private ILights mVintfLights = null;
 
@@ -96,8 +96,8 @@
             synchronized (LightsService.this) {
                 final List<Light> lights = new ArrayList<Light>();
                 for (int i = 0; i < mLightsById.size(); i++) {
-                    HwLight hwLight = mLightsById.valueAt(i).getHwLight();
-                    if (!isSystemLight(hwLight)) {
+                    if (!mLightsById.valueAt(i).isSystemLight()) {
+                        HwLight hwLight = mLightsById.valueAt(i).mHwLight;
                         lights.add(new Light(hwLight.id, hwLight.ordinal, hwLight.type));
                     }
                 }
@@ -138,7 +138,7 @@
 
             synchronized (LightsService.this) {
                 final LightImpl light = mLightsById.get(lightId);
-                if (light == null || isSystemLight(light.getHwLight())) {
+                if (light == null || light.isSystemLight()) {
                     throw new IllegalArgumentException("Invalid light: " + lightId);
                 }
                 return new LightState(light.getColor());
@@ -184,9 +184,8 @@
         private void checkRequestIsValid(int[] lightIds) {
             for (int i = 0; i < lightIds.length; i++) {
                 final LightImpl light = mLightsById.get(lightIds[i]);
-                final HwLight hwLight = light.getHwLight();
-                Preconditions.checkState(light != null && !isSystemLight(hwLight),
-                        "invalid lightId " + hwLight.id);
+                Preconditions.checkState(light != null && !light.isSystemLight(),
+                        "Invalid lightId " + lightIds[i]);
             }
         }
 
@@ -205,9 +204,8 @@
             }
             for (int i = 0; i < mLightsById.size(); i++) {
                 LightImpl light = mLightsById.valueAt(i);
-                HwLight hwLight = light.getHwLight();
-                if (!isSystemLight(hwLight)) {
-                    LightState state = states.get(hwLight.id);
+                if (!light.isSystemLight()) {
+                    LightState state = states.get(light.mHwLight.id);
                     if (state != null) {
                         light.setColor(state.getColor());
                     } else {
@@ -385,26 +383,22 @@
                 int brightnessMode) {
             Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLightState(" + mHwLight.id + ", 0x"
                     + Integer.toHexString(color) + ")");
-            if (mVintfLights != null) {
-                HwLightState lightState = new HwLightState();
-                lightState.color = color;
-                lightState.flashMode = (byte) mode;
-                lightState.flashOnMs = onMS;
-                lightState.flashOffMs = offMS;
-                lightState.brightnessMode = (byte) brightnessMode;
-                try {
+            try {
+                if (mVintfLights != null) {
+                    HwLightState lightState = new HwLightState();
+                    lightState.color = color;
+                    lightState.flashMode = (byte) mode;
+                    lightState.flashOnMs = onMS;
+                    lightState.flashOffMs = offMS;
+                    lightState.brightnessMode = (byte) brightnessMode;
                     mVintfLights.setLightState(mHwLight.id, lightState);
-                } catch (RemoteException | UnsupportedOperationException ex) {
-                    Slog.e(TAG, "Failed issuing setLightState", ex);
-                } finally {
-                    Trace.traceEnd(Trace.TRACE_TAG_POWER);
-                }
-            } else {
-                try {
+                } else {
                     setLight_native(mHwLight.id, color, mode, onMS, offMS, brightnessMode);
-                } finally {
-                    Trace.traceEnd(Trace.TRACE_TAG_POWER);
                 }
+            } catch (RemoteException | UnsupportedOperationException ex) {
+                Slog.e(TAG, "Failed issuing setLightState", ex);
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_POWER);
             }
         }
 
@@ -412,8 +406,14 @@
             return mVrModeEnabled && mUseLowPersistenceForVR;
         }
 
-        private HwLight getHwLight() {
-            return mHwLight;
+        /**
+         * Returns whether a light is system-use-only or should be accessible to
+         * applications using the {@link android.hardware.lights.LightsManager} API.
+         */
+        private boolean isSystemLight() {
+            // LIGHT_ID_COUNT comes from the 2.0 HIDL HAL and only contains system lights.
+            // Newly-added lights are made available via the public LightsManager API.
+            return (0 <= mHwLight.type && mHwLight.type < LightsManager.LIGHT_ID_COUNT);
         }
 
         private int getColor() {
@@ -451,39 +451,40 @@
     }
 
     private void populateAvailableLights(Context context) {
-        mLights = new LightImpl[LightsManager.LIGHT_ID_COUNT];
-        mLightsById = new SparseArray<>();
-
         if (mVintfLights != null) {
-            try {
-                for (HwLight availableLight : mVintfLights.getLights()) {
-                    LightImpl light = new LightImpl(context, availableLight);
-                    int type = (int) availableLight.type;
-                    if (0 <= type && type < mLights.length && mLights[type] == null) {
-                        mLights[type] = light;
-                    }
-                    mLightsById.put(availableLight.id, light);
-                }
-            } catch (RemoteException ex) {
-                Slog.e(TAG, "Unable to get lights for initialization", ex);
-            }
+            populateAvailableLightsFromAidl(context);
+        } else {
+            populateAvailableLightsFromHidl(context);
         }
 
-        // In the case where only the old HAL is available, all lights will be initialized here
-        for (int i = 0; i < mLights.length; i++) {
-            if (mLights[i] == null) {
-                // The ordinal can be anything if there is only 1 light of each type. Set it to 1.
-                HwLight light = new HwLight();
-                light.id = (byte) i;
-                light.ordinal = 1;
-                light.type = (byte) i;
-
-                mLights[i] = new LightImpl(context, light);
-                mLightsById.put(i, mLights[i]);
+        for (int i = mLightsById.size() - 1; i >= 0; i--) {
+            final int type = mLightsById.keyAt(i);
+            if (0 <= type && type < mLightsByType.length) {
+                mLightsByType[type] = mLightsById.valueAt(i);
             }
         }
     }
 
+    private void populateAvailableLightsFromAidl(Context context) {
+        try {
+            for (HwLight hwLight : mVintfLights.getLights()) {
+                mLightsById.put(hwLight.id, new LightImpl(context, hwLight));
+            }
+        } catch (RemoteException ex) {
+            Slog.e(TAG, "Unable to get lights from HAL", ex);
+        }
+    }
+
+    private void populateAvailableLightsFromHidl(Context context) {
+        for (int i = 0; i < mLightsByType.length; i++) {
+            HwLight hwLight = new HwLight();
+            hwLight.id = (byte) i;
+            hwLight.ordinal = 1;
+            hwLight.type = (byte) i;
+            mLightsById.put(hwLight.id, new LightImpl(context, hwLight));
+        }
+    }
+
     @Override
     public void onStart() {
         publishLocalService(LightsManager.class, mService);
@@ -505,25 +506,14 @@
     private final LightsManager mService = new LightsManager() {
         @Override
         public LogicalLight getLight(int lightType) {
-            if (mLights != null && 0 <= lightType && lightType < mLights.length) {
-                return mLights[lightType];
+            if (mLightsByType != null && 0 <= lightType && lightType < mLightsByType.length) {
+                return mLightsByType[lightType];
             } else {
                 return null;
             }
         }
     };
 
-    /**
-     * Returns whether a light is system-use-only or should be accessible to
-     * applications using the {@link android.hardware.lights.LightsManager} API.
-     */
-    private static boolean isSystemLight(HwLight light) {
-        // LIGHT_ID_COUNT comes from the 2.0 HIDL HAL and only contains system
-        // lights. Newly added lights will be made available via the
-        // LightsManager API.
-        return 0 <= light.type && light.type < LightsManager.LIGHT_ID_COUNT;
-    }
-
     static native void setLight_native(int light, int color, int mode,
             int onMS, int offMS, int brightnessMode);
 }
diff --git a/services/core/java/com/android/server/location/ExponentialBackOff.java b/services/core/java/com/android/server/location/ExponentialBackOff.java
deleted file mode 100644
index 8c77b21..0000000
--- a/services/core/java/com/android/server/location/ExponentialBackOff.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.android.server.location;
-
-/**
- * A simple implementation of exponential backoff.
- */
-class ExponentialBackOff {
-    private static final int MULTIPLIER = 2;
-    private final long mInitIntervalMillis;
-    private final long mMaxIntervalMillis;
-    private long mCurrentIntervalMillis;
-
-    ExponentialBackOff(long initIntervalMillis, long maxIntervalMillis) {
-        mInitIntervalMillis = initIntervalMillis;
-        mMaxIntervalMillis = maxIntervalMillis;
-
-        mCurrentIntervalMillis = mInitIntervalMillis / MULTIPLIER;
-    }
-
-    long nextBackoffMillis() {
-        if (mCurrentIntervalMillis > mMaxIntervalMillis) {
-            return mMaxIntervalMillis;
-        }
-
-        mCurrentIntervalMillis *= MULTIPLIER;
-        return mCurrentIntervalMillis;
-    }
-
-    void reset() {
-        mCurrentIntervalMillis = mInitIntervalMillis / MULTIPLIER;
-    }
-}
-
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index 11f0685..f665289 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -273,7 +273,7 @@
         }
 
         @Nullable
-        protected TRequest getRequest() {
+        public TRequest getRequest() {
             return mRequest;
         }
     }
diff --git a/services/core/java/com/android/server/location/gnss/ExponentialBackOff.java b/services/core/java/com/android/server/location/gnss/ExponentialBackOff.java
new file mode 100644
index 0000000..05a534f
--- /dev/null
+++ b/services/core/java/com/android/server/location/gnss/ExponentialBackOff.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 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.location.gnss;
+
+/**
+ * A simple implementation of exponential backoff.
+ */
+class ExponentialBackOff {
+    private static final int MULTIPLIER = 2;
+    private final long mInitIntervalMillis;
+    private final long mMaxIntervalMillis;
+    private long mCurrentIntervalMillis;
+
+    ExponentialBackOff(long initIntervalMillis, long maxIntervalMillis) {
+        mInitIntervalMillis = initIntervalMillis;
+        mMaxIntervalMillis = maxIntervalMillis;
+
+        mCurrentIntervalMillis = mInitIntervalMillis / MULTIPLIER;
+    }
+
+    long nextBackoffMillis() {
+        if (mCurrentIntervalMillis > mMaxIntervalMillis) {
+            return mMaxIntervalMillis;
+        }
+
+        mCurrentIntervalMillis *= MULTIPLIER;
+        return mCurrentIntervalMillis;
+    }
+
+    void reset() {
+        mCurrentIntervalMillis = mInitIntervalMillis / MULTIPLIER;
+    }
+}
+
diff --git a/services/core/java/com/android/server/location/GnssAntennaInfoProvider.java b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
similarity index 96%
rename from services/core/java/com/android/server/location/GnssAntennaInfoProvider.java
rename to services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
index bc50ebc..d839095 100644
--- a/services/core/java/com/android/server/location/GnssAntennaInfoProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location;
+package com.android.server.location.gnss;
 
 import android.content.Context;
 import android.location.GnssAntennaInfo;
@@ -23,6 +23,8 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.location.CallerIdentity;
+import com.android.server.location.RemoteListenerHelper;
 
 import java.util.List;
 
diff --git a/services/core/java/com/android/server/location/GnssBatchingProvider.java b/services/core/java/com/android/server/location/gnss/GnssBatchingProvider.java
similarity index 84%
rename from services/core/java/com/android/server/location/GnssBatchingProvider.java
rename to services/core/java/com/android/server/location/gnss/GnssBatchingProvider.java
index f3918ee..f583a3e 100644
--- a/services/core/java/com/android/server/location/GnssBatchingProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssBatchingProvider.java
@@ -1,4 +1,20 @@
-package com.android.server.location;
+/*
+ * Copyright (C) 2020 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.location.gnss;
 
 import android.util.Log;
 
diff --git a/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java b/services/core/java/com/android/server/location/gnss/GnssCapabilitiesProvider.java
similarity index 97%
rename from services/core/java/com/android/server/location/GnssCapabilitiesProvider.java
rename to services/core/java/com/android/server/location/gnss/GnssCapabilitiesProvider.java
index 5c8507f..71b5b33 100644
--- a/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssCapabilitiesProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location;
+package com.android.server.location.gnss;
 
 import android.location.GnssCapabilities;
 import android.util.Log;
diff --git a/services/core/java/com/android/server/location/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
similarity index 99%
rename from services/core/java/com/android/server/location/GnssConfiguration.java
rename to services/core/java/com/android/server/location/gnss/GnssConfiguration.java
index a3523f2..14ab79e 100644
--- a/services/core/java/com/android/server/location/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location;
+package com.android.server.location.gnss;
 
 import android.content.Context;
 import android.os.PersistableBundle;
diff --git a/services/core/java/com/android/server/location/GnssGeofenceProvider.java b/services/core/java/com/android/server/location/gnss/GnssGeofenceProvider.java
similarity index 90%
rename from services/core/java/com/android/server/location/GnssGeofenceProvider.java
rename to services/core/java/com/android/server/location/gnss/GnssGeofenceProvider.java
index a84b0b1..53883b9 100644
--- a/services/core/java/com/android/server/location/GnssGeofenceProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssGeofenceProvider.java
@@ -1,4 +1,20 @@
-package com.android.server.location;
+/*
+ * Copyright (C) 2020 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.location.gnss;
 
 import android.location.IGpsGeofenceHardware;
 import android.util.Log;
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
similarity index 99%
rename from services/core/java/com/android/server/location/GnssLocationProvider.java
rename to services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 58e332a..ad3c8a6 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location;
+package com.android.server.location.gnss;
 
 import android.app.AlarmManager;
 import android.app.AppOpsManager;
@@ -78,8 +78,9 @@
 import com.android.server.DeviceIdleInternal;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
-import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
-import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback;
+import com.android.server.location.AbstractLocationProvider;
+import com.android.server.location.gnss.NtpTimeHelper.InjectNtpTimeCallback;
+import com.android.server.location.gnss.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -653,9 +654,6 @@
         mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
         mTimeoutIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_TIMEOUT), 0);
 
-        mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
-                GnssLocationProvider.this::onNetworkAvailable, mLooper);
-
         // App ops service to keep track of who is accessing the GPS
         mAppOps = mContext.getSystemService(AppOpsManager.class);
 
@@ -677,6 +675,9 @@
         mNIHandler = new GpsNetInitiatedHandler(context,
                 mNetInitiatedListener,
                 mSuplEsEnabled);
+        mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
+                GnssLocationProvider.this::onNetworkAvailable, mLooper, mNIHandler);
+
         sendMessage(INITIALIZE_HANDLER, 0, null);
 
         mGnssStatusListenerHelper = new GnssStatusListenerHelper(mContext, mHandler) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index b57c261..9e64e3a 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -55,14 +55,6 @@
 import com.android.server.LocationManagerServiceUtils.LinkedListenerBase;
 import com.android.server.location.AppForegroundHelper;
 import com.android.server.location.CallerIdentity;
-import com.android.server.location.GnssAntennaInfoProvider;
-import com.android.server.location.GnssBatchingProvider;
-import com.android.server.location.GnssCapabilitiesProvider;
-import com.android.server.location.GnssLocationProvider;
-import com.android.server.location.GnssMeasurementCorrectionsProvider;
-import com.android.server.location.GnssMeasurementsProvider;
-import com.android.server.location.GnssNavigationMessageProvider;
-import com.android.server.location.GnssStatusListenerHelper;
 import com.android.server.location.LocationUsageLogger;
 import com.android.server.location.RemoteListenerHelper;
 import com.android.server.location.SettingsHelper;
diff --git a/services/core/java/com/android/server/location/GnssMeasurementCorrectionsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementCorrectionsProvider.java
similarity index 98%
rename from services/core/java/com/android/server/location/GnssMeasurementCorrectionsProvider.java
rename to services/core/java/com/android/server/location/gnss/GnssMeasurementCorrectionsProvider.java
index 82528ca..ac165d1 100644
--- a/services/core/java/com/android/server/location/GnssMeasurementCorrectionsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementCorrectionsProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location;
+package com.android.server.location.gnss;
 
 import android.location.GnssMeasurementCorrections;
 import android.os.Handler;
diff --git a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
similarity index 96%
rename from services/core/java/com/android/server/location/GnssMeasurementsProvider.java
rename to services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 6ba5f07..76c3ad0 100644
--- a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location;
+package com.android.server.location.gnss;
 
 import android.content.Context;
 import android.location.GnssMeasurementsEvent;
@@ -26,6 +26,8 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.location.CallerIdentity;
+import com.android.server.location.RemoteListenerHelper;
 
 /**
  * An base implementation for GPS measurements provider. It abstracts out the responsibility of
diff --git a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
similarity index 96%
rename from services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
rename to services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
index fb901e8..722be3d 100644
--- a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location;
+package com.android.server.location.gnss;
 
 import android.content.Context;
 import android.location.GnssNavigationMessage;
@@ -24,6 +24,8 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.location.CallerIdentity;
+import com.android.server.location.RemoteListenerHelper;
 
 /**
  * An base implementation for GPS navigation messages provider.
diff --git a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
similarity index 80%
rename from services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
rename to services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
index 93227bd..3fb713b 100644
--- a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location;
+package com.android.server.location.gnss;
 
 import android.content.Context;
 import android.database.Cursor;
@@ -29,12 +29,22 @@
 import android.provider.Telephony.Carriers;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.PreciseCallState;
+import android.telephony.PhoneStateListener;
 import android.util.Log;
 
+import com.android.internal.location.GpsNetInitiatedHandler;
+
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.Arrays;
+import java.util.Map;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Iterator;
 
 /**
  * Handles network connection requests and network state change updates for AGPS data download.
@@ -86,6 +96,9 @@
     private HashMap<Network, NetworkAttributes> mAvailableNetworkAttributes =
             new HashMap<>(HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS);
 
+    // Phone State Listeners to track all the active sub IDs
+    private HashMap<Integer, SubIdPhoneStateListener> mPhoneStateListeners;
+
     private final ConnectivityManager mConnMgr;
 
     private final Handler mHandler;
@@ -94,6 +107,9 @@
     private int mAGpsDataConnectionState;
     private InetAddress mAGpsDataConnectionIpAddr;
     private int mAGpsType;
+    private int mActiveSubId = -1;
+    private final GpsNetInitiatedHandler mNiHandler;
+
 
     private final Context mContext;
 
@@ -166,18 +182,109 @@
 
     GnssNetworkConnectivityHandler(Context context,
             GnssNetworkListener gnssNetworkListener,
-            Looper looper) {
+            Looper looper,
+            GpsNetInitiatedHandler niHandler) {
         mContext = context;
         mGnssNetworkListener = gnssNetworkListener;
 
+    SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class);
+        if (subManager != null) {
+            subManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener);
+        }
+
         PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
 
         mHandler = new Handler(looper);
+        mNiHandler = niHandler;
         mConnMgr = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
         mSuplConnectivityCallback = createSuplConnectivityCallback();
     }
 
+    /**
+     * SubId Phone State Listener is used cache the last active Sub ID when a call is made,
+     * which will be used during an emergency call to set the Network Specifier to the particular
+     * sub when an emergency supl connection is requested
+     */
+    private final class SubIdPhoneStateListener extends PhoneStateListener {
+        private Integer mSubId;
+        SubIdPhoneStateListener(Integer subId) {
+            mSubId = subId;
+        }
+        @Override
+        public void onPreciseCallStateChanged(PreciseCallState state) {
+            if (state.PRECISE_CALL_STATE_ACTIVE == state.getForegroundCallState()) {
+                mActiveSubId = mSubId;
+                if (DEBUG) Log.d(TAG, "mActiveSubId: " + mActiveSubId);
+            }
+        }
+    };
+
+    /**
+     * Subscription Changed Listener is used to get all active subscriptions and create a
+     * Phone State Listener for each Sub ID that we find in the active subscription list
+     */
+    private final SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangeListener
+            = new SubscriptionManager.OnSubscriptionsChangedListener() {
+        @Override
+        public void onSubscriptionsChanged() {
+            if (mPhoneStateListeners == null) {
+                // Capacity=2 Load-Factor=1.0, as typically no more than 2 SIMs
+                mPhoneStateListeners = new HashMap<Integer, SubIdPhoneStateListener>(2,1);
+            }
+            SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class);
+            TelephonyManager telManager = mContext.getSystemService(TelephonyManager.class);
+            if (subManager != null && telManager != null) {
+                List<SubscriptionInfo> subscriptionInfoList =
+                        subManager.getActiveSubscriptionInfoList();
+                HashSet<Integer> activeSubIds = new HashSet<Integer>();
+                if (subscriptionInfoList != null) {
+                    if (DEBUG) Log.d(TAG, "Active Sub List size: " + subscriptionInfoList.size());
+                    // populate phone state listeners with all new active subs
+                    for (SubscriptionInfo subInfo : subscriptionInfoList) {
+                        activeSubIds.add(subInfo.getSubscriptionId());
+                        if (!mPhoneStateListeners.containsKey(subInfo.getSubscriptionId())) {
+                            TelephonyManager subIdTelManager =
+                                    telManager.createForSubscriptionId(subInfo.getSubscriptionId());
+                            if (subIdTelManager != null) {
+                                if (DEBUG) Log.d(TAG, "Listener sub" + subInfo.getSubscriptionId());
+                                SubIdPhoneStateListener subIdPhoneStateListener =
+                                        new SubIdPhoneStateListener(subInfo.getSubscriptionId());
+                                mPhoneStateListeners.put(subInfo.getSubscriptionId(),
+                                        subIdPhoneStateListener);
+                                subIdTelManager.listen(subIdPhoneStateListener,
+                                        PhoneStateListener.LISTEN_PRECISE_CALL_STATE);
+                            }
+                        }
+                    }
+                }
+                // clean up phone state listeners than no longer have active subs
+                Iterator<Map.Entry<Integer, SubIdPhoneStateListener> > iterator =
+                        mPhoneStateListeners.entrySet().iterator();
+                while (iterator.hasNext()) {
+                    Map.Entry<Integer, SubIdPhoneStateListener> element = iterator.next();
+                    if (!activeSubIds.contains(element.getKey())) {
+                        TelephonyManager subIdTelManager =
+                                telManager.createForSubscriptionId(element.getKey());
+                        if (subIdTelManager != null) {
+                            if (DEBUG) Log.d(TAG, "unregister listener sub " + element.getKey());
+                            subIdTelManager.listen(element.getValue(),
+                                                   PhoneStateListener.LISTEN_NONE);
+                            // removes the element from mPhoneStateListeners
+                            iterator.remove();
+                        } else {
+                            Log.e(TAG, "Telephony Manager for Sub " + element.getKey() + " null");
+                        }
+                    }
+                }
+                // clean up cached active phone call sub if it is no longer an active sub
+                if (!activeSubIds.contains(mActiveSubId)) {
+                    mActiveSubId = -1;
+                }
+            }
+        }
+    };
+
     void registerNetworkCallbacks() {
         // register for connectivity change events.
         NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
@@ -467,6 +574,12 @@
         NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
         networkRequestBuilder.addCapability(getNetworkCapability(mAGpsType));
         networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+        // During an emergency call, and when we have cached the Active Sub Id, we set the
+        // Network Specifier so that the network request goes to the correct Sub Id
+        if (mNiHandler.getInEmergency() && mActiveSubId >= 0) {
+            if (DEBUG) Log.d(TAG, "Adding Network Specifier: " + Integer.toString(mActiveSubId));
+            networkRequestBuilder.setNetworkSpecifier(Integer.toString(mActiveSubId));
+        }
         NetworkRequest networkRequest = networkRequestBuilder.build();
         mConnMgr.requestNetwork(
                 networkRequest,
@@ -598,6 +711,15 @@
         }
         TelephonyManager phone = (TelephonyManager)
                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        // During an emergency call with an active sub id, get the Telephony Manager specific
+        // to the active sub to get the correct value from getServiceState and getNetworkType
+        if (mNiHandler.getInEmergency() && mActiveSubId >= 0) {
+            TelephonyManager subIdTelManager =
+                    phone.createForSubscriptionId(mActiveSubId);
+            if (subIdTelManager != null) {
+                phone = subIdTelManager;
+            }
+        }
         ServiceState serviceState = phone.getServiceState();
         String projection = null;
         String selection = null;
diff --git a/services/core/java/com/android/server/location/GnssPositionMode.java b/services/core/java/com/android/server/location/gnss/GnssPositionMode.java
similarity index 69%
rename from services/core/java/com/android/server/location/GnssPositionMode.java
rename to services/core/java/com/android/server/location/gnss/GnssPositionMode.java
index 36838fc..045118a 100644
--- a/services/core/java/com/android/server/location/GnssPositionMode.java
+++ b/services/core/java/com/android/server/location/gnss/GnssPositionMode.java
@@ -1,4 +1,20 @@
-package com.android.server.location;
+/*
+ * Copyright (C) 2020 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.location.gnss;
 
 import java.util.Arrays;
 
diff --git a/services/core/java/com/android/server/location/GnssSatelliteBlacklistHelper.java b/services/core/java/com/android/server/location/gnss/GnssSatelliteBlacklistHelper.java
similarity index 83%
rename from services/core/java/com/android/server/location/GnssSatelliteBlacklistHelper.java
rename to services/core/java/com/android/server/location/gnss/GnssSatelliteBlacklistHelper.java
index eb99a85..dccef9b 100644
--- a/services/core/java/com/android/server/location/GnssSatelliteBlacklistHelper.java
+++ b/services/core/java/com/android/server/location/gnss/GnssSatelliteBlacklistHelper.java
@@ -1,4 +1,20 @@
-package com.android.server.location;
+/*
+ * Copyright (C) 2020 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.location.gnss;
 
 import android.content.ContentResolver;
 import android.content.Context;
diff --git a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java b/services/core/java/com/android/server/location/gnss/GnssStatusListenerHelper.java
similarity index 93%
rename from services/core/java/com/android/server/location/GnssStatusListenerHelper.java
rename to services/core/java/com/android/server/location/gnss/GnssStatusListenerHelper.java
index 1d16c03..d2ecdee 100644
--- a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/gnss/GnssStatusListenerHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -11,16 +11,19 @@
  * 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
+ * limitations under the License.
  */
 
-package com.android.server.location;
+package com.android.server.location.gnss;
 
 import android.content.Context;
 import android.location.IGnssStatusListener;
 import android.os.Handler;
 import android.util.Log;
 
+import com.android.server.location.CallerIdentity;
+import com.android.server.location.RemoteListenerHelper;
+
 /**
  * Implementation of a handler for {@link IGnssStatusListener}.
  */
diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java
similarity index 99%
rename from services/core/java/com/android/server/location/GnssVisibilityControl.java
rename to services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java
index 2b5fc79..06fa0ea 100644
--- a/services/core/java/com/android/server/location/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/gnss/GnssVisibilityControl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location;
+package com.android.server.location.gnss;
 
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
diff --git a/services/core/java/com/android/server/location/GpsPsdsDownloader.java b/services/core/java/com/android/server/location/gnss/GpsPsdsDownloader.java
similarity index 98%
rename from services/core/java/com/android/server/location/GpsPsdsDownloader.java
rename to services/core/java/com/android/server/location/gnss/GpsPsdsDownloader.java
index 6fcb7d1..273f9cb 100644
--- a/services/core/java/com/android/server/location/GpsPsdsDownloader.java
+++ b/services/core/java/com/android/server/location/gnss/GpsPsdsDownloader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location;
+package com.android.server.location.gnss;
 
 import android.net.TrafficStats;
 import android.text.TextUtils;
diff --git a/services/core/java/com/android/server/location/NtpTimeHelper.java b/services/core/java/com/android/server/location/gnss/NtpTimeHelper.java
similarity index 90%
rename from services/core/java/com/android/server/location/NtpTimeHelper.java
rename to services/core/java/com/android/server/location/gnss/NtpTimeHelper.java
index d2296ea..2bbb61f 100644
--- a/services/core/java/com/android/server/location/NtpTimeHelper.java
+++ b/services/core/java/com/android/server/location/gnss/NtpTimeHelper.java
@@ -1,4 +1,20 @@
-package com.android.server.location;
+/*
+ * Copyright (C) 2020 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.location.gnss;
 
 import android.content.Context;
 import android.net.ConnectivityManager;
@@ -12,6 +28,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.location.gnss.ExponentialBackOff;
 
 import java.util.Date;
 
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 9bbbc3b..6aae62e 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -708,7 +708,7 @@
         }
 
         List<RoutingSessionInfo> sessionInfos = new ArrayList<>();
-        for (MediaRoute2Provider provider : managerRecord.mUserRecord.mHandler.mMediaProviders) {
+        for (MediaRoute2Provider provider : managerRecord.mUserRecord.mHandler.mRouteProviders) {
             sessionInfos.addAll(provider.getSessionInfos());
         }
         return sessionInfos;
@@ -1059,7 +1059,7 @@
 
         //TODO: Make this thread-safe.
         private final SystemMediaRoute2Provider mSystemProvider;
-        private final ArrayList<MediaRoute2Provider> mMediaProviders =
+        private final ArrayList<MediaRoute2Provider> mRouteProviders =
                 new ArrayList<>();
 
         private final List<MediaRoute2ProviderInfo> mLastProviderInfos = new ArrayList<>();
@@ -1074,7 +1074,7 @@
             mServiceRef = new WeakReference<>(service);
             mUserRecord = userRecord;
             mSystemProvider = new SystemMediaRoute2Provider(service.mContext, this);
-            mMediaProviders.add(mSystemProvider);
+            mRouteProviders.add(mSystemProvider);
             mWatcher = new MediaRoute2ProviderWatcher(service.mContext, this,
                     this, mUserRecord.mUserId);
         }
@@ -1097,13 +1097,13 @@
         @Override
         public void onAddProviderService(@NonNull MediaRoute2ProviderServiceProxy proxy) {
             proxy.setCallback(this);
-            mMediaProviders.add(proxy);
+            mRouteProviders.add(proxy);
             proxy.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference);
         }
 
         @Override
         public void onRemoveProviderService(@NonNull MediaRoute2ProviderServiceProxy proxy) {
-            mMediaProviders.remove(proxy);
+            mRouteProviders.remove(proxy);
         }
 
         @Override
@@ -1148,10 +1148,10 @@
 
         //TODO: notify session info updates
         private void onProviderStateChangedOnHandler(@NonNull MediaRoute2Provider provider) {
-            int providerIndex = getProviderInfoIndex(provider.getUniqueId());
+            int providerInfoIndex = getLastProviderInfoIndex(provider.getUniqueId());
             MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo();
             MediaRoute2ProviderInfo prevInfo =
-                    (providerIndex < 0) ? null : mLastProviderInfos.get(providerIndex);
+                    (providerInfoIndex < 0) ? null : mLastProviderInfos.get(providerInfoIndex);
 
             if (Objects.equals(prevInfo, providerInfo)) return;
 
@@ -1171,7 +1171,7 @@
                             this, getRouters(), new ArrayList<>(removedRoutes)));
                 }
             } else {
-                mLastProviderInfos.set(providerIndex, providerInfo);
+                mLastProviderInfos.set(providerInfoIndex, providerInfo);
                 List<MediaRoute2Info> addedRoutes = new ArrayList<>();
                 List<MediaRoute2Info> removedRoutes = new ArrayList<>();
                 List<MediaRoute2Info> changedRoutes = new ArrayList<>();
@@ -1219,7 +1219,7 @@
             }
         }
 
-        private int getProviderInfoIndex(@NonNull String providerId) {
+        private int getLastProviderInfoIndex(@NonNull String providerId) {
             for (int i = 0; i < mLastProviderInfos.size(); i++) {
                 MediaRoute2ProviderInfo providerInfo = mLastProviderInfos.get(i);
                 if (TextUtils.equals(providerInfo.getUniqueId(), providerId)) {
@@ -1795,13 +1795,13 @@
                         new RouteDiscoveryPreference.Builder(discoveryPreferences)
                         .build();
             }
-            for (MediaRoute2Provider provider : mMediaProviders) {
+            for (MediaRoute2Provider provider : mRouteProviders) {
                 provider.updateDiscoveryPreference(mUserRecord.mCompositeDiscoveryPreference);
             }
         }
 
         private MediaRoute2Provider findProvider(@Nullable String providerId) {
-            for (MediaRoute2Provider provider : mMediaProviders) {
+            for (MediaRoute2Provider provider : mRouteProviders) {
                 if (TextUtils.equals(provider.getUniqueId(), providerId)) {
                     return provider;
                 }
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index c7d14e0..c69787d 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -54,6 +54,7 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE";
+    static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE";
     static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION";
 
     private final AudioManager mAudioManager;
@@ -67,14 +68,17 @@
             SystemMediaRoute2Provider.class.getName());
 
     private String mSelectedRouteId;
+    // For apps without MODIFYING_AUDIO_ROUTING permission.
+    // This should be the currently selected route.
     MediaRoute2Info mDefaultRoute;
+    MediaRoute2Info mDeviceRoute;
     final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
 
     final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
         @Override
         public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
             mHandler.post(() -> {
-                updateDefaultRoute(newRoutes);
+                updateDeviceRoute(newRoutes);
                 notifyProviderState();
             });
         }
@@ -97,7 +101,7 @@
             newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
         } catch (RemoteException e) {
         }
-        updateDefaultRoute(newAudioRoutes);
+        updateDeviceRoute(newAudioRoutes);
 
         mBtRouteProvider = BluetoothRouteProvider.getInstance(context, (routes) -> {
             publishProviderState();
@@ -109,7 +113,6 @@
             }
         });
         updateSessionInfosIfNeeded();
-
         mContext.registerReceiver(new VolumeChangeReceiver(),
                 new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
 
@@ -150,7 +153,7 @@
 
     @Override
     public void transferToRoute(long requestId, String sessionId, String routeId) {
-        if (TextUtils.equals(routeId, mDefaultRoute.getId())) {
+        if (TextUtils.equals(routeId, mDeviceRoute.getId())) {
             mBtRouteProvider.transferTo(null);
         } else {
             mBtRouteProvider.transferTo(routeId);
@@ -170,7 +173,11 @@
         // Do nothing since we don't support grouping volume yet.
     }
 
-    private void updateDefaultRoute(AudioRoutesInfo newRoutes) {
+    public MediaRoute2Info getDefaultRoute() {
+        return mDefaultRoute;
+    }
+
+    private void updateDeviceRoute(AudioRoutesInfo newRoutes) {
         int name = R.string.default_audio_route_name;
         if (newRoutes != null) {
             mCurAudioRoutesInfo.mainType = newRoutes.mainType;
@@ -185,8 +192,8 @@
                 name = com.android.internal.R.string.default_audio_route_name_usb;
             }
         }
-        mDefaultRoute = new MediaRoute2Info.Builder(
-                DEFAULT_ROUTE_ID, mContext.getResources().getText(name).toString())
+        mDeviceRoute = new MediaRoute2Info.Builder(
+                DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString())
                 .setVolumeHandling(mAudioManager.isVolumeFixed()
                         ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
                         : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
@@ -203,7 +210,7 @@
 
     private void updateProviderState() {
         MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
-        builder.addRoute(mDefaultRoute);
+        builder.addRoute(mDeviceRoute);
         if (mBtRouteProvider != null) {
             for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) {
                 builder.addRoute(route);
@@ -228,11 +235,12 @@
 
             MediaRoute2Info selectedRoute = mBtRouteProvider.getSelectedRoute();
             if (selectedRoute == null) {
-                selectedRoute = mDefaultRoute;
+                selectedRoute = mDeviceRoute;
             } else {
-                builder.addTransferableRoute(mDefaultRoute.getId());
+                builder.addTransferableRoute(mDeviceRoute.getId());
             }
             mSelectedRouteId = selectedRoute.getId();
+            mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute).build();
             builder.addSelectedRoute(mSelectedRouteId);
 
             for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
@@ -282,8 +290,8 @@
                     AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
 
             if (newVolume != oldVolume) {
-                if (TextUtils.equals(mDefaultRoute.getId(), mSelectedRouteId)) {
-                    mDefaultRoute = new MediaRoute2Info.Builder(mDefaultRoute)
+                if (TextUtils.equals(mDeviceRoute.getId(), mSelectedRouteId)) {
+                    mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)
                             .setVolume(newVolume)
                             .build();
                 } else {
diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java
index 7792123..2fa80cd 100644
--- a/services/core/java/com/android/server/notification/BubbleExtractor.java
+++ b/services/core/java/com/android/server/notification/BubbleExtractor.java
@@ -76,7 +76,8 @@
                 record.setAllowBubble(appCanShowBubble);
             }
         }
-        final boolean applyFlag = mBubbleChecker.isNotificationAppropriateToBubble(record);
+        final boolean applyFlag = mBubbleChecker.isNotificationAppropriateToBubble(record)
+                && !record.isFlagBubbleRemoved();
         if (applyFlag) {
             record.getNotification().flags |= FLAG_BUBBLE;
         } else {
@@ -157,8 +158,8 @@
         }
 
         /**
-         * @return whether the user has enabled the provided notification to bubble, does not
-         * account for policy.
+         * @return whether the user has enabled the provided notification to bubble, and if the
+         * developer has provided valid information for the notification to bubble.
          */
         @VisibleForTesting
         boolean canBubble(NotificationRecord r, String pkg, int userId) {
@@ -184,8 +185,17 @@
             }
 
             String shortcutId = metadata.getShortcutId();
-            boolean shortcutValid = shortcutId != null
-                    && mShortcutHelper.hasValidShortcutInfo(shortcutId, pkg, r.getUser());
+            String notificationShortcutId = r.getShortcutInfo() != null
+                    ? r.getShortcutInfo().getId()
+                    : null;
+            boolean shortcutValid = false;
+            if (notificationShortcutId != null && shortcutId != null) {
+                // NoMan already checks validity of shortcut, just check if they match.
+                shortcutValid = shortcutId.equals(notificationShortcutId);
+            } else if (shortcutId != null) {
+                shortcutValid =
+                        mShortcutHelper.getValidShortcutInfo(shortcutId, pkg, r.getUser()) != null;
+            }
             if (metadata.getIntent() == null && !shortcutValid) {
                 // Should have a shortcut if intent is null
                 logBubbleError(r.getKey(),
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 35dec5a..ebc1bc4 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -107,6 +107,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -139,6 +140,7 @@
 import android.app.usage.UsageStatsManagerInternal;
 import android.companion.ICompanionDeviceManager;
 import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentProvider;
@@ -387,10 +389,9 @@
      * still post toasts created with
      * {@link android.widget.Toast#makeText(Context, CharSequence, int)} and its variants while
      * in the background.
-     *
-     * TODO(b/144152069): Add @EnabledAfter(Q) to target R+ after assessing impact on dogfood
      */
     @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
     private static final long CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK = 128611929L;
 
     private IActivityManager mAm;
@@ -1213,10 +1214,12 @@
                         // apps querying noMan will know that their notification is not showing
                         // as a bubble.
                         r.getNotification().flags &= ~FLAG_BUBBLE;
+                        r.setFlagBubbleRemoved(true);
                     } else {
                         // Enqueue will trigger resort & if the flag is allowed to be true it'll
                         // be applied there.
                         r.getNotification().flags |= FLAG_ONLY_ALERT_ONCE;
+                        r.setFlagBubbleRemoved(false);
                         mHandler.post(new EnqueueNotificationRunnable(r.getUser().getIdentifier(),
                                 r, isAppForeground));
                     }
@@ -1579,7 +1582,9 @@
                 }
             } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
                 // turn off LED when user passes through lock screen
-                mNotificationLight.turnOff();
+                if (mNotificationLight != null) {
+                    mNotificationLight.turnOff();
+                }
             } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
                 mUserProfiles.updateCache(context);
@@ -2751,24 +2756,18 @@
         @Override
         public void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration,
                 int displayId, @Nullable ITransientNotificationCallback callback) {
-            enqueueToast(pkg, token, text, null, duration, displayId, callback, false);
+            enqueueToast(pkg, token, text, null, duration, displayId, callback);
         }
 
         @Override
         public void enqueueToast(String pkg, IBinder token, ITransientNotification callback,
                 int duration, int displayId) {
-            enqueueToast(pkg, token, null, callback, duration, displayId, null, true);
-        }
-
-        @Override
-        public void enqueueTextOrCustomToast(String pkg, IBinder token,
-                ITransientNotification callback, int duration, int displayId, boolean isCustom) {
-            enqueueToast(pkg, token, null, callback, duration, displayId, null, isCustom);
+            enqueueToast(pkg, token, null, callback, duration, displayId, null);
         }
 
         private void enqueueToast(String pkg, IBinder token, @Nullable CharSequence text,
                 @Nullable ITransientNotification callback, int duration, int displayId,
-                @Nullable ITransientNotificationCallback textCallback, boolean isCustom) {
+                @Nullable ITransientNotificationCallback textCallback) {
             if (DBG) {
                 Slog.i(TAG, "enqueueToast pkg=" + pkg + " token=" + token
                         + " duration=" + duration + " displayId=" + displayId);
@@ -2807,11 +2806,15 @@
             }
 
             boolean isAppRenderedToast = (callback != null);
-            if (isAppRenderedToast && isCustom && !isSystemToast
-                    && !isPackageInForegroundForToast(pkg, callingUid)) {
+            if (isAppRenderedToast && !isSystemToast && !isPackageInForegroundForToast(pkg,
+                    callingUid)) {
                 boolean block;
                 long id = Binder.clearCallingIdentity();
                 try {
+                    // CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK is gated on targetSdk, so block will be
+                    // false for apps with targetSdk < R. For apps with targetSdk R+, text toasts
+                    // are not app-rendered, so isAppRenderedToast == true means it's a custom
+                    // toast.
                     block = mPlatformCompat.isChangeEnabledByPackageName(
                             CHANGE_BACKGROUND_CUSTOM_TOAST_BLOCK, pkg,
                             callingUser.getIdentifier());
@@ -2824,11 +2827,6 @@
                     Binder.restoreCallingIdentity(id);
                 }
                 if (block) {
-                    // TODO(b/144152069): Remove informative toast
-                    mUiHandler.post(() -> Toast.makeText(getContext(),
-                            "Background custom toast blocked for package " + pkg + ".\n"
-                                    + "See g.co/dev/toast.",
-                            Toast.LENGTH_SHORT).show());
                     Slog.w(TAG, "Blocking custom toast from package " + pkg
                             + " due to package not in the foreground");
                     return;
@@ -2847,20 +2845,18 @@
                         record = mToastQueue.get(index);
                         record.update(duration);
                     } else {
-                        // Limit the number of toasts that any given package except the android
-                        // package can enqueue.  Prevents DOS attacks and deals with leaks.
-                        if (!isSystemToast) {
-                            int count = 0;
-                            final int N = mToastQueue.size();
-                            for (int i = 0; i < N; i++) {
-                                final ToastRecord r = mToastQueue.get(i);
-                                if (r.pkg.equals(pkg)) {
-                                    count++;
-                                    if (count >= MAX_PACKAGE_NOTIFICATIONS) {
-                                        Slog.e(TAG, "Package has already posted " + count
-                                                + " toasts. Not showing more. Package=" + pkg);
-                                        return;
-                                    }
+                        // Limit the number of toasts that any given package can enqueue.
+                        // Prevents DOS attacks and deals with leaks.
+                        int count = 0;
+                        final int N = mToastQueue.size();
+                        for (int i = 0; i < N; i++) {
+                            final ToastRecord r = mToastQueue.get(i);
+                            if (r.pkg.equals(pkg)) {
+                                count++;
+                                if (count >= MAX_PACKAGE_NOTIFICATIONS) {
+                                    Slog.e(TAG, "Package has already posted " + count
+                                            + " toasts. Not showing more. Package=" + pkg);
+                                    return;
                                 }
                             }
                         }
@@ -3453,7 +3449,7 @@
             ArrayList<ConversationChannelWrapper> conversations =
                     mPreferencesHelper.getConversations(onlyImportant);
             for (ConversationChannelWrapper conversation : conversations) {
-                conversation.setShortcutInfo(mShortcutHelper.getShortcutInfo(
+                conversation.setShortcutInfo(mShortcutHelper.getValidShortcutInfo(
                         conversation.getNotificationChannel().getConversationId(),
                         conversation.getPkg(),
                         UserHandle.of(UserHandle.getUserId(conversation.getUid()))));
@@ -3476,7 +3472,7 @@
             ArrayList<ConversationChannelWrapper> conversations =
                     mPreferencesHelper.getConversations(pkg, uid);
             for (ConversationChannelWrapper conversation : conversations) {
-                conversation.setShortcutInfo(mShortcutHelper.getShortcutInfo(
+                conversation.setShortcutInfo(mShortcutHelper.getValidShortcutInfo(
                         conversation.getNotificationChannel().getConversationId(),
                         pkg,
                         UserHandle.of(UserHandle.getUserId(uid))));
@@ -3638,13 +3634,23 @@
         }
 
         /**
+         * @deprecated Use {@link #getActiveNotificationsWithAttribution(String, String)} instead.
+         */
+        @Deprecated
+        @Override
+        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
+            return getActiveNotificationsWithAttribution(callingPkg, null);
+        }
+
+        /**
          * System-only API for getting a list of current (i.e. not cleared) notifications.
          *
          * Requires ACCESS_NOTIFICATIONS which is signature|system.
          * @returns A list of all the notifications, in natural order.
          */
         @Override
-        public StatusBarNotification[] getActiveNotifications(String callingPkg) {
+        public StatusBarNotification[] getActiveNotificationsWithAttribution(String callingPkg,
+                String callingAttributionTag) {
             // enforce() will ensure the calling uid has the correct permission
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.ACCESS_NOTIFICATIONS,
@@ -3654,7 +3660,8 @@
             int uid = Binder.getCallingUid();
 
             // noteOp will check to make sure the callingPkg matches the uid
-            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
+            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
+                    callingAttributionTag, null)
                     == AppOpsManager.MODE_ALLOWED) {
                 synchronized (mNotificationLock) {
                     tmp = new StatusBarNotification[mNotificationList.size()];
@@ -3736,12 +3743,24 @@
         }
 
         /**
-         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
+         * @deprecated Use {@link #getHistoricalNotificationsWithAttribution} instead.
          */
+        @Deprecated
         @Override
         @RequiresPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
         public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count,
                 boolean includeSnoozed) {
+            return getHistoricalNotificationsWithAttribution(callingPkg, null, count,
+                    includeSnoozed);
+        }
+
+        /**
+         * System-only API for getting a list of recent (cleared, no longer shown) notifications.
+         */
+        @Override
+        @RequiresPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
+        public StatusBarNotification[] getHistoricalNotificationsWithAttribution(String callingPkg,
+                String callingAttributionTag, int count, boolean includeSnoozed) {
             // enforce() will ensure the calling uid has the correct permission
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.ACCESS_NOTIFICATIONS,
@@ -3751,7 +3770,8 @@
             int uid = Binder.getCallingUid();
 
             // noteOp will check to make sure the callingPkg matches the uid
-            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
+            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
+                    callingAttributionTag, null)
                     == AppOpsManager.MODE_ALLOWED) {
                 synchronized (mArchive) {
                     tmp = mArchive.getArray(count, includeSnoozed);
@@ -3767,7 +3787,8 @@
         @Override
         @WorkerThread
         @RequiresPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
-        public NotificationHistory getNotificationHistory(String callingPkg) {
+        public NotificationHistory getNotificationHistory(String callingPkg,
+                String callingAttributionTag) {
             // enforce() will ensure the calling uid has the correct permission
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.ACCESS_NOTIFICATIONS,
@@ -3775,7 +3796,8 @@
             int uid = Binder.getCallingUid();
 
             // noteOp will check to make sure the callingPkg matches the uid
-            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg)
+            if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg,
+                    callingAttributionTag, null)
                     == AppOpsManager.MODE_ALLOWED) {
                 IntArray currentUserIds = mUserProfiles.getCurrentProfileIds();
                 Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryReadHistory");
@@ -4174,7 +4196,7 @@
         @Override
         public int getInterruptionFilterFromListener(INotificationListener token)
                 throws RemoteException {
-            synchronized (mNotificationLight) {
+            synchronized (mNotificationLock) {
                 return mInterruptionFilter;
             }
         }
@@ -5617,6 +5639,7 @@
         final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
         r.setIsAppImportanceLocked(mPreferencesHelper.getIsAppImportanceLocked(pkg, callingUid));
         r.setPostSilently(postSilently);
+        r.setFlagBubbleRemoved(false);
 
         if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
             final boolean fgServiceShown = channel.isFgServiceShown();
@@ -5647,7 +5670,8 @@
             }
         }
 
-        r.setShortcutInfo(mShortcutHelper.getShortcutInfo(notification.getShortcutId(), pkg, user));
+        r.setShortcutInfo(mShortcutHelper.getValidShortcutInfo(
+                notification.getShortcutId(), pkg, user));
 
         if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
                 r.getSbn().getOverrideGroupKey() != null)) {
@@ -6753,7 +6777,7 @@
         if (canShowLightsLocked(record, aboveThreshold)) {
             mLights.add(key);
             updateLightsLocked();
-            if (mUseAttentionLight) {
+            if (mUseAttentionLight && mAttentionLight != null) {
                 mAttentionLight.pulse();
             }
             blink = true;
@@ -7954,6 +7978,10 @@
     @GuardedBy("mNotificationLock")
     void updateLightsLocked()
     {
+        if (mNotificationLight == null) {
+            return;
+        }
+
         // handle notification lights
         NotificationRecord ledNotification = null;
         while (ledNotification == null && !mLights.isEmpty()) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 3f24b38..54a0f9f 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -187,6 +187,7 @@
     private boolean mSuggestionsGeneratedByAssistant;
     private boolean mEditChoicesBeforeSending;
     private boolean mHasSeenSmartReplies;
+    private boolean mFlagBubbleRemoved;
     private boolean mPostSilently;
     /**
      * Whether this notification (and its channels) should be considered user locked. Used in
@@ -1201,6 +1202,19 @@
         return stats.hasBeenVisiblyExpanded();
     }
 
+    /**
+     * When the bubble state on a notif changes due to user action (e.g. dismiss a bubble) then
+     * this value is set until an update or bubble change event due to user action (e.g. create
+     * bubble from sysui)
+     **/
+    public boolean isFlagBubbleRemoved() {
+        return mFlagBubbleRemoved;
+    }
+
+    public void setFlagBubbleRemoved(boolean flagBubbleRemoved) {
+        mFlagBubbleRemoved = flagBubbleRemoved;
+    }
+
     public void setSystemGeneratedSmartActions(
             ArrayList<Notification.Action> systemGeneratedSmartActions) {
         mSystemGeneratedSmartActions = systemGeneratedSmartActions;
diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java
index 7bbb3b1..f1ce3a7 100644
--- a/services/core/java/com/android/server/notification/ShortcutHelper.java
+++ b/services/core/java/com/android/server/notification/ShortcutHelper.java
@@ -121,7 +121,10 @@
         mLauncherAppsService = launcherApps;
     }
 
-    ShortcutInfo getShortcutInfo(String shortcutId, String packageName, UserHandle user) {
+    /**
+     * Only returns shortcut info if it's found and if it's {@link ShortcutInfo#isLongLived()}.
+     */
+    ShortcutInfo getValidShortcutInfo(String shortcutId, String packageName, UserHandle user) {
         if (mLauncherAppsService == null) {
             return null;
         }
@@ -135,20 +138,15 @@
             query.setShortcutIds(Arrays.asList(shortcutId));
             query.setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_CACHED);
             List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(query, user);
-            return shortcuts != null && shortcuts.size() > 0
+            ShortcutInfo info = shortcuts != null && shortcuts.size() > 0
                     ? shortcuts.get(0)
                     : null;
+            return info != null && info.isLongLived() ? info : null;
         } finally {
             Binder.restoreCallingIdentity(token);
         }
     }
 
-    boolean hasValidShortcutInfo(String shortcutId, String packageName,
-            UserHandle user) {
-        ShortcutInfo shortcutInfo = getShortcutInfo(shortcutId, packageName, user);
-        return shortcutInfo != null && shortcutInfo.isLongLived();
-    }
-
     /**
      * Shortcut based bubbles require some extra work to listen for shortcut changes.
      *
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index c37ea8b..c97f33b 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -16,22 +16,22 @@
 
 package com.android.server.pm;
 
+import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.apex.ApexInfo;
 import android.apex.ApexInfoList;
 import android.apex.ApexSessionInfo;
 import android.apex.ApexSessionParams;
 import android.apex.IApexService;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
+import android.content.pm.parsing.PackageInfoWithoutStateUtils;
 import android.os.Environment;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -44,8 +44,9 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
+import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.utils.TimingsTraceAndSlog;
 
@@ -61,7 +62,9 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
 import java.util.stream.Collectors;
 
 /**
@@ -72,7 +75,7 @@
 
     private static final String TAG = "ApexManager";
 
-    static final int MATCH_ACTIVE_PACKAGE = 1 << 0;
+    public static final int MATCH_ACTIVE_PACKAGE = 1 << 0;
     static final int MATCH_FACTORY_PACKAGE = 1 << 1;
 
     private static final Singleton<ApexManager> sApexManagerSingleton =
@@ -139,7 +142,14 @@
      */
     public abstract List<ActiveApexInfo> getActiveApexInfos();
 
-    abstract void systemReady(Context context);
+    /**
+     * Called by package manager service to scan apex package files when device boots up.
+     *
+     * @param packageParser The package parser which supports caches.
+     * @param executorService An executor to support parallel package parsing.
+     */
+    abstract void scanApexPackagesTraced(@NonNull PackageParser2 packageParser,
+            @NonNull ExecutorService executorService);
 
     /**
      * Retrieves information about an APEX package.
@@ -154,7 +164,7 @@
      *         is not found.
      */
     @Nullable
-    abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags);
+    public abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags);
 
     /**
      * Retrieves information about all active APEX packages.
@@ -189,6 +199,27 @@
     abstract boolean isApexPackage(String packageName);
 
     /**
+     * Whether the APEX package is pre-installed or not.
+     *
+     * @param packageInfo the package to check
+     * @return {@code true} if this package is pre-installed, {@code false} otherwise.
+     */
+    public static boolean isFactory(@NonNull PackageInfo packageInfo) {
+        return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+    }
+
+    /**
+     * Returns the active apex package's name that contains the (apk) package.
+     *
+     * @param containedPackage The (apk) package that might be in a apex
+     * @return the apex package's name of {@code null} if the {@code containedPackage} is not inside
+     *         any apex.
+     */
+    @Nullable
+    public abstract String getActiveApexPackageNameContainingPackage(
+            @NonNull AndroidPackage containedPackage);
+
+    /**
      * Retrieves information about an apexd staged session i.e. the internal state used by apexd to
      * track the different states of a session.
      *
@@ -342,7 +373,7 @@
          * difference between {@code packageName} and {@code apexModuleName}.
          */
         @GuardedBy("mLock")
-        private Map<String, List<String>> mApksInApex = new ArrayMap<>();
+        private ArrayMap<String, List<String>> mApksInApex = new ArrayMap<>();
 
         @GuardedBy("mLock")
         private List<PackageInfo> mAllPackagesCache;
@@ -357,7 +388,7 @@
          * the apk container to {@code apexModuleName} of the apex-payload inside.
          */
         @GuardedBy("mLock")
-        private Map<String, String> mPackageNameToApexModuleName;
+        private ArrayMap<String, String> mPackageNameToApexModuleName;
 
         ApexManagerImpl(IApexService apexService) {
             mApexService = apexService;
@@ -373,16 +404,6 @@
             return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
         }
 
-        /**
-         * Whether the APEX package is pre-installed or not.
-         *
-         * @param packageInfo the package to check
-         * @return {@code true} if this package is pre-installed, {@code false} otherwise.
-         */
-        private static boolean isFactory(PackageInfo packageInfo) {
-            return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
-        }
-
         @Override
         public List<ActiveApexInfo> getActiveApexInfos() {
             final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
@@ -411,104 +432,94 @@
         }
 
         @Override
-        void systemReady(Context context) {
-            context.registerReceiver(new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    // Post populateAllPackagesCacheIfNeeded to a background thread, since it's
-                    // expensive to run it in broadcast handler thread.
-                    BackgroundThread.getHandler().post(() -> populateAllPackagesCacheIfNeeded());
-                    context.unregisterReceiver(this);
+        void scanApexPackagesTraced(@NonNull PackageParser2 packageParser,
+                @NonNull ExecutorService executorService) {
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanApexPackagesTraced");
+            try {
+                synchronized (mLock) {
+                    scanApexPackagesInternalLocked(packageParser, executorService);
                 }
-            }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
-        }
-
-        private void populatePackageNameToApexModuleNameIfNeeded() {
-            synchronized (mLock) {
-                if (mPackageNameToApexModuleName != null) {
-                    return;
-                }
-                try {
-                    mPackageNameToApexModuleName = new ArrayMap<>();
-                    final ApexInfo[] allPkgs = mApexService.getAllPackages();
-                    for (int i = 0; i < allPkgs.length; i++) {
-                        ApexInfo ai = allPkgs[i];
-                        PackageParser.PackageLite pkgLite;
-                        try {
-                            File apexFile = new File(ai.modulePath);
-                            pkgLite = PackageParser.parsePackageLite(apexFile, 0);
-                        } catch (PackageParser.PackageParserException pe) {
-                            throw new IllegalStateException("Unable to parse: "
-                                    + ai.modulePath, pe);
-                        }
-                        mPackageNameToApexModuleName.put(pkgLite.packageName, ai.moduleName);
-                    }
-                } catch (RemoteException re) {
-                    Slog.e(TAG, "Unable to retrieve packages from apexservice: ", re);
-                    throw new RuntimeException(re);
-                }
+            } finally {
+                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             }
         }
 
-        private void populateAllPackagesCacheIfNeeded() {
-            synchronized (mLock) {
-                if (mAllPackagesCache != null) {
-                    return;
-                }
-                try {
-                    mAllPackagesCache = new ArrayList<>();
-                    HashSet<String> activePackagesSet = new HashSet<>();
-                    HashSet<String> factoryPackagesSet = new HashSet<>();
-                    final ApexInfo[] allPkgs = mApexService.getAllPackages();
-                    for (ApexInfo ai : allPkgs) {
-                        // If the device is using flattened APEX, don't report any APEX
-                        // packages since they won't be managed or updated by PackageManager.
-                        if ((new File(ai.modulePath)).isDirectory()) {
-                            break;
-                        }
-                        int flags = PackageManager.GET_META_DATA
-                                | PackageManager.GET_SIGNING_CERTIFICATES
-                                | PackageManager.GET_SIGNATURES;
-                        PackageParser.Package pkg;
-                        try {
-                            File apexFile = new File(ai.modulePath);
-                            PackageParser pp = new PackageParser();
-                            pkg = pp.parsePackage(apexFile, flags, false);
-                            PackageParser.collectCertificates(pkg, false);
-                        } catch (PackageParser.PackageParserException pe) {
-                            throw new IllegalStateException("Unable to parse: " + ai, pe);
-                        }
+        @GuardedBy("mLock")
+        private void scanApexPackagesInternalLocked(PackageParser2 packageParser,
+                ExecutorService executorService) {
+            final ApexInfo[] allPkgs;
+            try {
+                mAllPackagesCache = new ArrayList<>();
+                mPackageNameToApexModuleName = new ArrayMap<>();
+                allPkgs = mApexService.getAllPackages();
+            } catch (RemoteException re) {
+                Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
+                throw new RuntimeException(re);
+            }
+            if (allPkgs.length == 0) {
+                return;
+            }
+            int flags = PackageManager.GET_META_DATA
+                    | PackageManager.GET_SIGNING_CERTIFICATES
+                    | PackageManager.GET_SIGNATURES;
+            ArrayMap<File, ApexInfo> parsingApexInfo = new ArrayMap<>();
+            ParallelPackageParser parallelPackageParser =
+                    new ParallelPackageParser(packageParser, executorService);
 
-                        final PackageInfo packageInfo =
-                                PackageParser.generatePackageInfo(pkg, ai, flags);
-                        mAllPackagesCache.add(packageInfo);
-                        if (ai.isActive) {
-                            if (activePackagesSet.contains(packageInfo.packageName)) {
-                                throw new IllegalStateException(
-                                        "Two active packages have the same name: "
-                                                + packageInfo.packageName);
-                            }
-                            activePackagesSet.add(packageInfo.packageName);
-                        }
-                        if (ai.isFactory) {
-                            if (factoryPackagesSet.contains(packageInfo.packageName)) {
-                                throw new IllegalStateException(
-                                        "Two factory packages have the same name: "
-                                                + packageInfo.packageName);
-                            }
-                            factoryPackagesSet.add(packageInfo.packageName);
-                        }
+            for (ApexInfo ai : allPkgs) {
+                File apexFile = new File(ai.modulePath);
+                parallelPackageParser.submit(apexFile, flags);
+                parsingApexInfo.put(apexFile, ai);
+            }
+
+            HashSet<String> activePackagesSet = new HashSet<>();
+            HashSet<String> factoryPackagesSet = new HashSet<>();
+            // Process results one by one
+            for (int i = 0; i < parsingApexInfo.size(); i++) {
+                ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
+                Throwable throwable = parseResult.throwable;
+                ApexInfo ai = parsingApexInfo.get(parseResult.scanFile);
+
+                if (throwable == null) {
+                    final PackageInfo packageInfo = PackageInfoWithoutStateUtils.generate(
+                            parseResult.parsedPackage, ai, flags);
+                    if (packageInfo == null) {
+                        throw new IllegalStateException("Unable to generate package info: "
+                                + ai.modulePath);
                     }
-                } catch (RemoteException re) {
-                    Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString());
-                    throw new RuntimeException(re);
+                    mAllPackagesCache.add(packageInfo);
+                    mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName);
+                    if (ai.isActive) {
+                        if (activePackagesSet.contains(packageInfo.packageName)) {
+                            throw new IllegalStateException(
+                                    "Two active packages have the same name: "
+                                            + packageInfo.packageName);
+                        }
+                        activePackagesSet.add(packageInfo.packageName);
+                    }
+                    if (ai.isFactory) {
+                        if (factoryPackagesSet.contains(packageInfo.packageName)) {
+                            throw new IllegalStateException(
+                                    "Two factory packages have the same name: "
+                                            + packageInfo.packageName);
+                        }
+                        factoryPackagesSet.add(packageInfo.packageName);
+                    }
+                } else if (throwable instanceof PackageParser.PackageParserException) {
+                    throw new IllegalStateException("Unable to parse: " + ai.modulePath, throwable);
+                } else {
+                    throw new IllegalStateException("Unexpected exception occurred while parsing "
+                            + ai.modulePath, throwable);
                 }
             }
         }
 
         @Override
-        @Nullable PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags) {
-            populateAllPackagesCacheIfNeeded();
+        @Nullable
+        public PackageInfo getPackageInfo(String packageName,
+                @PackageInfoFlags int flags) {
+            Preconditions.checkState(mAllPackagesCache != null,
+                    "APEX packages have not been scanned");
             boolean matchActive = (flags & MATCH_ACTIVE_PACKAGE) != 0;
             boolean matchFactory = (flags & MATCH_FACTORY_PACKAGE) != 0;
             for (PackageInfo packageInfo: mAllPackagesCache) {
@@ -525,7 +536,8 @@
 
         @Override
         List<PackageInfo> getActivePackages() {
-            populateAllPackagesCacheIfNeeded();
+            Preconditions.checkState(mAllPackagesCache != null,
+                    "APEX packages have not been scanned");
             return mAllPackagesCache
                     .stream()
                     .filter(item -> isActive(item))
@@ -534,7 +546,8 @@
 
         @Override
         List<PackageInfo> getFactoryPackages() {
-            populateAllPackagesCacheIfNeeded();
+            Preconditions.checkState(mAllPackagesCache != null,
+                    "APEX packages have not been scanned");
             return mAllPackagesCache
                     .stream()
                     .filter(item -> isFactory(item))
@@ -543,7 +556,8 @@
 
         @Override
         List<PackageInfo> getInactivePackages() {
-            populateAllPackagesCacheIfNeeded();
+            Preconditions.checkState(mAllPackagesCache != null,
+                    "APEX packages have not been scanned");
             return mAllPackagesCache
                     .stream()
                     .filter(item -> !isActive(item))
@@ -553,7 +567,8 @@
         @Override
         boolean isApexPackage(String packageName) {
             if (!isApexSupported()) return false;
-            populateAllPackagesCacheIfNeeded();
+            Preconditions.checkState(mAllPackagesCache != null,
+                    "APEX packages have not been scanned");
             for (PackageInfo packageInfo : mAllPackagesCache) {
                 if (packageInfo.packageName.equals(packageName)) {
                     return true;
@@ -563,6 +578,36 @@
         }
 
         @Override
+        @Nullable
+        public String getActiveApexPackageNameContainingPackage(
+                @NonNull AndroidPackage containedPackage) {
+            Preconditions.checkState(mPackageNameToApexModuleName != null,
+                    "APEX packages have not been scanned");
+
+            Objects.requireNonNull(containedPackage);
+
+            synchronized (mLock) {
+                int numApksInApex = mApksInApex.size();
+                for (int apkInApexNum = 0; apkInApexNum < numApksInApex; apkInApexNum++) {
+                    if (mApksInApex.valueAt(apkInApexNum).contains(
+                            containedPackage.getPackageName())) {
+                        String apexModuleName = mApksInApex.keyAt(apkInApexNum);
+
+                        int numApexPkgs = mPackageNameToApexModuleName.size();
+                        for (int apexPkgNum = 0; apexPkgNum < numApexPkgs; apexPkgNum++) {
+                            if (mPackageNameToApexModuleName.valueAt(apexPkgNum).equals(
+                                    apexModuleName)) {
+                                return mPackageNameToApexModuleName.keyAt(apexPkgNum);
+                            }
+                        }
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        @Override
         @Nullable ApexSessionInfo getStagedSessionInfo(int sessionId) {
             try {
                 ApexSessionInfo apexSessionInfo = mApexService.getStagedSessionInfo(sessionId);
@@ -684,8 +729,9 @@
 
         @Override
         List<String> getApksInApex(String apexPackageName) {
-            populatePackageNameToApexModuleNameIfNeeded();
             synchronized (mLock) {
+                Preconditions.checkState(mPackageNameToApexModuleName != null,
+                        "APEX packages have not been scanned");
                 String moduleName = mPackageNameToApexModuleName.get(apexPackageName);
                 if (moduleName == null) {
                     return Collections.emptyList();
@@ -697,17 +743,19 @@
         @Override
         @Nullable
         public String getApexModuleNameForPackageName(String apexPackageName) {
-            populatePackageNameToApexModuleNameIfNeeded();
             synchronized (mLock) {
+                Preconditions.checkState(mPackageNameToApexModuleName != null,
+                        "APEX packages have not been scanned");
                 return mPackageNameToApexModuleName.get(apexPackageName);
             }
         }
 
         @Override
         public long snapshotCeData(int userId, int rollbackId, String apexPackageName) {
-            populatePackageNameToApexModuleNameIfNeeded();
             String apexModuleName;
             synchronized (mLock) {
+                Preconditions.checkState(mPackageNameToApexModuleName != null,
+                        "APEX packages have not been scanned");
                 apexModuleName = mPackageNameToApexModuleName.get(apexPackageName);
             }
             if (apexModuleName == null) {
@@ -724,9 +772,10 @@
 
         @Override
         public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) {
-            populatePackageNameToApexModuleNameIfNeeded();
             String apexModuleName;
             synchronized (mLock) {
+                Preconditions.checkState(mPackageNameToApexModuleName != null,
+                        "APEX packages have not been scanned");
                 apexModuleName = mPackageNameToApexModuleName.get(apexPackageName);
             }
             if (apexModuleName == null) {
@@ -797,15 +846,7 @@
         void dump(PrintWriter pw, @Nullable String packageName) {
             final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
             try {
-                populateAllPackagesCacheIfNeeded();
                 ipw.println();
-                ipw.println("Active APEX packages:");
-                dumpFromPackagesCache(getActivePackages(), packageName, ipw);
-                ipw.println("Inactive APEX packages:");
-                dumpFromPackagesCache(getInactivePackages(), packageName, ipw);
-                ipw.println("Factory APEX packages:");
-                dumpFromPackagesCache(getFactoryPackages(), packageName, ipw);
-                ipw.increaseIndent();
                 ipw.println("APEX session state:");
                 ipw.increaseIndent();
                 final ApexSessionInfo[] sessions = mApexService.getSessions();
@@ -834,6 +875,17 @@
                     ipw.decreaseIndent();
                 }
                 ipw.decreaseIndent();
+                ipw.println();
+                if (mAllPackagesCache == null) {
+                    ipw.println("APEX packages have not been scanned");
+                    return;
+                }
+                ipw.println("Active APEX packages:");
+                dumpFromPackagesCache(getActivePackages(), packageName, ipw);
+                ipw.println("Inactive APEX packages:");
+                dumpFromPackagesCache(getInactivePackages(), packageName, ipw);
+                ipw.println("Factory APEX packages:");
+                dumpFromPackagesCache(getFactoryPackages(), packageName, ipw);
             } catch (RemoteException e) {
                 ipw.println("Couldn't communicate with apexd.");
             }
@@ -879,12 +931,13 @@
         }
 
         @Override
-        void systemReady(Context context) {
+        void scanApexPackagesTraced(@NonNull PackageParser2 packageParser,
+                @NonNull ExecutorService executorService) {
             // No-op
         }
 
         @Override
-        PackageInfo getPackageInfo(String packageName, int flags) {
+        public PackageInfo getPackageInfo(String packageName, int flags) {
             return null;
         }
 
@@ -909,6 +962,15 @@
         }
 
         @Override
+        @Nullable
+        public String getActiveApexPackageNameContainingPackage(
+                @NonNull AndroidPackage containedPackage) {
+            Objects.requireNonNull(containedPackage);
+
+            return null;
+        }
+
+        @Override
         ApexSessionInfo getStagedSessionInfo(int sessionId) {
             throw new UnsupportedOperationException();
         }
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 7069818..4087675 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -64,6 +64,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
     private static final String TAG = "CrossProfileAppsService";
@@ -447,7 +448,7 @@
         final int uid = mInjector.getPackageManager()
                 .getPackageUidAsUser(packageName, /* flags= */ 0, userId);
         if (currentModeEquals(newMode, packageName, uid)) {
-            Slog.w(TAG, "Attempt to set mode to existing value of " + newMode + " for "
+            Slog.i(TAG, "Attempt to set mode to existing value of " + newMode + " for "
                     + packageName + " on user ID " + userId);
             return;
         }
@@ -577,6 +578,24 @@
         setInteractAcrossProfilesAppOp(packageName, AppOpsManager.opToDefaultMode(op));
     }
 
+    @Override
+    public void clearInteractAcrossProfilesAppOps() {
+        final int defaultMode =
+                AppOpsManager.opToDefaultMode(
+                        AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES));
+        findAllPackageNames()
+                .forEach(packageName -> setInteractAcrossProfilesAppOp(packageName, defaultMode));
+    }
+
+    private List<String> findAllPackageNames() {
+        return mInjector.getPackageManagerInternal()
+                .getInstalledApplications(
+                        /* flags= */ 0, mInjector.getCallingUserId(), mInjector.getCallingUid())
+                .stream()
+                .map(applicationInfo -> applicationInfo.packageName)
+                .collect(Collectors.toList());
+    }
+
     CrossProfileAppsInternal getLocalService() {
         return mLocalService;
     }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 2ff3d2a..8ff7ea9 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -490,6 +490,14 @@
             throw new SecurityException("User restriction prevents installing");
         }
 
+        if (params.dataLoaderParams != null
+                && mContext.checkCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2)
+                        != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("You need the "
+                    + "com.android.permission.USE_INSTALLER_V2 permission "
+                    + "to use a data loader");
+        }
+
         String requestedInstallerPackageName = params.installerPackageName != null
                 ? params.installerPackageName : installerPackageName;
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 348a4cb..1248ec0 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2479,12 +2479,14 @@
 
     @Override
     public DataLoaderParamsParcel getDataLoaderParams() {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
         return params.dataLoaderParams != null ? params.dataLoaderParams.getData() : null;
     }
 
     @Override
     public void addFile(int location, String name, long lengthBytes, byte[] metadata,
             byte[] signature) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
         if (!isDataLoaderInstallation()) {
             throw new IllegalStateException(
                     "Cannot add files to non-data loader installation session.");
@@ -2517,6 +2519,7 @@
 
     @Override
     public void removeFile(int location, String name) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.USE_INSTALLER_V2, null);
         if (!isDataLoaderInstallation()) {
             throw new IllegalStateException(
                     "Cannot add files to non-data loader installation session.");
@@ -3242,8 +3245,7 @@
                     new ComponentName(
                             readStringAttribute(in, ATTR_DATALOADER_PACKAGE_NAME),
                             readStringAttribute(in, ATTR_DATALOADER_CLASS_NAME)),
-                    readStringAttribute(in, ATTR_DATALOADER_ARGUMENTS),
-                    null);
+                    readStringAttribute(in, ATTR_DATALOADER_ARGUMENTS));
         }
 
         final File appIconFile = buildAppIconFile(sessionId, sessionsDir);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b79f75a..34363c8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -161,6 +161,7 @@
 import android.content.pm.FallbackCategoryProvider;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IDexModuleRegisterCallback;
+import android.content.pm.IPackageChangeObserver;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageDeleteObserver2;
@@ -178,6 +179,7 @@
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
 import android.content.pm.ModuleInfo;
+import android.content.pm.PackageChangeEvent;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageInstaller;
@@ -812,6 +814,10 @@
 
     private final OverlayConfig mOverlayConfig;
 
+    @GuardedBy("itself")
+    final private ArrayList<IPackageChangeObserver> mPackageChangeObservers =
+        new ArrayList<>();
+
     /**
      * Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
      *
@@ -2894,6 +2900,9 @@
                     mMetrics, mCacheDir, mPackageParserCallback);
 
             ExecutorService executorService = ParallelPackageParser.makeExecutorService();
+            // Prepare apex package info before scanning APKs, these information are needed when
+            // scanning apk in apex.
+            mApexManager.scanApexPackagesTraced(packageParser, executorService);
             // Collect vendor/product/system_ext overlay packages. (Do this before scanning
             // any apps.)
             // For security and version matching reason, only consider overlay packages if they
@@ -5064,15 +5073,16 @@
      * action and a {@code android.intent.category.BROWSABLE} category</li>
      * </ul>
      */
-    int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps) {
+    int updateFlagsForResolve(int flags, int userId, int callingUid, boolean wantInstantApps,
+            boolean matchSystemOnly) {
         return updateFlagsForResolve(flags, userId, callingUid,
-                wantInstantApps, false /*onlyExposedExplicitly*/);
+                wantInstantApps, matchSystemOnly, false /*onlyExposedExplicitly*/);
     }
 
     int updateFlagsForResolve(int flags, int userId, int callingUid,
-            boolean wantInstantApps, boolean onlyExposedExplicitly) {
+            boolean wantInstantApps, boolean onlyExposedExplicitly, boolean matchSystemOnly) {
         // Safe mode means we shouldn't match any third-party components
-        if (mSafeMode) {
+        if (mSafeMode || matchSystemOnly) {
             flags |= PackageManager.MATCH_SYSTEM_ONLY;
         }
         if (getInstantAppPackageName(callingUid) != null) {
@@ -6164,7 +6174,8 @@
 
             if (!mUserManager.exists(userId)) return null;
             final int callingUid = Binder.getCallingUid();
-            flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart);
+            flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
+                    intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
             mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                     false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
 
@@ -6196,7 +6207,8 @@
         intent = updateIntentForResolve(intent);
         final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
         final int flags = updateFlagsForResolve(
-                0, userId, callingUid, false /*includeInstantApps*/);
+                0, userId, callingUid, false /*includeInstantApps*/,
+                intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
         final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags,
                 userId);
         synchronized (mLock) {
@@ -6517,7 +6529,8 @@
                 android.provider.Settings.Global.getInt(mContext.getContentResolver(),
                         android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1;
         flags = updateFlagsForResolve(
-                flags, userId, callingUid, false /*includeInstantApps*/);
+                flags, userId, callingUid, false /*includeInstantApps*/,
+                intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
         intent = updateIntentForResolve(intent);
         // writer
         synchronized (mLock) {
@@ -6729,7 +6742,8 @@
             }
             synchronized (mLock) {
                 int flags = updateFlagsForResolve(0, parent.id, callingUid,
-                        false /*includeInstantApps*/);
+                        false /*includeInstantApps*/,
+                        intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
                 CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr(
                         intent, resolvedType, flags, sourceUserId, parent.id);
                 return xpDomainInfo != null;
@@ -6815,7 +6829,8 @@
         }
 
         flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
-                comp != null || pkgName != null /*onlyExposedExplicitly*/);
+                comp != null || pkgName != null /*onlyExposedExplicitly*/,
+                intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
         if (comp != null) {
             final List<ResolveInfo> list = new ArrayList<>(1);
             final ActivityInfo ai = getActivityInfo(comp, flags, userId);
@@ -7600,7 +7615,8 @@
             String resolvedType, int flags, int userId) {
         if (!mUserManager.exists(userId)) return Collections.emptyList();
         final int callingUid = Binder.getCallingUid();
-        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+                intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 false /*requireFullPermission*/, false /*checkShell*/,
                 "query intent activity options");
@@ -7786,7 +7802,8 @@
                 false /*requireFullPermission*/, false /*checkShell*/,
                 "query intent receivers");
         final String instantAppPkgName = getInstantAppPackageName(callingUid);
-        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+                intent.isImplicitImageCaptureIntent() /*matchSystemOnly*/);
         ComponentName comp = intent.getComponent();
         if (comp == null) {
             if (intent.getSelector() != null) {
@@ -7876,7 +7893,8 @@
     private ResolveInfo resolveServiceInternal(Intent intent, String resolvedType, int flags,
             int userId, int callingUid) {
         if (!mUserManager.exists(userId)) return null;
-        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+                false /* matchSystemOnly */);
         List<ResolveInfo> query = queryIntentServicesInternal(
                 intent, resolvedType, flags, userId, callingUid, false /*includeInstantApps*/);
         if (query != null) {
@@ -7907,7 +7925,8 @@
                 false /*checkShell*/,
                 "query intent receivers");
         final String instantAppPkgName = getInstantAppPackageName(callingUid);
-        flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps);
+        flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
+                false /* matchSystemOnly */);
         ComponentName comp = intent.getComponent();
         if (comp == null) {
             if (intent.getSelector() != null) {
@@ -8044,7 +8063,8 @@
         if (!mUserManager.exists(userId)) return Collections.emptyList();
         final int callingUid = Binder.getCallingUid();
         final String instantAppPkgName = getInstantAppPackageName(callingUid);
-        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/);
+        flags = updateFlagsForResolve(flags, userId, callingUid, false /*includeInstantApps*/,
+                false /* matchSystemOnly */);
         ComponentName comp = intent.getComponent();
         if (comp == null) {
             if (intent.getSelector() != null) {
@@ -16458,9 +16478,56 @@
             // BackgroundDexOptService will remove it from its blacklist.
             // TODO: Layering violation
             BackgroundDexOptService.notifyPackageChanged(packageName);
+
+            notifyPackageChangeObserversOnUpdate(reconciledPkg);
         }
     }
 
+    private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) {
+      final PackageSetting pkgSetting = reconciledPkg.pkgSetting;
+      final PackageInstalledInfo pkgInstalledInfo = reconciledPkg.installResult;
+      final PackageRemovedInfo pkgRemovedInfo = pkgInstalledInfo.removedInfo;
+
+      PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
+      pkgChangeEvent.packageName = pkgSetting.pkg.getPackageName();
+      pkgChangeEvent.version = pkgSetting.versionCode;
+      pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.lastUpdateTime;
+      pkgChangeEvent.newInstalled = (pkgRemovedInfo == null || !pkgRemovedInfo.isUpdate);
+      pkgChangeEvent.dataRemoved = (pkgRemovedInfo != null && pkgRemovedInfo.dataRemoved);
+      pkgChangeEvent.isDeleted = false;
+
+      notifyPackageChangeObservers(pkgChangeEvent);
+    }
+
+    private void notifyPackageChangeObserversOnDelete(String packageName, long version) {
+      PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
+      pkgChangeEvent.packageName = packageName;
+      pkgChangeEvent.version = version;
+      pkgChangeEvent.lastUpdateTimeMillis = 0L;
+      pkgChangeEvent.newInstalled = false;
+      pkgChangeEvent.dataRemoved = false;
+      pkgChangeEvent.isDeleted = true;
+
+      notifyPackageChangeObservers(pkgChangeEvent);
+    }
+
+    private void notifyPackageChangeObservers(PackageChangeEvent event) {
+      try {
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "notifyPackageChangeObservers");
+        synchronized (mPackageChangeObservers) {
+          for(IPackageChangeObserver observer : mPackageChangeObservers) {
+            try {
+              observer.onPackageChanged(event);
+            } catch(RemoteException e) {
+              Log.wtf(TAG, e);
+            }
+          }
+        }
+      } finally {
+        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+      }
+    }
+
     /**
      * The set of data needed to successfully install the prepared package. This includes data that
      * will be used to scan and reconcile the package.
@@ -17521,6 +17588,7 @@
             } catch (RemoteException e) {
                 Log.i(TAG, "Observer no longer exists.");
             } //end catch
+            notifyPackageChangeObserversOnDelete(packageName, versionCode);
         });
     }
 
@@ -20631,7 +20699,6 @@
         storage.registerListener(mStorageListener);
 
         mInstallerService.systemReady();
-        mApexManager.systemReady(mContext);
         mPackageDexOptimizer.systemReady();
 
         mInjector.getStorageManagerInternal().addExternalStoragePolicy(
@@ -22980,8 +23047,49 @@
         }
     }
 
+    private final class PackageChangeObserverDeathRecipient implements IBinder.DeathRecipient {
+        private final IPackageChangeObserver mObserver;
+
+        PackageChangeObserverDeathRecipient(IPackageChangeObserver observer) {
+            mObserver = observer;
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mPackageChangeObservers) {
+                mPackageChangeObservers.remove(mObserver);
+                Log.d(TAG, "Size of mPackageChangeObservers after removing dead observer is "
+                    + mPackageChangeObservers.size());
+            }
+        }
+    }
+
     private class PackageManagerNative extends IPackageManagerNative.Stub {
         @Override
+        public void registerPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
+          synchronized (mPackageChangeObservers) {
+            try {
+                observer.asBinder().linkToDeath(
+                    new PackageChangeObserverDeathRecipient(observer), 0);
+            } catch (RemoteException e) {
+              Log.e(TAG, e.getMessage());
+            }
+            mPackageChangeObservers.add(observer);
+            Log.d(TAG, "Size of mPackageChangeObservers after registry is "
+                + mPackageChangeObservers.size());
+          }
+        }
+
+        @Override
+        public void unregisterPackageChangeObserver(@NonNull IPackageChangeObserver observer) {
+          synchronized (mPackageChangeObservers) {
+            mPackageChangeObservers.remove(observer);
+            Log.d(TAG, "Size of mPackageChangeObservers after unregistry is "
+                + mPackageChangeObservers.size());
+          }
+        }
+
+        @Override
         public String[] getAllPackages() {
             return PackageManagerService.this.getAllPackages().toArray(new String[0]);
         }
@@ -24381,7 +24489,8 @@
         } else {
             synchronized (mLock) {
                 boolean manifestWhitelisted =
-                        mPackages.get(packageName).isAllowDontAutoRevokePermmissions();
+                        mPackages.get(packageName).getAutoRevokePermissions()
+                                == ApplicationInfo.AUTO_REVOKE_DISALLOWED;
                 return manifestWhitelisted;
             }
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 8a9f1b3..4d49a64 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -105,6 +105,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
+import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata;
 
 import dalvik.system.DexFile;
 
@@ -118,7 +119,6 @@
 import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.net.URISyntaxException;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Base64;
 import java.util.Collection;
@@ -3025,9 +3025,9 @@
             // 1. Single file from stdin.
             if (args.isEmpty() || STDIN_PATH.equals(args.get(0))) {
                 final String name = "base." + (isApex ? "apex" : "apk");
-                final String metadata = "-" + name;
+                final Metadata metadata = Metadata.forStdIn(name);
                 session.addFile(LOCATION_DATA_APP, name, sessionSizeBytes,
-                        metadata.getBytes(StandardCharsets.UTF_8), null);
+                        metadata.toByteArray(), null);
                 return 0;
             }
 
@@ -3056,9 +3056,10 @@
 
     private int processArgForStdin(String arg, PackageInstaller.Session session) {
         final String[] fileDesc = arg.split(":");
-        String name, metadata;
+        String name, fileId;
         long sizeBytes;
         byte[] signature = null;
+        int streamingVersion = 0;
 
         try {
             if (fileDesc.length < 2) {
@@ -3067,14 +3068,22 @@
             }
             name = fileDesc[0];
             sizeBytes = Long.parseUnsignedLong(fileDesc[1]);
-            metadata = name;
+            fileId = name;
 
             if (fileDesc.length > 2 && !TextUtils.isEmpty(fileDesc[2])) {
-                metadata = fileDesc[2];
+                fileId = fileDesc[2];
             }
             if (fileDesc.length > 3) {
                 signature = Base64.getDecoder().decode(fileDesc[3]);
             }
+            if (fileDesc.length > 4) {
+                streamingVersion = Integer.parseUnsignedInt(fileDesc[4]);
+                if (streamingVersion < 0 || streamingVersion > 1) {
+                    getErrPrintWriter().println(
+                            "Unsupported streaming version: " + streamingVersion);
+                    return 1;
+                }
+            }
         } catch (IllegalArgumentException e) {
             getErrPrintWriter().println(
                     "Unable to parse file parameters: " + arg + ", reason: " + e);
@@ -3086,9 +3095,14 @@
             return 1;
         }
 
+        final Metadata metadata;
+
         if (signature != null) {
-            // Streaming/adb mode.
-            metadata = "+" + metadata;
+            // Streaming/adb mode. Versions:
+            // 0: data only streaming, tree has to be fully available,
+            // 1: tree and data streaming.
+            metadata = (streamingVersion == 0) ? Metadata.forDataOnlyStreaming(fileId)
+                    : Metadata.forStreaming(fileId);
             try {
                 if (V4Signature.readFrom(signature) == null) {
                     getErrPrintWriter().println("V4 signature is invalid in: " + arg);
@@ -3101,11 +3115,10 @@
             }
         } else {
             // Single-shot read from stdin.
-            metadata = "-" + metadata;
+            metadata = Metadata.forStdIn(fileId);
         }
 
-        session.addFile(LOCATION_DATA_APP, name, sizeBytes,
-                metadata.getBytes(StandardCharsets.UTF_8), signature);
+        session.addFile(LOCATION_DATA_APP, name, sizeBytes, metadata.toByteArray(), signature);
         return 0;
     }
 
@@ -3115,7 +3128,7 @@
         final File file = new File(inPath);
         final String name = file.getName();
         final long size = file.length();
-        final byte[] metadata = inPath.getBytes(StandardCharsets.UTF_8);
+        final Metadata metadata = Metadata.forLocalFile(inPath);
 
         byte[] v4signatureBytes = null;
         // Try to load the v4 signature file for the APK; it might not exist.
@@ -3132,7 +3145,7 @@
             }
         }
 
-        session.addFile(LOCATION_DATA_APP, name, size, metadata, v4signatureBytes);
+        session.addFile(LOCATION_DATA_APP, name, size, metadata.toByteArray(), v4signatureBytes);
     }
 
     private int doWriteSplits(int sessionId, ArrayList<String> splitPaths, long sessionSizeBytes,
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index 6d83d70..2aa6e573 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -24,7 +24,6 @@
 import android.os.ParcelFileDescriptor;
 import android.os.ShellCommand;
 import android.service.dataloader.DataLoaderService;
-import android.text.TextUtils;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -114,6 +113,74 @@
         }
     }
 
+    static class Metadata {
+        /**
+         * Full files read from stdin.
+         */
+        static final byte STDIN = 0;
+        /**
+         * Full files read from local file.
+         */
+        static final byte LOCAL_FILE = 1;
+        /**
+         * Signature tree read from stdin, data streamed.
+         */
+        static final byte DATA_ONLY_STREAMING = 2;
+        /**
+         * Everything streamed.
+         */
+        static final byte STREAMING = 3;
+
+        private final byte mMode;
+        private final String mData;
+
+        static Metadata forStdIn(String fileId) {
+            return new Metadata(STDIN, fileId);
+        }
+
+        static Metadata forLocalFile(String filePath) {
+            return new Metadata(LOCAL_FILE, filePath);
+        }
+
+        static Metadata forDataOnlyStreaming(String fileId) {
+            return new Metadata(DATA_ONLY_STREAMING, fileId);
+        }
+
+        static Metadata forStreaming(String fileId) {
+            return new Metadata(STREAMING, fileId);
+        }
+
+        private Metadata(byte mode, String data) {
+            this.mMode = mode;
+            this.mData = (data == null) ? "" : data;
+        }
+
+        static Metadata fromByteArray(byte[] bytes) throws IOException {
+            if (bytes == null || bytes.length == 0) {
+                return null;
+            }
+            byte mode = bytes[0];
+            String data = new String(bytes, 1, bytes.length - 1, StandardCharsets.UTF_8);
+            return new Metadata(mode, data);
+        }
+
+        byte[] toByteArray() {
+            byte[] dataBytes = this.mData.getBytes(StandardCharsets.UTF_8);
+            byte[] result = new byte[1 + dataBytes.length];
+            result[0] = this.mMode;
+            System.arraycopy(dataBytes, 0, result, 1, dataBytes.length);
+            return result;
+        }
+
+        byte getMode() {
+            return this.mMode;
+        }
+
+        String getData() {
+            return this.mData;
+        }
+    }
+
     private static class DataLoader implements DataLoaderService.DataLoader {
         private DataLoaderParams mParams = null;
         private FileSystemConnector mConnector = null;
@@ -136,19 +203,31 @@
             }
             try {
                 for (InstallationFile file : addedFiles) {
-                    String filePath = new String(file.getMetadata(), StandardCharsets.UTF_8);
-                    if (TextUtils.isEmpty(filePath) || filePath.startsWith(STDIN_PATH)) {
-                        final ParcelFileDescriptor inFd = getStdInPFD(shellCommand);
-                        mConnector.writeData(file.getName(), 0, file.getLengthBytes(), inFd);
-                    } else {
-                        ParcelFileDescriptor incomingFd = null;
-                        try {
-                            incomingFd = getLocalFile(shellCommand, filePath);
-                            mConnector.writeData(file.getName(), 0, incomingFd.getStatSize(),
-                                    incomingFd);
-                        } finally {
-                            IoUtils.closeQuietly(incomingFd);
+                    Metadata metadata = Metadata.fromByteArray(file.getMetadata());
+                    if (metadata == null) {
+                        Slog.e(TAG, "Invalid metadata for file: " + file.getName());
+                        return false;
+                    }
+                    switch (metadata.getMode()) {
+                        case Metadata.STDIN: {
+                            final ParcelFileDescriptor inFd = getStdInPFD(shellCommand);
+                            mConnector.writeData(file.getName(), 0, file.getLengthBytes(), inFd);
+                            break;
                         }
+                        case Metadata.LOCAL_FILE: {
+                            ParcelFileDescriptor incomingFd = null;
+                            try {
+                                incomingFd = getLocalFile(shellCommand, metadata.getData());
+                                mConnector.writeData(file.getName(), 0, incomingFd.getStatSize(),
+                                        incomingFd);
+                            } finally {
+                                IoUtils.closeQuietly(incomingFd);
+                            }
+                            break;
+                        }
+                        default:
+                            Slog.e(TAG, "Unsupported metadata mode: " + metadata.getMode());
+                            return false;
                     }
                 }
                 return true;
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 3cc10d1..5a1e8e2 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -230,6 +230,10 @@
             info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos;
         }
 
+        info.seInfo = AndroidPackageUtils.getSeInfo(pkg, pkgSetting);
+        info.primaryCpuAbi = AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting);
+        info.secondaryCpuAbi = AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting);
+
         info.flags |= appInfoFlags(pkg, pkgSetting);
         info.privateFlags |= appInfoPrivateFlags(pkg, pkgSetting);
         return info;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 79d0c2d..765ecb9 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -21,6 +21,8 @@
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISALLOWED;
+import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED;
 import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
@@ -40,6 +42,7 @@
 import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED;
 import static android.permission.PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED;
 
+import static com.android.server.pm.ApexManager.MATCH_ACTIVE_PACKAGE;
 import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
 import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
 import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS;
@@ -128,6 +131,7 @@
 import com.android.server.ServiceThread;
 import com.android.server.SystemConfig;
 import com.android.server.Watchdog;
+import com.android.server.pm.ApexManager;
 import com.android.server.pm.PackageManagerServiceUtils;
 import com.android.server.pm.PackageSetting;
 import com.android.server.pm.SharedUserSetting;
@@ -3238,31 +3242,25 @@
 
     @Override
     public List<String> getAutoRevokeExemptionRequestedPackages(int userId) {
-        mContext.enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
-                "Must hold " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY);
-
-        List<String> result = new ArrayList<>();
-        mPackageManagerInt.forEachInstalledPackage(pkg -> {
-            if (pkg.isDontAutoRevokePermmissions()) {
-                result.add(pkg.getPackageName());
-            }
-        }, userId);
-
-        return result;
+        return getPackagesWithAutoRevokePolicy(AUTO_REVOKE_DISCOURAGED, userId);
     }
 
     @Override
     public List<String> getAutoRevokeExemptionGrantedPackages(int userId) {
+        return getPackagesWithAutoRevokePolicy(AUTO_REVOKE_DISALLOWED, userId);
+    }
+
+    @NonNull
+    private List<String> getPackagesWithAutoRevokePolicy(int autoRevokePolicy, int userId) {
         mContext.enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
                 "Must hold " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY);
 
         List<String> result = new ArrayList<>();
         mPackageManagerInt.forEachInstalledPackage(pkg -> {
-            if (pkg.isAllowDontAutoRevokePermmissions()) {
+            if (pkg.getAutoRevokePermissions() == autoRevokePolicy) {
                 result.add(pkg.getPackageName());
             }
         }, userId);
-
         return result;
     }
 
@@ -3321,8 +3319,16 @@
         if (!privappPermissionsDisable && privilegedPermission && pkg.isPrivileged()
                 && !platformPackage && platformPermission) {
             if (!hasPrivappWhitelistEntry(perm, pkg)) {
-                // Only report violations for apps on system image
-                if (!mSystemReady && !pkgSetting.getPkgState().isUpdatedSystemApp()) {
+                ApexManager apexMgr = ApexManager.getInstance();
+                String apexContainingPkg = apexMgr.getActiveApexPackageNameContainingPackage(pkg);
+
+                // Only enforce whitelist this on boot
+                if (!mSystemReady
+                        // Updated system apps do not need to be whitelisted
+                        && !pkgSetting.getPkgState().isUpdatedSystemApp()
+                        // Apps that are in updated apexs' do not need to be whitelisted
+                        && (apexContainingPkg == null || apexMgr.isFactory(
+                        apexMgr.getPackageInfo(apexContainingPkg, MATCH_ACTIVE_PACKAGE)))) {
                     // it's only a reportable violation if the permission isn't explicitly denied
                     ArraySet<String> deniedPermissions = null;
                     if (pkg.isVendor()) {
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 161f304..27288d8 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -30,6 +30,7 @@
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
 import android.app.AppOpsManagerInternal;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -47,6 +48,7 @@
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
 import android.permission.PermissionControllerManager;
+import android.provider.Settings;
 import android.provider.Telephony;
 import android.telecom.TelecomManager;
 import android.util.ArrayMap;
@@ -70,7 +72,9 @@
 import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.ExecutionException;
 
 /**
@@ -180,8 +184,6 @@
         intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         intentFilter.addDataScheme("package");
 
-
-        /* TODO ntmyren: enable receiver when test flakes are fixed
         getContext().registerReceiverAsUser(new BroadcastReceiver() {
             final List<Integer> mUserSetupUids = new ArrayList<>(200);
             final Map<UserHandle, PermissionControllerManager> mPermControllerManagers =
@@ -232,7 +234,6 @@
                 manager.updateUserSensitiveForApp(uid);
             }
         }, UserHandle.ALL, intentFilter, null, null);
-         */
     }
 
     /**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 1e12565..c973640 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -49,7 +49,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
@@ -2091,7 +2090,6 @@
                     // Window manager does the checking for this.
                     outAppOp[0] = OP_TOAST_WINDOW;
                     return ADD_OKAY;
-                case TYPE_DREAM:
                 case TYPE_INPUT_METHOD:
                 case TYPE_WALLPAPER:
                 case TYPE_PRESENTATION:
@@ -2230,7 +2228,6 @@
             case TYPE_STATUS_BAR:
             case TYPE_NAVIGATION_BAR:
             case TYPE_WALLPAPER:
-            case TYPE_DREAM:
                 return false;
             default:
                 // Hide only windows below the keyguard host window.
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index 39aeafc..d6c48a0 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -26,6 +26,7 @@
 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.storage.StorageManager.PROP_LEGACY_OP_STICKY;
 
 import static java.lang.Integer.min;
 
@@ -36,6 +37,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.os.Build;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.storage.StorageManagerInternal;
 
@@ -63,6 +65,9 @@
                 }
             };
 
+    private static final boolean isLegacyStorageAppOpStickyGlobal = SystemProperties.getBoolean(
+            PROP_LEGACY_OP_STICKY, /*defaultValue*/true);
+
     /**
      * TargetSDK is per package. To make sure two apps int the same shared UID do not fight over
      * what to set, always compute the combined targetSDK.
@@ -136,9 +141,12 @@
                     shouldPreserveLegacyExternalStorage = pkg.hasPreserveLegacyExternalStorage()
                             && smInternal.hasLegacyExternalStorage(appInfo.uid);
                     targetSDK = getMinimumTargetSDK(context, appInfo, user);
+                    // LEGACY_STORAGE op is normally sticky for apps targetig <= Q.
+                    // However, this device can be configured to make it non-sticky.
+                    boolean isLegacyAppOpSticky = isLegacyStorageAppOpStickyGlobal
+                            && targetSDK <= Build.VERSION_CODES.Q;
                     shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0
-                            || (targetSDK > Build.VERSION_CODES.Q
-                            && !shouldPreserveLegacyExternalStorage);
+                            || (!isLegacyAppOpSticky && !shouldPreserveLegacyExternalStorage);
                 } else {
                     isWhiteListed = false;
                     shouldApplyRestriction = false;
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index efe2af3..7eb3f01 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -31,7 +31,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_DRAG;
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
@@ -806,9 +805,6 @@
                 return  canAddInternalSystemWindow ? 13 : 10;
             case TYPE_APPLICATION_OVERLAY:
                 return  12;
-            case TYPE_DREAM:
-                // used for Dreams (screensavers with TYPE_DREAM windows)
-                return  14;
             case TYPE_INPUT_METHOD:
                 // on-screen keyboards and other such input method user interfaces go here.
                 return  15;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 5025835..86ff926 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3531,7 +3531,9 @@
         }
 
         // Control light outside of lock.
-        light.setFlashing(color, LogicalLight.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0);
+        if (light != null) {
+            light.setFlashing(color, LogicalLight.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0);
+        }
     }
 
     private void setDozeAfterScreenOffInternal(boolean on) {
diff --git a/services/core/java/com/android/server/power/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING
index acf3f79..74958b6 100644
--- a/services/core/java/com/android/server/power/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/TEST_MAPPING
@@ -3,6 +3,7 @@
     {
       "name": "CtsBatterySavingTestCases",
       "options": [
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
         {"exclude-annotation": "androidx.test.filters.LargeTest"},
         {"exclude-annotation": "androidx.test.filters.FlakyTest"}
       ]
@@ -11,6 +12,7 @@
       "name": "FrameworksMockingServicesTests",
       "options": [
         {"include-filter": "com.android.server.power"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
         {"exclude-annotation": "androidx.test.filters.FlakyTest"}
       ]
     },
@@ -18,6 +20,7 @@
       "name": "FrameworksServicesTests",
       "options": [
         {"include-filter": "com.android.server.power"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
         {"exclude-annotation": "androidx.test.filters.FlakyTest"},
         {
           "exclude-filter": "com.android.server.power.PowerManagerServiceTest#testWakefulnessAwake_ShouldWakeUpWhenPluggedIn"
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 74c3a9e..2783d0b 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -767,7 +767,7 @@
         protected boolean connectToHal() {
             synchronized (mHalLock) {
                 try {
-                    mThermalHal10 = android.hardware.thermal.V1_0.IThermal.getService();
+                    mThermalHal10 = android.hardware.thermal.V1_0.IThermal.getService(true);
                     mThermalHal10.linkToDeath(new DeathRecipient(),
                             THERMAL_HAL_DEATH_COOKIE);
                     Slog.i(TAG,
@@ -902,7 +902,7 @@
         protected boolean connectToHal() {
             synchronized (mHalLock) {
                 try {
-                    mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService();
+                    mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService(true);
                     mThermalHal11.linkToDeath(new DeathRecipient(),
                             THERMAL_HAL_DEATH_COOKIE);
                     mThermalHal11.registerThermalCallback(mThermalCallback11);
@@ -1046,7 +1046,7 @@
         protected boolean connectToHal() {
             synchronized (mHalLock) {
                 try {
-                    mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService();
+                    mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService(true);
                     mThermalHal20.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
                     mThermalHal20.registerThermalChangedCallback(mThermalCallback20, false,
                             0 /* not used */);
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 63048f6..06f2d65 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -34,6 +34,7 @@
 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
 import android.media.soundtrigger_middleware.Status;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.util.Log;
@@ -107,6 +108,12 @@
 public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddlewareService, Dumpable {
     private static final String TAG = "SoundTriggerMiddlewareValidation";
 
+    private enum ModuleState {
+        ALIVE,
+        DETACHED,
+        DEAD
+    };
+
     private final @NonNull ISoundTriggerMiddlewareService mDelegate;
     private final @NonNull Context mContext;
     private Map<Integer, Set<ModuleService>> mModules;
@@ -139,6 +146,14 @@
             throw new ServiceSpecificException(((RecoverableException) e).errorCode,
                     e.getMessage());
         }
+
+        /* Throwing an exception is not enough in this case. When the HAL behaves unexpectedly, the
+           system service and the HAL must be reset and the client must be notified. Without a full
+           reset in this catastrophic case, the state of the HAL and the system service cannot be
+           guaranteed to the client.
+         */
+        Log.wtf(TAG, "Crashing system server due to unrecoverable exception", e);
+        Process.killProcess(Process.myPid());
         throw new InternalServerError(e);
     }
 
@@ -372,12 +387,13 @@
         private ISoundTriggerModule mDelegate;
         private @NonNull Map<Integer, ModelState> mLoadedModels = new HashMap<>();
         private final int mHandle;
+        private ModuleState mState = ModuleState.ALIVE;
 
         ModuleService(int handle, @NonNull ISoundTriggerCallback callback) {
             mCallback = callback;
             mHandle = handle;
             try {
-                mCallback.asBinder().linkToDeath(null, 0);
+                mCallback.asBinder().linkToDeath(this, 0);
             } catch (RemoteException e) {
                 throw e.rethrowAsRuntimeException();
             }
@@ -397,7 +413,7 @@
 
             synchronized (SoundTriggerMiddlewareValidation.this) {
                 // State validation.
-                if (mDelegate == null) {
+                if (mState == ModuleState.DETACHED) {
                     throw new IllegalStateException("Module has been detached.");
                 }
 
@@ -421,7 +437,7 @@
 
             synchronized (SoundTriggerMiddlewareValidation.this) {
                 // State validation.
-                if (mDelegate == null) {
+                if (mState == ModuleState.DETACHED) {
                     throw new IllegalStateException("Module has been detached.");
                 }
 
@@ -444,7 +460,7 @@
 
             synchronized (SoundTriggerMiddlewareValidation.this) {
                 // State validation.
-                if (mDelegate == null) {
+                if (mState == ModuleState.DETACHED) {
                     throw new IllegalStateException("Module has been detached.");
                 }
                 ModelState modelState = mLoadedModels.get(
@@ -477,7 +493,7 @@
 
             synchronized (SoundTriggerMiddlewareValidation.this) {
                 // State validation.
-                if (mDelegate == null) {
+                if (mState == ModuleState.DETACHED) {
                     throw new IllegalStateException("Module has been detached.");
                 }
                 ModelState modelState = mLoadedModels.get(
@@ -511,7 +527,7 @@
 
             synchronized (SoundTriggerMiddlewareValidation.this) {
                 // State validation.
-                if (mDelegate == null) {
+                if (mState == ModuleState.DETACHED) {
                     throw new IllegalStateException("Module has been detached.");
                 }
                 ModelState modelState = mLoadedModels.get(
@@ -540,7 +556,7 @@
 
             synchronized (SoundTriggerMiddlewareValidation.this) {
                 // State validation.
-                if (mDelegate == null) {
+                if (mState == ModuleState.DETACHED) {
                     throw new IllegalStateException("Module has been detached.");
                 }
                 ModelState modelState = mLoadedModels.get(
@@ -568,7 +584,7 @@
 
             synchronized (SoundTriggerMiddlewareValidation.this) {
                 // State validation.
-                if (mDelegate == null) {
+                if (mState == ModuleState.DETACHED) {
                     throw new IllegalStateException("Module has been detached.");
                 }
                 ModelState modelState = mLoadedModels.get(
@@ -596,7 +612,7 @@
 
             synchronized (SoundTriggerMiddlewareValidation.this) {
                 // State validation.
-                if (mDelegate == null) {
+                if (mState == ModuleState.DETACHED) {
                     throw new IllegalStateException("Module has been detached.");
                 }
                 ModelState modelState = mLoadedModels.get(
@@ -625,7 +641,7 @@
 
             synchronized (SoundTriggerMiddlewareValidation.this) {
                 // State validation.
-                if (mDelegate == null) {
+                if (mState == ModuleState.DETACHED) {
                     throw new IllegalStateException("Module has been detached.");
                 }
                 ModelState modelState = mLoadedModels.get(
@@ -654,10 +670,10 @@
 
             synchronized (SoundTriggerMiddlewareValidation.this) {
                 // State validation.
-                if (mDelegate == null) {
+                if (mState == ModuleState.DETACHED) {
                     throw new IllegalStateException("Module has already been detached.");
                 }
-                if (!mLoadedModels.isEmpty()) {
+                if (mState == ModuleState.ALIVE && !mLoadedModels.isEmpty()) {
                     throw new IllegalStateException("Cannot detach while models are loaded.");
                 }
 
@@ -673,14 +689,14 @@
         // Override toString() in order to have the delegate's ID in it.
         @Override
         public String toString() {
-            return mDelegate.toString();
+            return Objects.toString(mDelegate.toString());
         }
 
         private void detachInternal() {
             try {
                 mDelegate.detach();
-                mDelegate = null;
-                mCallback.asBinder().unlinkToDeath(null, 0);
+                mState = ModuleState.DETACHED;
+                mCallback.asBinder().unlinkToDeath(this, 0);
                 mModules.get(mHandle).remove(this);
             } catch (RemoteException e) {
                 throw e.rethrowAsRuntimeException();
@@ -688,13 +704,18 @@
         }
 
         void dump(PrintWriter pw) {
-            pw.printf("Loaded models for session %s (handle, active)", toString());
-            pw.println();
-            pw.println("-------------------------------");
-            for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
-                pw.print(entry.getKey());
-                pw.print('\t');
-                pw.print(entry.getValue().activityState.name());
+            if (mState == ModuleState.ALIVE) {
+                pw.printf("Loaded models for session %s (handle, active)", toString());
+                pw.println();
+                pw.println("-------------------------------");
+                for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
+                    pw.print(entry.getKey());
+                    pw.print('\t');
+                    pw.print(entry.getValue().activityState.name());
+                    pw.println();
+                }
+            } else {
+                pw.printf("Session %s is dead", toString());
                 pw.println();
             }
         }
@@ -753,6 +774,7 @@
         public void onModuleDied() {
             synchronized (SoundTriggerMiddlewareValidation.this) {
                 try {
+                    mState = ModuleState.DEAD;
                     mCallback.onModuleDied();
                 } catch (RemoteException e) {
                     // Dead client will be handled by binderDied() - no need to handle here.
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
index b051bab..8ff2a1b 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
@@ -37,9 +37,9 @@
         }
 
         switch (cmd) {
-            case "suggestTelephonyTimeZone":
+            case "suggest_telephony_time_zone":
                 return runSuggestTelephonyTimeZone();
-            case "suggestManualTimeZone":
+            case "suggest_manual_time_zone":
                 return runSuggestManualTimeZone();
             default: {
                 return handleDefaultCommands(cmd);
@@ -105,9 +105,9 @@
         pw.println("Time Zone Detector (time_zone_detector) commands:");
         pw.println("  help");
         pw.println("    Print this help text.");
-        pw.println("  suggestTelephonyTimeZone");
+        pw.println("  suggest_telephony_time_zone");
         pw.println("    --suggestion <telephony suggestion opts>");
-        pw.println("  suggestManualTimeZone");
+        pw.println("  suggest_manual_time_zone");
         pw.println("    --suggestion <manual suggestion opts>");
         pw.println();
         ManualTimeZoneSuggestion.printCommandLineOpts(pw);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 722d9f7..ddf166e 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2441,9 +2441,8 @@
              * the caller here writes new bitmap data.
              */
             if (which == FLAG_SYSTEM && mLockWallpaperMap.get(userId) == null) {
-                if (DEBUG) {
-                    Slog.i(TAG, "Migrating system->lock to preserve");
-                }
+                Slog.i(TAG, "Migrating current wallpaper to be lock-only before"
+                        + "updating system wallpaper");
                 migrateSystemToLockWallpaperLocked(userId);
             }
 
@@ -2511,6 +2510,8 @@
             ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.wallpaperFile,
                     MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE);
             if (!SELinux.restorecon(wallpaper.wallpaperFile)) {
+                Slog.w(TAG, "restorecon failed for wallpaper file: " +
+                        wallpaper.wallpaperFile.getPath());
                 return null;
             }
             wallpaper.name = name;
@@ -2520,10 +2521,8 @@
             }
             // Nullify field to require new computation
             wallpaper.primaryColors = null;
-            if (DEBUG) {
-                Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId
-                        + " name=" + name + " file=" + wallpaper.wallpaperFile.getName());
-            }
+            Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId
+                    + " name=" + name + " file=" + wallpaper.wallpaperFile.getName());
             return fd;
         } catch (FileNotFoundException e) {
             Slog.w(TAG, "Error setting wallpaper", e);
@@ -2556,7 +2555,7 @@
         WallpaperData wallpaper;
 
         synchronized (mLock) {
-            if (DEBUG) Slog.v(TAG, "setWallpaperComponent name=" + name);
+            Slog.v(TAG, "setWallpaperComponent name=" + name);
             wallpaper = mWallpaperMap.get(userId);
             if (wallpaper == null) {
                 throw new IllegalStateException("Wallpaper not yet initialized for user " + userId);
@@ -2571,6 +2570,8 @@
                 if (mLockWallpaperMap.get(userId) == null) {
                     // We're using the static imagery and there is no lock-specific image in place,
                     // therefore it's a shared system+lock image that we need to migrate.
+                    Slog.i(TAG, "Migrating current wallpaper to be lock-only before"
+                            + "updating system wallpaper");
                     migrateSystemToLockWallpaperLocked(userId);
                 }
             }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 6d53786..29f7d52 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2085,7 +2085,7 @@
 
     /** Returns true if this activity is opaque and fills the entire space of this task. */
     boolean occludesParent() {
-        return mOccludesParent;
+        return !finishing && mOccludesParent;
     }
 
     boolean setOccludesParent(boolean occludesParent) {
@@ -4618,6 +4618,9 @@
             } catch (Exception e) {
                 Slog.w(TAG, "Exception thrown sending start: " + intent.getComponent(), e);
             }
+            // The activity may be waiting for stop, but that is no longer appropriate if we are
+            // starting the activity again
+            mStackSupervisor.mStoppingActivities.remove(this);
         }
         return false;
     }
@@ -4667,7 +4670,7 @@
      * and {@link #shouldPauseActivity(ActivityRecord)}.
      */
     private boolean shouldStartActivity() {
-        return mVisibleRequested && isState(STOPPED);
+        return mVisibleRequested && (isState(STOPPED) || isState(STOPPING));
     }
 
     /**
@@ -5198,7 +5201,7 @@
         }
         finishLaunchTickingLocked();
         if (task != null) {
-            task.hasBeenVisible = true;
+            task.setHasBeenVisible(true);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index e8bfe8e..9089859 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -36,7 +36,6 @@
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
 import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
@@ -303,9 +302,6 @@
 
     private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
 
-    // TODO(task-hierarchy): remove when tiles can be actual parents
-    TaskTile mTile = null;
-
     private final Handler mHandler;
 
     private class ActivityStackHandler extends Handler {
@@ -551,10 +547,10 @@
     }
 
     ActivityStack(ActivityTaskManagerService atmService, int id, int activityType,
-            ActivityInfo info, Intent intent) {
+            ActivityInfo info, Intent intent, boolean createdByOrganizer) {
         this(atmService, id, info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
                 null /*taskDescription*/, null /*stack*/);
-
+        mCreatedByOrganizer = createdByOrganizer;
         setActivityType(activityType);
     }
 
@@ -601,20 +597,11 @@
     }
 
     @Override
-    public void resolveTileOverrideConfiguration(Configuration newParentConfig) {
-        super.resolveTileOverrideConfiguration(newParentConfig);
-        if (mTile != null) {
-            // If this is a virtual child of a tile, simulate the parent-child relationship
-            mTile.updateResolvedConfig(getResolvedOverrideConfiguration());
-        }
-    }
-
-    @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
         // Calling Task#onConfigurationChanged() for leaf task since the ops in this method are
         // particularly for ActivityStack, like preventing bounds changes when inheriting certain
         // windowing mode.
-        if (!isRootTask() || this instanceof TaskTile) {
+        if (!isRootTask()) {
             super.onConfigurationChanged(newParentConfig);
             return;
         }
@@ -689,6 +676,9 @@
 
     @Override
     public void setWindowingMode(int windowingMode) {
+        // Reset the cached result of toString()
+        stringName = null;
+
         // Calling Task#setWindowingMode() for leaf task since this is the a specialization of
         // {@link #setWindowingMode(int)} for ActivityStack.
         if (!isRootTask()) {
@@ -742,7 +732,6 @@
         final int currentOverrideMode = getRequestedOverrideWindowingMode();
         final DisplayContent display = getDisplay();
         final Task topTask = getTopMostTask();
-        final ActivityStack splitScreenStack = display.getRootSplitScreenPrimaryTask();
         int windowingMode = preferredWindowingMode;
         if (preferredWindowingMode == WINDOWING_MODE_UNDEFINED
                 && isTransientWindowingMode(currentMode)) {
@@ -756,14 +745,14 @@
             windowingMode = display.validateWindowingMode(windowingMode,
                     null /* ActivityRecord */, topTask, getActivityType());
         }
-        if (splitScreenStack == this
+        if (display.getRootSplitScreenPrimaryTask() == this
                 && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
             // Resolution to split-screen secondary for the primary split-screen stack means
             // we want to leave split-screen mode.
             windowingMode = mRestoreOverrideWindowingMode;
         }
 
-        final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryTask();
+        final boolean alreadyInSplitScreenMode = display.isSplitScreenModeActivated();
 
         // Don't send non-resizeable notifications if the windowing mode changed was a side effect
         // of us entering split-screen mode.
@@ -831,7 +820,7 @@
                 return;
             }
 
-            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && splitScreenStack != null) {
+            if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && alreadyInSplitScreenMode) {
                 // We already have a split-screen stack in this display, so just move the tasks over.
                 // TODO: Figure-out how to do all the stuff in
                 // AMS.setTaskWindowingModeSplitScreenPrimary
@@ -1063,7 +1052,7 @@
         final DisplayContent display = getDisplay();
 
         if (inSplitScreenSecondaryWindowingMode()) {
-            // If the stack is in split-screen seconardy mode, we need to make sure we move the
+            // If the stack is in split-screen secondary mode, we need to make sure we move the
             // primary split-screen stack forward in the case it is currently behind a fullscreen
             // stack so both halves of the split-screen appear on-top and the fullscreen stack isn't
             // cutting between them.
@@ -1085,12 +1074,13 @@
             display.moveHomeStackToFront(reason + " returnToHome");
         }
 
-        final boolean movingTask = task != null;
-        display.positionStackAtTop(this, !movingTask /* includingParents */, reason);
-        if (movingTask) {
-            // This also moves the entire hierarchy branch to top, including parents
-            positionChildAtTop(task);
+        if (isRootTask()) {
+            display.positionStackAtTop(this, false /* includingParents */, reason);
         }
+        if (task == null) {
+            task = this;
+        }
+        task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */);
     }
 
     /**
@@ -1116,12 +1106,6 @@
         }
     }
 
-    @Override
-    boolean isFocusable() {
-        // Special check for tile which isn't really in the hierarchy
-        return mTile != null ? mTile.isFocusable() : super.isFocusable();
-    }
-
     boolean isTopActivityFocusable() {
         final ActivityRecord r = topRunningActivity();
         return r != null ? r.isFocusable()
@@ -2358,8 +2342,7 @@
         if (!newTask && isOrhasTask) {
             // Starting activity cannot be occluding activity, otherwise starting window could be
             // remove immediately without transferring to starting activity.
-            final ActivityRecord occludingActivity = getActivity(
-                    (ar) -> !ar.finishing && ar.occludesParent(), true, r);
+            final ActivityRecord occludingActivity = getOccludingActivityAbove(r);
             if (occludingActivity != null) {
                 // Here it is!  Now, if this is not yet visible (occluded by another task) to the
                 // user, then just add it without starting; it will get started when the user
@@ -2588,6 +2571,13 @@
         if (r == null || r.app != app) {
             return null;
         }
+        if (r.isActivityTypeHome() && mAtmService.mHomeProcess == app) {
+            // Home activities should not be force-finished as we have nothing else to go
+            // back to. AppErrors will get to it after two crashes in MIN_CRASH_INTERVAL.
+            Slog.w(TAG, "  Not force finishing home activity "
+                    + r.intent.getComponent().flattenToShortString());
+            return null;
+        }
         Slog.w(TAG, "  Force finishing activity "
                 + r.intent.getComponent().flattenToShortString());
         Task finishedTask = r.getTask();
@@ -3078,6 +3068,14 @@
         task.setOverrideDisplayedBounds(bounds == null || bounds.isEmpty() ? null : bounds);
     }
 
+    /**
+     * Returns the top-most activity that occludes the given one, or @{code null} if none.
+     */
+    @Nullable
+    private ActivityRecord getOccludingActivityAbove(ActivityRecord activity) {
+        return getActivity((ar) -> ar.occludesParent(), true /* traverseTopToBottom */, activity);
+    }
+
     boolean willActivityBeVisible(IBinder token) {
         final ActivityRecord r = ActivityRecord.forTokenLocked(token);
         if (r == null) {
@@ -3085,9 +3083,7 @@
         }
 
         // See if there is an occluding activity on-top of this one.
-        final ActivityRecord occludingActivity = getActivity((ar) ->
-                ar.occludesParent() && !ar.finishing,
-                r, false /*includeBoundary*/, true /*traverseTopToBottom*/);
+        final ActivityRecord occludingActivity = getOccludingActivityAbove(r);
         if (occludingActivity != null) return false;
 
         if (r.finishing) Slog.e(TAG, "willActivityBeVisible: Returning false,"
@@ -3542,6 +3538,10 @@
 
     @Override
     void onChildPositionChanged(WindowContainer child) {
+        if (isOrganized()) {
+            mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(this, false /* force */);
+        }
+
         if (!mChildren.contains(child)) {
             return;
         }
@@ -3572,11 +3572,6 @@
         if (oldDisplay != null && oldDisplay.isRemoving()) {
             postReparent();
         }
-        if (mTile != null && getSurfaceControl() != null) {
-            // by now, the TaskStack should already have been reparented, so we can reparent its
-            // surface here
-            reparentSurfaceControl(getPendingTransaction(), mTile.getSurfaceControl());
-        }
     }
 
     void reparent(DisplayContent newParent, boolean onTop) {
@@ -3614,16 +3609,7 @@
 
     @Override
     void getRelativeDisplayedPosition(Point outPos) {
-        // check for tile which is "virtually" a parent.
-        if (mTile != null) {
-            final Rect dispBounds = getDisplayedBounds();
-            outPos.set(dispBounds.left, dispBounds.top);
-            final Rect parentBounds = mTile.getBounds();
-            outPos.offset(-parentBounds.left, -parentBounds.top);
-        } else {
-            super.getRelativeDisplayedPosition(outPos);
-        }
-
+        super.getRelativeDisplayedPosition(outPos);
         final int outset = getStackOutset();
         outPos.x -= outset;
         outPos.y -= outset;
@@ -3633,16 +3619,6 @@
         if (mSurfaceControl == null) {
             return;
         }
-        if (mTile != null) {
-            // Tile controls crop, so the app needs to be able to draw its background outside of
-            // the stack bounds for when the tile crop gets bigger than the stack.
-            if (mLastSurfaceSize.equals(0, 0)) {
-                return;
-            }
-            transaction.setWindowCrop(mSurfaceControl, null);
-            mLastSurfaceSize.set(0, 0);
-            return;
-        }
 
         final Rect stackBounds = getDisplayedBounds();
         int width = stackBounds.width();
@@ -3666,9 +3642,6 @@
 
     @Override
     void onDisplayChanged(DisplayContent dc) {
-        if (mTile != null && dc != mTile.getDisplay()) {
-            mTile.removeChild(this);
-        }
         super.onDisplayChanged(dc);
         if (isRootTask()) {
             updateSurfaceBounds();
@@ -3781,20 +3754,6 @@
         return super.checkCompleteDeferredRemoval();
     }
 
-    @Override
-    int getOrientation() {
-        return (canSpecifyOrientation()) ? super.getOrientation() : SCREEN_ORIENTATION_UNSET;
-    }
-
-    private boolean canSpecifyOrientation() {
-        final int windowingMode = getWindowingMode();
-        final int activityType = getActivityType();
-        return windowingMode == WINDOWING_MODE_FULLSCREEN
-                || activityType == ACTIVITY_TYPE_HOME
-                || activityType == ACTIVITY_TYPE_RECENTS
-                || activityType == ACTIVITY_TYPE_ASSISTANT;
-    }
-
     public DisplayInfo getDisplayInfo() {
         return mDisplayContent.getDisplayInfo();
     }
@@ -3825,49 +3784,6 @@
         return shouldSleepActivities() || mAtmService.mShuttingDown;
     }
 
-    TaskTile getTile() {
-        return mTile;
-    }
-
-    /**
-     * Don't call this directly. instead use {@link TaskTile#addChild} or
-     * {@link TaskTile#removeChild}.
-     */
-    void setTile(TaskTile tile) {
-        TaskTile origTile = mTile;
-        mTile = tile;
-        final ConfigurationContainer parent = getParent();
-        if (parent != null) {
-            onConfigurationChanged(parent.getConfiguration());
-        }
-
-        // Reparent to tile surface or back to original parent
-        if (getSurfaceControl() == null) {
-            return;
-        }
-        if (mTile != null) {
-            // don't use reparentSurfaceControl because we need to bypass taskorg check
-            mSurfaceAnimator.reparent(getPendingTransaction(), mTile.getSurfaceControl());
-        } else if (mTile == null && origTile != null) {
-            mSurfaceAnimator.reparent(getPendingTransaction(), getParentSurfaceControl());
-        }
-    }
-
-    @Override
-    public SurfaceControl getParentSurfaceControl() {
-        // Tile is a "virtual" parent, so we need to intercept the parent surface here
-        return mTile != null ? mTile.getSurfaceControl() : super.getParentSurfaceControl();
-    }
-
-    @Override
-    void removeImmediately() {
-        // TODO(task-hierarchy): remove this override when tiles are in hierarchy
-        if (mTile != null) {
-            mTile.removeChild(this);
-        }
-        super.removeImmediately();
-    }
-
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
             @WindowTraceLogLevel int logLevel) {
@@ -3912,7 +3828,7 @@
             proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
         }
 
-        proto.write(CREATED_BY_ORGANIZER, this instanceof TaskTile);
+        proto.write(CREATED_BY_ORGANIZER, mCreatedByOrganizer);
 
         proto.end(token);
     }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 57f357d..4652f49 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -423,6 +423,10 @@
 
             final ActivityStack toStack = mToDisplay.getOrCreateStack(
                     null, mTmpOptions, task, task.getActivityType(), mOnTop);
+            if (task == toStack) {
+                // The task was reused as the root task.
+                return;
+            }
 
             if (mOnTop) {
                 final boolean isTopTask = task == mTopTask;
@@ -1704,7 +1708,7 @@
                 mRootWindowContainer.getLaunchStack(null, aOptions, task, onTop);
         final WindowContainer parent = task.getParent();
 
-        if (parent == stack) {
+        if (parent == stack || task == stack) {
             // Nothing else to do since it is already restored in the right stack.
             return true;
         }
@@ -2237,7 +2241,7 @@
         final boolean isSecondaryDisplayPreferred =
                 (preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY);
         final boolean inSplitScreenMode = actualStack != null
-                && actualStack.getDisplay().hasSplitScreenPrimaryTask();
+                && actualStack.getDisplay().isSplitScreenModeActivated();
         if (((!inSplitScreenMode && preferredWindowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
                 && !isSecondaryDisplayPreferred) || !task.isActivityTypeStandardOrUndefined()) {
             return;
@@ -2284,16 +2288,14 @@
         if (!task.supportsSplitScreenWindowingMode() || forceNonResizable) {
             // Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
             // we need to move it to top of fullscreen stack, otherwise it will be covered.
-
-            final ActivityStack dockedStack =
-                    task.getStack().getDisplay().getRootSplitScreenPrimaryTask();
-            if (dockedStack != null) {
+            final DisplayContent display = task.getStack().getDisplay();
+            if (display.isSplitScreenModeActivated()) {
                 // Display a warning toast that we tried to put an app that doesn't support
                 // split-screen in split-screen.
                 mService.getTaskChangeNotificationController()
                         .notifyActivityDismissingDockedStack();
-                dockedStack.getDisplay().onSplitScreenModeDismissed();
-                dockedStack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+                display.onSplitScreenModeDismissed();
+                display.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
                         true /* notifyClients */);
             }
             return;
@@ -2602,7 +2604,7 @@
                         "startActivityFromRecents: Task " + taskId + " not found.");
             } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
                     && task.getWindowingMode() != windowingMode) {
-                mService.moveTaskToSplitScreenPrimaryTile(task, true /* toTop */);
+                mService.moveTaskToSplitScreenPrimaryTask(task, true /* toTop */);
             }
 
             if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 14ca7cb..da1c045 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -30,7 +30,6 @@
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WaitResult.LAUNCH_STATE_COLD;
 import static android.app.WaitResult.LAUNCH_STATE_HOT;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -78,6 +77,7 @@
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
 import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
 import static com.android.server.wm.Task.REPARENT_MOVE_STACK_TO_FRONT;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1566,7 +1566,7 @@
         }
 
         if (!mAvoidMoveToFront && mDoResume) {
-            mTargetStack.moveToFront("reuseOrNewTask");
+            mTargetStack.getStack().moveToFront("reuseOrNewTask", targetTask);
             if (mOptions != null) {
                 if (mPreferredWindowingMode != WINDOWING_MODE_UNDEFINED) {
                     mTargetStack.setWindowingMode(mPreferredWindowingMode);
@@ -2364,6 +2364,7 @@
     private void setTargetStackIfNeeded(ActivityRecord intentActivity) {
         mTargetStack = intentActivity.getRootTask();
         mTargetStack.mLastPausedActivity = null;
+        Task intentTask = intentActivity.getTask();
         // If the target task is not in the front, then we need to bring it to the front...
         // except...  well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have
         // the same behavior as if a new instance was being started, which means not bringing it
@@ -2374,7 +2375,7 @@
             final ActivityRecord curTop = (focusStack == null)
                     ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
             final Task topTask = curTop != null ? curTop.getTask() : null;
-            differentTopTask = topTask != intentActivity.getTask()
+            differentTopTask = topTask != intentTask
                     || (focusStack != null && topTask != focusStack.getTopMostTask());
         } else {
             // The existing task should always be different from those in other displays.
@@ -2391,7 +2392,6 @@
                     intentActivity.setTaskToAffiliateWith(mSourceRecord.getTask());
                 }
 
-                final Task intentTask = intentActivity.getTask();
                 final ActivityStack launchStack =
                         getLaunchStack(mStartActivity, mLaunchFlags, intentTask, mOptions);
                 if (launchStack == null || launchStack == mTargetStack) {
@@ -2400,6 +2400,14 @@
                     // new intent has delivered.
                     final boolean isSplitScreenTopStack = mTargetStack.isTopSplitScreenStack();
 
+                    // TODO(b/151572268): Figure out a better way to move tasks in above 2-levels
+                    //  tasks hierarchies.
+                    if (mTargetStack != intentTask
+                            && mTargetStack != intentTask.getParent().asTask()) {
+                        intentTask.getParent().positionChildAt(POSITION_TOP, intentTask,
+                                false /* includingParents */);
+                        intentTask = intentTask.getParent().asTask();
+                    }
                     // We only want to move to the front, if we aren't going to launch on a
                     // different stack. If we launch on a different stack, we will put the
                     // task on top there.
@@ -2420,8 +2428,8 @@
         // Need to update mTargetStack because if task was moved out of it, the original stack may
         // be destroyed.
         mTargetStack = intentActivity.getRootTask();
-        mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(),
-                WINDOWING_MODE_UNDEFINED, DEFAULT_DISPLAY, mTargetStack);
+        mSupervisor.handleNonResizableTaskIfNeeded(intentTask, WINDOWING_MODE_UNDEFINED,
+                DEFAULT_DISPLAY, mTargetStack);
     }
 
     private void resumeTargetStackIfNeeded() {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index edc87e5..2263795 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -548,8 +548,11 @@
 
     /**
      * Gets bitmap snapshot of the provided task id.
+     *
+     * <p>Warning! this may restore the snapshot from disk so can block, don't call in a latency
+     * sensitive environment.
      */
-    public abstract ActivityManager.TaskSnapshot getTaskSnapshotNoRestore(int taskId,
+    public abstract ActivityManager.TaskSnapshot getTaskSnapshotBlocking(int taskId,
             boolean isLowResolution);
 
     /** Returns true if uid is considered foreground for activity start purposes. */
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f7278e7..ce885ab 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -31,12 +31,11 @@
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -122,6 +121,7 @@
 import static com.android.server.wm.Task.LOCK_TASK_AUTH_DONT_LOCK;
 import static com.android.server.wm.Task.REPARENT_KEEP_STACK_AT_FRONT;
 import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import android.Manifest;
 import android.annotation.IntDef;
@@ -144,7 +144,6 @@
 import android.app.IAssistDataReceiver;
 import android.app.INotificationManager;
 import android.app.IRequestFinishCallback;
-import android.window.ITaskOrganizerController;
 import android.app.ITaskStackListener;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -230,9 +229,9 @@
 import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
+import android.view.WindowManager;
 import android.window.IWindowOrganizerController;
 import android.window.WindowContainerTransaction;
-import android.view.WindowManager;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -1274,9 +1273,13 @@
         a.colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
         a.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
 
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchActivityType(ACTIVITY_TYPE_DREAM);
+
         try {
             getActivityStartController().obtainStarter(intent, "dream")
                     .setActivityInfo(a)
+                    .setActivityOptions(options.toBundle())
                     .setIsDream(true)
                     .execute();
             return true;
@@ -2349,16 +2352,18 @@
                 }
 
                 final ActivityStack stack = task.getStack();
-                // Convert some windowing-mode changes into root-task reparents for split-screen.
-                if (stack.getTile() != null) {
-                    stack.getDisplay().onSplitScreenModeDismissed();
-                }
                 if (toTop) {
                     stack.moveToFront("setTaskWindowingMode", task);
                 }
-                stack.setWindowingMode(windowingMode);
-                stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
-                        true /* notifyClients */);
+                // Convert some windowing-mode changes into root-task reparents for split-screen.
+                if (stack.inSplitScreenWindowingMode()) {
+                    stack.getDisplay().onSplitScreenModeDismissed();
+
+                } else {
+                    stack.setWindowingMode(windowingMode);
+                    stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+                            true /* notifyClients */);
+                }
                 return true;
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -2609,13 +2614,16 @@
 
     @Override
     public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
-        return getFilteredTasks(maxNum, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED);
+        return getFilteredTasks(maxNum, false /* filterForVisibleRecents */);
     }
 
+    /**
+     * @param filterOnlyVisibleRecents whether to filter the tasks based on whether they would ever
+     *                                 be visible in the recent task list in systemui
+     */
     @Override
     public List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum,
-            @WindowConfiguration.ActivityType int ignoreActivityType,
-            @WindowConfiguration.WindowingMode int ignoreWindowingMode) {
+            boolean filterOnlyVisibleRecents) {
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
         final boolean crossUser = isCrossUserAllowed(callingPid, callingUid);
@@ -2631,8 +2639,8 @@
             if (DEBUG_ALL) Slog.v(TAG, "getTasks: max=" + maxNum);
 
             final boolean allowed = isGetTasksAllowed("getTasks", callingPid, callingUid);
-            mRootWindowContainer.getRunningTasks(maxNum, list, ignoreActivityType,
-                    ignoreWindowingMode, callingUid, allowed, crossUser, callingProfileIds);
+            mRootWindowContainer.getRunningTasks(maxNum, list, filterOnlyVisibleRecents, callingUid,
+                    allowed, crossUser, callingProfileIds);
         }
 
         return list;
@@ -2755,24 +2763,22 @@
         }
 
         final int prevMode = task.getWindowingMode();
-        moveTaskToSplitScreenPrimaryTile(task, toTop);
+        moveTaskToSplitScreenPrimaryTask(task, toTop);
         return prevMode != task.getWindowingMode();
     }
 
-    void moveTaskToSplitScreenPrimaryTile(Task task, boolean toTop) {
-        ActivityStack stack = task.getStack();
-        TaskTile tile = null;
-        for (int i = stack.getDisplay().getStackCount() - 1; i >= 0; --i) {
-            tile = stack.getDisplay().getStackAt(i).asTile();
-            if (tile != null && tile.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                break;
-            }
+    void moveTaskToSplitScreenPrimaryTask(Task task, boolean toTop) {
+        final DisplayContent display = task.getDisplayContent();
+        final ActivityStack primarySplitTask = display.getRootSplitScreenPrimaryTask();
+        if (primarySplitTask == null) {
+            throw new IllegalStateException("Can't enter split without associated organized task");
         }
-        if (tile == null) {
-            throw new IllegalStateException("Can't enter split without associated tile");
+
+        if (toTop) {
+            display.positionStackAt(POSITION_TOP, primarySplitTask, false /* includingParents */);
         }
         WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.reparent(stack.mRemoteToken, tile.mRemoteToken, toTop);
+        wct.reparent(task.getStack().mRemoteToken, primarySplitTask.mRemoteToken, toTop);
         mWindowOrganizerController.applyTransaction(wct);
     }
 
@@ -3239,7 +3245,8 @@
 
                 final ActivityStack stack = r.getRootTask();
                 final Task task = stack.getDisplay().createStack(stack.getWindowingMode(),
-                        stack.getActivityType(), !ON_TOP, ainfo, intent);
+                        stack.getActivityType(), !ON_TOP, ainfo, intent,
+                        false /* createdByOrganizer */);
 
                 if (!mRecentTasks.addToBottom(task)) {
                     // The app has too many tasks already and we can't add any more
@@ -3979,35 +3986,6 @@
         }
     }
 
-    /**
-     * Dismisses Pip
-     * @param animate True if the dismissal should be animated.
-     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
-     *                          default animation duration should be used.
-     */
-    @Override
-    public void dismissPip(boolean animate, int animationDuration) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityStack stack =
-                        mRootWindowContainer.getDefaultDisplay().getRootPinnedTask();
-                if (stack == null) {
-                    Slog.w(TAG, "dismissPip: pinned stack not found.");
-                    return;
-                }
-                if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
-                    throw new IllegalArgumentException("Stack: " + stack
-                            + " doesn't support animated resize.");
-                }
-                stack.dismissPip();
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
     @Override
     public void suppressResizeConfigChanges(boolean suppress) throws RemoteException {
         mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "suppressResizeConfigChanges()");
@@ -4278,19 +4256,9 @@
         try {
             synchronized (mGlobalLock) {
                 final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
-                TaskTile primary = null;
-                TaskTile secondary = null;
-                for (int i = dc.getStackCount() - 1; i >= 0; --i) {
-                    final TaskTile t = dc.getStackAt(i).asTile();
-                    if (t == null) {
-                        continue;
-                    }
-                    if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                        primary = t;
-                    } else if (t.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
-                        secondary = t;
-                    }
-                }
+                final Task primary = dc.getRootSplitScreenPrimaryTask();
+                final Task secondary = dc.getTask(t -> t.mCreatedByOrganizer && t.isRootTask()
+                        && t.inSplitScreenSecondaryWindowingMode());
                 if (primary == null || secondary == null) {
                     return;
                 }
@@ -7477,10 +7445,10 @@
         }
 
         @Override
-        public ActivityManager.TaskSnapshot getTaskSnapshotNoRestore(int taskId,
-                boolean isLowResolution) {
+        public ActivityManager.TaskSnapshot getTaskSnapshotBlocking(
+                int taskId, boolean isLowResolution) {
             return ActivityTaskManagerService.this.getTaskSnapshot(taskId, isLowResolution,
-                    false /* restoreFromDisk */);
+                    true /* restoreFromDisk */);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 6a47c9e..654ccc8 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -618,6 +618,7 @@
                 Animation anim = mService.mPolicy.createKeyguardWallpaperExit(
                         (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
                 if (anim != null) {
+                    anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
                     mDisplayContent.mWallpaperController.startWallpaperAnimation(anim);
                 }
             }
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 33dd9cf..1036af6 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -467,6 +468,10 @@
         return getActivityType() == ACTIVITY_TYPE_ASSISTANT;
     }
 
+    public boolean isActivityTypeDream() {
+        return getActivityType() == ACTIVITY_TYPE_DREAM;
+    }
+
     public boolean isActivityTypeStandard() {
         return getActivityType() == ACTIVITY_TYPE_STANDARD;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7df731b..9fc90da 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -67,7 +68,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
@@ -200,7 +200,6 @@
 import android.view.Gravity;
 import android.view.IDisplayWindowInsetsController;
 import android.view.ISystemGestureExclusionListener;
-import android.window.ITaskOrganizer;
 import android.view.IWindow;
 import android.view.InputChannel;
 import android.view.InputDevice;
@@ -217,6 +216,7 @@
 import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
+import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
@@ -456,6 +456,8 @@
 
     private final Configuration mTmpConfiguration = new Configuration();
 
+    private ArrayList<Task> mTmpTasks = new ArrayList<>();
+
     /** Remove this display when animation on it has completed. */
     private boolean mDeferredRemoval;
 
@@ -654,8 +656,11 @@
     private final RootWindowContainer.FindTaskResult
             mTmpFindTaskResult = new RootWindowContainer.FindTaskResult();
 
-    // When non-null, new stacks get put into this tile.
-    TaskTile mLaunchTile = null;
+    // When non-null, new tasks get put into this root task.
+    Task mLaunchRootTask = null;
+
+    // Used in performing layout
+    private boolean mTmpWindowsBehindIme;
 
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
@@ -748,6 +753,12 @@
                     + " parentHidden=" + w.isParentWindowHidden());
         }
 
+        // Sets mBehindIme for each window. Windows behind IME can get IME insets.
+        w.mBehindIme = mTmpWindowsBehindIme;
+        if (w == mInputMethodWindow) {
+            mTmpWindowsBehindIme = true;
+        }
+
         // If this view is GONE, then skip it -- keep the current frame, and let the caller know
         // so they can ignore it if they want.  (We do the normal layout for INVISIBLE windows,
         // since that means "perform layout as normal, just don't display").
@@ -755,11 +766,6 @@
             if (mTmpInitial) {
                 w.resetContentChanged();
             }
-            if (w.mAttrs.type == TYPE_DREAM) {
-                // Don't layout windows behind a dream, so that if it does stuff like hide
-                // the status bar we won't get a bad transition when it goes away.
-                mTmpWindow = w;
-            }
             w.mLayoutNeeded = false;
             w.prelayout();
             final boolean firstLayout = !w.isLaidOut();
@@ -813,10 +819,6 @@
                         + " mContainingFrame=" + w.getContainingFrame()
                         + " mDisplayFrame=" + w.getDisplayFrameLw());
             }
-        } else if (w.mAttrs.type == TYPE_DREAM) {
-            // Don't layout windows behind a dream, so that if it does stuff like hide the
-            // status bar we won't get a bad transition when it goes away.
-            mTmpWindow = mTmpWindow2;
         }
     };
 
@@ -900,17 +902,6 @@
             // Take care of the window being ready to display.
             final boolean committed = winAnimator.commitFinishDrawingLocked();
             if (isDefaultDisplay && committed) {
-                if (w.mAttrs.type == TYPE_DREAM) {
-                    // HACK: When a dream is shown, it may at that point hide the lock screen.
-                    // So we need to redo the layout to let the phone window manager make this
-                    // happen.
-                    pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
-                    if (DEBUG_LAYOUT_REPEATS) {
-                        surfacePlacer.debugLayoutRepeats(
-                                "dream and commitFinishDrawingLocked true",
-                                pendingLayoutChanges);
-                    }
-                }
                 if ((w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
                     if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG,
                             "First draw done in potential wallpaper target " + w);
@@ -1306,8 +1297,6 @@
         if (mDisplayRotation.isWaitingForRemoteRotation()) {
             return;
         }
-        // Clear the record because the display will sync to current rotation.
-        mFixedRotationLaunchingApp = null;
 
         final boolean configUpdated = updateDisplayOverrideConfigurationLocked();
         if (configUpdated) {
@@ -1498,7 +1487,7 @@
         startFixedRotationTransform(r, rotation);
         mAppTransition.registerListenerLocked(new WindowManagerInternal.AppTransitionListener() {
             void done() {
-                r.clearFixedRotationTransform();
+                r.finishFixedRotationTransform();
                 mAppTransition.unregisterListener(this);
             }
 
@@ -1527,7 +1516,8 @@
         if (token != mFixedRotationLaunchingApp) {
             return false;
         }
-        if (updateOrientation()) {
+        // Update directly because the app which will change the orientation of display is ready.
+        if (mDisplayRotation.updateOrientation(getOrientation(), false /* forceUpdate */)) {
             sendNewConfiguration();
             return true;
         }
@@ -1573,7 +1563,7 @@
      * @param oldRotation the rotation we are coming from.
      * @param rotation the rotation to apply.
      */
-    void applyRotationLocked(final int oldRotation, final int rotation) {
+    private void applyRotation(final int oldRotation, final int rotation) {
         mDisplayRotation.applyCurrentRotation(rotation);
         final boolean rotateSeamlessly = mDisplayRotation.isRotatingSeamlessly();
         final Transaction transaction = getPendingTransaction();
@@ -2128,12 +2118,13 @@
     }
 
     /** @return The primary split-screen task, and {@code null} otherwise. */
-    ActivityStack getRootSplitScreenPrimaryTask() {
+    @Nullable ActivityStack getRootSplitScreenPrimaryTask() {
         return mTaskContainers.getRootSplitScreenPrimaryTask();
     }
 
-    boolean hasSplitScreenPrimaryTask() {
-        return getRootSplitScreenPrimaryTask() != null;
+    boolean isSplitScreenModeActivated() {
+        Task task = getRootSplitScreenPrimaryTask();
+        return task != null && task.hasChild();
     }
 
     ActivityStack getRootPinnedTask() {
@@ -2600,7 +2591,7 @@
         }
         amendWindowTapExcludeRegion(mTouchExcludeRegion);
         // TODO(multi-display): Support docked stacks on secondary displays.
-        if (mDisplayId == DEFAULT_DISPLAY && getRootSplitScreenPrimaryTask() != null) {
+        if (mDisplayId == DEFAULT_DISPLAY && isSplitScreenModeActivated()) {
             mDividerControllerLocked.getTouchRegion(mTmpRect);
             mTmpRegion.set(mTmpRect);
             mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
@@ -2623,8 +2614,7 @@
         // If the task is home stack and it is resizable and visible (top of its root task), we want
         // to exclude the docked stack from touch so we need the entire screen area and not just a
         // small portion which the home stack currently is resized to.
-        if (task.isActivityTypeHome() && task.isVisible() && task.getStack().getTile() != null
-                && task.isResizeable()) {
+        if (task.isActivityTypeHome() && task.isVisible() && task.isResizeable()) {
             mDisplayContent.getBounds(mTmpRect);
         } else {
             task.getDimBounds(mTmpRect);
@@ -3011,6 +3001,11 @@
         if (recentsStack != null) {
             pw.println(prefix + "recentsStack=" + recentsStack.getName());
         }
+        final ActivityStack dreamStack =
+                getStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_DREAM);
+        if (dreamStack != null) {
+            pw.println(prefix + "dreamStack=" + dreamStack.getName());
+        }
 
         pw.println();
         mPinnedStackControllerLocked.dump(prefix, pw);
@@ -3199,7 +3194,7 @@
             if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                 performLayout(true /*initial*/, updateInputWindows);
             } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
-                mWmService.mRoot.performSurfacePlacement(false);
+                mWmService.mRoot.performSurfacePlacement();
             }
         }
 
@@ -3836,7 +3831,7 @@
     }
 
     // TODO: Super crazy long method that should be broken down...
-    void applySurfaceChangesTransaction(boolean recoveringMemory) {
+    void applySurfaceChangesTransaction() {
         final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
 
         mTmpUpdateAllDrawn.clear();
@@ -4014,6 +4009,9 @@
         mTmpWindow = null;
         mTmpInitial = initial;
 
+        // Used to indicate that we have processed the IME window.
+        mTmpWindowsBehindIme = false;
+
         // First perform layout of any root windows (not attached to another window).
         forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
 
@@ -4328,15 +4326,8 @@
 
         @VisibleForTesting
         ActivityStack getTopStack() {
-            // TODO(task-hierarchy): Just grab index -1 once tiles are in hierarchy.
-            for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
-                final ActivityStack child = mTaskContainers.getChildAt(i);
-                if (child instanceof TaskTile) {
-                    continue;
-                }
-                return child;
-            }
-            return null;
+            final int count = mTaskContainers.getChildCount();
+            return count > 0 ? mTaskContainers.getChildAt(count - 1) : null;
         }
 
         int getIndexOf(ActivityStack stack) {
@@ -4375,10 +4366,6 @@
         }
 
         private void addStackReferenceIfNeeded(ActivityStack stack) {
-            // TODO(task-hierarchy): Remove when tiles are in hierarchy.
-            if (stack instanceof TaskTile) {
-                return;
-            }
             if (stack.isActivityTypeHome()) {
                 if (mRootHomeTask != null) {
                     if (!stack.isDescendantOf(mRootHomeTask)) {
@@ -4390,27 +4377,26 @@
                     mRootHomeTask = stack;
                 }
             }
+
+            if (!stack.isRootTask()) {
+                return;
+            }
             final int windowingMode = stack.getWindowingMode();
             if (windowingMode == WINDOWING_MODE_PINNED) {
                 if (mRootPinnedTask != null) {
-                    if (!stack.isDescendantOf(mRootPinnedTask)) {
-                        throw new IllegalArgumentException(
-                                "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask
-                                        + " already exist on display=" + this + " stack=" + stack);
-                    }
-                } else {
-                    mRootPinnedTask = stack;
+                    throw new IllegalArgumentException(
+                            "addStackReferenceIfNeeded: pinned stack=" + mRootPinnedTask
+                                    + " already exist on display=" + this + " stack=" + stack);
                 }
+                mRootPinnedTask = stack;
             } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                 if (mRootSplitScreenPrimaryTask != null) {
-                    if (!stack.isDescendantOf(mRootSplitScreenPrimaryTask)) {
-                        throw new IllegalArgumentException("addStackReferenceIfNeeded:"
-                                + " split-screen-primary" + " stack=" + mRootSplitScreenPrimaryTask
-                                + " already exist on display=" + this + " stack=" + stack);
-                    }
-                } else {
-                    mRootSplitScreenPrimaryTask = stack;
+                    throw new IllegalArgumentException(
+                            "addStackReferenceIfNeeded: split screen primary stack="
+                                    + mRootSplitScreenPrimaryTask
+                                    + " already exist on display=" + this + " stack=" + stack);
                 }
+                mRootSplitScreenPrimaryTask = stack;
             }
         }
 
@@ -4430,6 +4416,7 @@
             position = findPositionForStack(position, stack, true /* adding */);
 
             super.addChild(stack, position);
+            mAtmService.updateSleepIfNeededLocked();
 
             // The reparenting case is handled in WindowContainer.
             if (!stack.mReparenting) {
@@ -4441,6 +4428,7 @@
         protected void removeChild(ActivityStack stack) {
             super.removeChild(stack);
             mDisplayContent.onStackRemoved(stack);
+            mAtmService.updateSleepIfNeededLocked();
             removeStackReferenceIfNeeded(stack);
         }
 
@@ -4495,6 +4483,10 @@
          */
         private int findPositionForStack(int requestedPosition, ActivityStack stack,
                 boolean adding) {
+            if (stack.isActivityTypeDream()) {
+                return POSITION_TOP;
+            }
+
             if (stack.inPinnedWindowingMode()) {
                 return POSITION_TOP;
             }
@@ -4645,10 +4637,9 @@
                 // Apps and their containers are not allowed to specify an orientation while using
                 // root tasks...except for the home stack if it is not resizable and currently
                 // visible (top of) its root task.
-                if (mRootHomeTask != null && mRootHomeTask.isVisible()
-                        && mRootHomeTask.getTile() != null) {
+                if (mRootHomeTask != null && mRootHomeTask.isVisible()) {
                     final Task topMost = mRootHomeTask.getTopMostTask();
-                    final boolean resizable = topMost == null && topMost.isResizeable();
+                    final boolean resizable = topMost != null && topMost.isResizeable();
                     if (!(resizable && mRootHomeTask.matchParentBounds())) {
                         final int orientation = mRootHomeTask.getOrientation();
                         if (orientation != SCREEN_ORIENTATION_UNSET) {
@@ -4792,17 +4783,6 @@
                 mSplitScreenDividerAnchor = null;
             }
         }
-
-        @Override
-        void onChildPositionChanged(WindowContainer child) {
-            // TODO(task-hierarchy): Move functionality to TaskTile when it's a proper parent.
-            TaskTile tile = ((ActivityStack) child).getTile();
-            if (tile == null) {
-                return;
-            }
-            mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
-                    tile, false /* force */);
-        }
     }
 
     private class WindowContainers extends DisplayChildWindowContainer<WindowContainer> {
@@ -4983,7 +4963,7 @@
         private boolean skipImeWindowsDuringTraversal(DisplayContent dc) {
             // We skip IME windows so they're processed just above their target, except
             // in split-screen mode where we process the IME containers above the docked divider.
-            return dc.mInputMethodTarget != null && !dc.hasSplitScreenPrimaryTask();
+            return dc.mInputMethodTarget != null && !dc.isSplitScreenModeActivated();
         }
 
         /** Like {@link #forAllWindows}, but ignores {@link #skipImeWindowsDuringTraversal} */
@@ -5633,6 +5613,10 @@
     }
 
     void onDisplayChanged() {
+        mDisplay.getRealSize(mTmpDisplaySize);
+        setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
+        updateDisplayInfo();
+
         // The window policy is responsible for stopping activities on the default display.
         final int displayId = mDisplay.getDisplayId();
         if (displayId != DEFAULT_DISPLAY) {
@@ -5644,17 +5628,20 @@
                 mOffToken = null;
             }
         }
-
-        mDisplay.getRealSize(mTmpDisplaySize);
-        setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
-        updateDisplayInfo();
         mWmService.requestTraversal();
     }
 
     void addStack(ActivityStack stack, int position) {
         setStackOnDisplay(stack, position);
         positionStackAt(stack, position);
-        mAtmService.updateSleepIfNeededLocked();
+    }
+
+    void addStackReferenceIfNeeded(ActivityStack stack) {
+        mTaskContainers.addStackReferenceIfNeeded(stack);
+    }
+
+    void removeStackReferenceIfNeeded(ActivityStack stack) {
+        mTaskContainers.removeStackReferenceIfNeeded(stack);
     }
 
     void onStackRemoved(ActivityStack stack) {
@@ -5665,7 +5652,6 @@
             mPreferredTopFocusableStack = null;
         }
         releaseSelfIfNeeded();
-        mAtmService.updateSleepIfNeededLocked();
         onStackOrderChanged(stack);
     }
 
@@ -5703,6 +5689,14 @@
                     "positionStackAt: Can only have one task on display=" + this);
         }
 
+        final boolean movingToTop = wasContained && position >= getStackCount() - 1;
+        // Reset mPreferredTopFocusableStack before positioning to top or {@link
+        // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top
+        // resumed activity.
+        if (movingToTop && stack.isFocusable()) {
+            mPreferredTopFocusableStack = null;
+        }
+
         // Since positionChildAt() is called during the creation process of pinned stacks,
         // ActivityStack#getStack() can be null.
         positionStackAt(position, stack, includingParents);
@@ -5712,7 +5706,7 @@
         // we are looking for top focusable stack. The condition {@code wasContained} restricts the
         // preferred stack is set only when moving an existing stack to top instead of adding a new
         // stack that may be too early (e.g. in the middle of launching or reparenting).
-        if (wasContained && position >= getStackCount() - 1 && stack.isFocusableAndVisible()) {
+        if (movingToTop && stack.isFocusableAndVisible()) {
             mPreferredTopFocusableStack = stack;
         } else if (mPreferredTopFocusableStack == stack) {
             mPreferredTopFocusableStack = null;
@@ -5755,18 +5749,54 @@
     /**
      * Returns an existing stack compatible with the windowing mode and activity type or creates one
      * if a compatible stack doesn't exist.
+     * @see #getOrCreateStack(int, int, boolean, Intent, Task, boolean)
+     */
+    ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop) {
+        return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
+                null /* candidateTask */, false /* createdByOrganizer */);
+    }
+
+    /**
+     * When two level tasks are required for given windowing mode and activity type, returns an
+     * existing compatible root task or creates a new one.
+     * For one level task, the candidate task would be reused to also be the root task or create
+     * a new root task if no candidate task.
      * @see #getStack(int, int)
      * @see #createStack(int, int, boolean)
      */
-    ActivityStack getOrCreateStack(int windowingMode, int activityType,
-            boolean onTop) {
+    ActivityStack getOrCreateStack(int windowingMode, int activityType, boolean onTop,
+            Intent intent, Task candidateTask, boolean createdByOrganizer) {
         if (!alwaysCreateStack(windowingMode, activityType)) {
             ActivityStack stack = getStack(windowingMode, activityType);
             if (stack != null) {
                 return stack;
             }
+        } else if (candidateTask != null) {
+            final ActivityStack stack = (ActivityStack) candidateTask;
+            final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
+            if (isSplitScreenModeActivated()) {
+                final Task splitRootSecondary = getTask(t -> t.mCreatedByOrganizer && t.isRootTask()
+                        && t.inSplitScreenSecondaryWindowingMode());
+                if (stack.getParent() == null) {
+                    splitRootSecondary.addChild(stack, position);
+                } else if (stack.getParent() != splitRootSecondary) {
+                    stack.reparent(splitRootSecondary, position);
+                }
+            } else if (stack.getDisplay() != this || !stack.isRootTask()) {
+                if (stack.getParent() == null) {
+                    addStack(stack, position);
+                } else {
+                    stack.reparent(this, onTop);
+                }
+            }
+            // Update windowing mode if necessary, e.g. moving a pinned task to fullscreen.
+            if (candidateTask.getWindowingMode() != windowingMode) {
+                candidateTask.setWindowingMode(windowingMode);
+            }
+            return stack;
         }
-        return createStack(windowingMode, activityType, onTop);
+        return createStack(windowingMode, activityType, onTop, null /*info*/, intent,
+                createdByOrganizer);
     }
 
     /**
@@ -5784,7 +5814,8 @@
         // UNDEFINED windowing mode is a valid result and means that the new stack will inherit
         // it's display's windowing mode.
         windowingMode = validateWindowingMode(windowingMode, r, candidateTask, activityType);
-        return getOrCreateStack(windowingMode, activityType, onTop);
+        return getOrCreateStack(windowingMode, activityType, onTop, null /* intent */,
+                candidateTask, false /* createdByOrganizer */);
     }
 
     @VisibleForTesting
@@ -5793,7 +5824,8 @@
     }
 
     ActivityStack createStack(int windowingMode, int activityType, boolean onTop) {
-        return createStack(windowingMode, activityType, onTop, null /*info*/, null /*intent*/);
+        return createStack(windowingMode, activityType, onTop, null /* info */, null /* intent */,
+                false /* createdByOrganizer */);
     }
 
     /**
@@ -5805,25 +5837,29 @@
      *                     {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack will
      *                     be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
      * @param onTop If true the stack will be created at the top of the display, else at the bottom.
+     * @param info The started activity info.
+     * @param intent The intent that started this task.
+     * @param createdByOrganizer @{code true} if this is created by task organizer, @{code false}
+     *                          otherwise.
      * @return The newly created stack.
      */
     ActivityStack createStack(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
-            Intent intent) {
+            Intent intent, boolean createdByOrganizer) {
         if (mSingleTaskInstance && getStackCount() > 0) {
             // Create stack on default display instead since this display can only contain 1 stack.
             // TODO: Kinda a hack, but better that having the decision at each call point. Hoping
             // this goes away once ActivityView is no longer using virtual displays.
             return mRootWindowContainer.getDefaultDisplay().createStack(
-                    windowingMode, activityType, onTop, info, intent);
+                    windowingMode, activityType, onTop, info, intent, createdByOrganizer);
         }
 
-        if (activityType == ACTIVITY_TYPE_UNDEFINED) {
+        if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) {
             // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
-            // anything else should be passing it in anyways...
+            // anything else should be passing it in anyways...except for the task organizer.
             activityType = ACTIVITY_TYPE_STANDARD;
         }
 
-        if (activityType != ACTIVITY_TYPE_STANDARD) {
+        if (activityType != ACTIVITY_TYPE_STANDARD && activityType != ACTIVITY_TYPE_UNDEFINED) {
             // For now there can be only one stack of a particular non-standard activity type on a
             // display. So, get that ignoring whatever windowing mode it is currently in.
             ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
@@ -5842,39 +5878,39 @@
         }
 
         final int stackId = getNextStackId();
-        return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent);
+        return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent,
+                createdByOrganizer);
     }
 
-    /** @return the tile to create the next stack in. */
-    private TaskTile updateLaunchTile(int windowingMode) {
+    /** @return the root task to create the next task in. */
+    private Task updateLaunchRootTask(int windowingMode) {
         if (!isSplitScreenWindowingMode(windowingMode)) {
-            // Only split-screen windowing modes interact with tiles.
+            // Only split-screen windowing modes can do this currently...
             return null;
         }
         for (int i = getStackCount() - 1; i >= 0; --i) {
-            final TaskTile t = getStackAt(i).asTile();
-            if (t == null || t.getRequestedOverrideWindowingMode() != windowingMode) {
+            final Task t = getStackAt(i);
+            if (!t.mCreatedByOrganizer || t.getRequestedOverrideWindowingMode() != windowingMode) {
                 continue;
             }
-            // If not already set, pick a launch tile which is not the one we are launching
-            // into.
-            if (mLaunchTile == null) {
+            // If not already set, pick a launch root which is not the one we are launching into.
+            if (mLaunchRootTask == null) {
                 for (int j = 0, n = getStackCount(); j < n; ++j) {
-                    TaskTile tt = getStackAt(j).asTile();
-                    if (tt != t) {
-                        mLaunchTile = tt;
+                    final Task tt = getStackAt(j);
+                    if (tt.mCreatedByOrganizer && tt != t) {
+                        mLaunchRootTask = tt;
                         break;
                     }
                 }
             }
             return t;
         }
-        return mLaunchTile;
+        return mLaunchRootTask;
     }
 
     @VisibleForTesting
-    ActivityStack createStackUnchecked(int windowingMode, int activityType,
-            int stackId, boolean onTop, ActivityInfo info, Intent intent) {
+    ActivityStack createStackUnchecked(int windowingMode, int activityType, int stackId,
+            boolean onTop, ActivityInfo info, Intent intent, boolean createdByOrganizer) {
         if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) {
             throw new IllegalArgumentException("Stack with windowing mode cannot with non standard "
                     + "activity type.");
@@ -5884,19 +5920,25 @@
             info.applicationInfo = new ApplicationInfo();
         }
 
-        TaskTile tile = updateLaunchTile(windowingMode);
-        if (tile != null) {
-            // Since this stack will be put into a tile, its windowingMode will be inherited.
+        // Task created by organizer are added as root.
+        Task launchRootTask = createdByOrganizer ? null : updateLaunchRootTask(windowingMode);
+        if (launchRootTask != null) {
+            // Since this stack will be put into a root task, its windowingMode will be inherited.
             windowingMode = WINDOWING_MODE_UNDEFINED;
         }
+
         final ActivityStack stack = (ActivityStack) Task.create(mAtmService, stackId, activityType,
-                info, intent);
-        addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
-        stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
-                false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
-                true /* creating */);
-        if (tile != null) {
-            tile.addChild(stack, 0 /* index */);
+                info, intent, createdByOrganizer);
+        if (launchRootTask != null) {
+            launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
+            if (onTop) {
+                positionStackAtTop((ActivityStack) launchRootTask, false /* includingParents */);
+            }
+        } else {
+            addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
+            stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
+                    false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
+                    true /* creating */);
         }
         return stack;
     }
@@ -6031,7 +6073,7 @@
         mTmpFindTaskResult.clear();
         for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
             final ActivityStack stack = getStackAt(stackNdx);
-            if (!r.hasCompatibleActivityType(stack)) {
+            if (!r.hasCompatibleActivityType(stack) && stack.isLeafTask()) {
                 if (DEBUG_TASKS) {
                     Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack);
                 }
@@ -6103,7 +6145,15 @@
             final int activityType = activityTypes[j];
             for (int i = getStackCount() - 1; i >= 0; --i) {
                 final ActivityStack stack = getStackAt(i);
-                if (stack.getActivityType() == activityType) {
+                // Collect the root tasks that are currently being organized.
+                if (stack.isOrganized()) {
+                    for (int k = stack.getChildCount() - 1; k >= 0; --k) {
+                        final ActivityStack childStack = (ActivityStack) stack.getChildAt(k);
+                        if (childStack.getActivityType() == activityType) {
+                            stacks.add(childStack);
+                        }
+                    }
+                } else if (stack.getActivityType() == activityType) {
                     stacks.add(stack);
                 }
             }
@@ -6117,13 +6167,8 @@
     void onSplitScreenModeDismissed() {
         mAtmService.deferWindowLayout();
         try {
-            mLaunchTile = null;
-            for (int i = getStackCount() - 1; i >= 0; --i) {
-                final TaskTile t = getStackAt(i).asTile();
-                if (t != null) {
-                    t.removeAllChildren();
-                }
-            }
+            mLaunchRootTask = null;
+            moveSplitScreenTasksToFullScreen();
         } finally {
             final ActivityStack topFullscreenStack =
                     getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
@@ -6141,6 +6186,24 @@
         }
     }
 
+    private void moveSplitScreenTasksToFullScreen() {
+        final WindowContainerTransaction wct = new WindowContainerTransaction();
+        mTmpTasks.clear();
+        forAllTasks(task -> {
+            if (task.mCreatedByOrganizer && task.inSplitScreenWindowingMode() && task.hasChild()) {
+                mTmpTasks.add(task);
+            }
+        });
+
+        for (int i = mTmpTasks.size() - 1; i >= 0; i--) {
+            final Task root = mTmpTasks.get(i);
+            for (int j = 0; j < root.getChildCount(); j++) {
+                wct.reparent(root.getChildAt(j).mRemoteToken, null, true /* toTop */);
+            }
+        }
+        mAtmService.mWindowOrganizerController.applyTransaction(wct);
+    }
+
     /**
      * Returns true if the {@param windowingMode} is supported based on other parameters passed in.
      * @param windowingMode The windowing mode we are checking support for.
@@ -6253,7 +6316,7 @@
             }
         }
 
-        final boolean inSplitScreenMode = hasSplitScreenPrimaryTask();
+        final boolean inSplitScreenMode = isSplitScreenModeActivated();
         if (!inSplitScreenMode
                 && windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY) {
             // Switch to the display's windowing mode if we are not in split-screen mode and we are
@@ -6278,10 +6341,6 @@
     }
 
     boolean isTopNotPinnedStack(ActivityStack stack) {
-        // TODO(task-hierarchy): Remove when tiles are in hierarchy.
-        if (stack instanceof TaskTile) {
-            return false;
-        }
         for (int i = getStackCount() - 1; i >= 0; --i) {
             final ActivityStack current = getStackAt(i);
             if (!current.inPinnedWindowingMode()) {
@@ -6418,15 +6477,20 @@
 
     @Override
     public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
-        final int currRotation =
-                getRequestedOverrideConfiguration().windowConfiguration.getRotation();
-        if (currRotation != ROTATION_UNDEFINED
-                && currRotation != overrideConfiguration.windowConfiguration.getRotation()) {
-            applyRotationLocked(currRotation,
-                    overrideConfiguration.windowConfiguration.getRotation());
+        final Configuration currOverrideConfig = getRequestedOverrideConfiguration();
+        final int currRotation = currOverrideConfig.windowConfiguration.getRotation();
+        final int overrideRotation = overrideConfiguration.windowConfiguration.getRotation();
+        if (currRotation != ROTATION_UNDEFINED && currRotation != overrideRotation) {
+            if (mFixedRotationLaunchingApp != null) {
+                mFixedRotationLaunchingApp.clearFixedRotationTransform(
+                        () -> applyRotation(currRotation, overrideRotation));
+                // Clear the record because the display will sync to current rotation.
+                mFixedRotationLaunchingApp = null;
+            } else {
+                applyRotation(currRotation, overrideRotation);
+            }
         }
-        mCurrentOverrideConfigurationChanges =
-            getRequestedOverrideConfiguration().diff(overrideConfiguration);
+        mCurrentOverrideConfigurationChanges = currOverrideConfig.diff(overrideConfiguration);
         super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
         mCurrentOverrideConfigurationChanges = 0;
         mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration, this);
@@ -6503,7 +6567,7 @@
                     // If default display is in split-window mode, set windowing mode of the stack
                     // to split-screen secondary. Otherwise, set the windowing mode to undefined by
                     // default to let stack inherited the windowing mode from the new display.
-                    final int windowingMode = toDisplay.hasSplitScreenPrimaryTask()
+                    final int windowingMode = toDisplay.isSplitScreenModeActivated()
                             ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
                             : WINDOWING_MODE_UNDEFINED;
                     stack.reparent(toDisplay, true /* onTop */);
@@ -6607,27 +6671,35 @@
      *         already top-most.
      */
     ActivityStack getStackAbove(ActivityStack stack) {
-        final int stackIndex = getIndexOf(stack) + 1;
-        return (stackIndex < getStackCount()) ? getStackAt(stackIndex) : null;
+        final WindowContainer wc = stack.getParent();
+        final int index = wc.mChildren.indexOf(stack) + 1;
+        return (index < wc.mChildren.size()) ? (ActivityStack) wc.mChildren.get(index) : null;
     }
 
     /**
      * Adjusts the {@param stack} behind the last visible stack in the display if necessary.
      * Generally used in conjunction with {@link #moveStackBehindStack}.
      */
+    // TODO(b/151575894): Remove special stack movement methods.
     void moveStackBehindBottomMostVisibleStack(ActivityStack stack) {
         if (stack.shouldBeVisible(null)) {
             // Skip if the stack is already visible
             return;
         }
 
-        // Move the stack to the bottom to not affect the following visibility checks
-        positionStackAtBottom(stack);
+        final boolean isRootTask = stack.isRootTask();
+        if (isRootTask) {
+            // Move the stack to the bottom to not affect the following visibility checks
+            positionStackAtBottom(stack);
+        } else {
+            stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */);
+        }
 
         // Find the next position where the stack should be placed
-        final int numStacks = getStackCount();
+        final int numStacks = isRootTask ? getStackCount() : stack.getParent().getChildCount();
         for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
-            final ActivityStack s = getStackAt(stackNdx);
+            final ActivityStack s = isRootTask ? getStackAt(stackNdx)
+                    : (ActivityStack) stack.getParent().getChildAt(stackNdx);
             if (s == stack) {
                 continue;
             }
@@ -6636,7 +6708,12 @@
                     || winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
             if (s.shouldBeVisible(null) && isValidWindowingMode) {
                 // Move the provided stack to behind this stack
-                positionStackAt(stack, Math.max(0, stackNdx - 1));
+                final int position = Math.max(0, stackNdx - 1);
+                if (isRootTask) {
+                    positionStackAt(stack, position);
+                } else {
+                    stack.getParent().positionChildAt(position, stack, false /*includingParents */);
+                }
                 break;
             }
         }
@@ -6652,15 +6729,25 @@
             return;
         }
 
+        final WindowContainer parent = stack.getParent();
+        if (parent == null || parent != behindStack.getParent()) {
+            return;
+        }
+
         // Note that positionChildAt will first remove the given stack before inserting into the
         // list, so we need to adjust the insertion index to account for the removed index
         // TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the
         //       position internally
-        final int stackIndex = getIndexOf(stack);
-        final int behindStackIndex = getIndexOf(behindStack);
+        final int stackIndex = parent.mChildren.indexOf(stack);
+        final int behindStackIndex = parent.mChildren.indexOf(behindStack);
         final int insertIndex = stackIndex <= behindStackIndex
                 ? behindStackIndex - 1 : behindStackIndex;
-        positionStackAt(stack, Math.max(0, insertIndex));
+        final int position = Math.max(0, insertIndex);
+        if (stack.isRootTask()) {
+            positionStackAt(stack, position);
+        } else {
+            parent.positionChildAt(position, stack, false /* includingParents */);
+        }
     }
 
     void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
@@ -6697,19 +6784,6 @@
         return getHomeActivityForUser(mRootWindowContainer.mCurrentUser);
     }
 
-    // TODO(task-hierarchy): Remove when tiles are in hierarchy.
-    void addTile(TaskTile tile) {
-        mTaskContainers.addChild(tile, POSITION_BOTTOM);
-        ITaskOrganizer organizer = mAtmService.mTaskOrganizerController.getTaskOrganizer(
-                tile.getWindowingMode());
-        tile.setTaskOrganizer(organizer);
-    }
-
-    // TODO(task-hierarchy): Remove when tiles are in hierarchy.
-    void removeTile(TaskTile tile) {
-        mTaskContainers.removeChild(tile);
-    }
-
     @Nullable
     ActivityRecord getHomeActivityForUser(int userId) {
         final ActivityStack homeStack = getRootHomeTask();
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 5e88fb0..367151c 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -29,7 +29,6 @@
 import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
 import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
 import static android.view.InsetsState.ITYPE_CAPTION_BAR;
-import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
 import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -79,7 +78,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
@@ -851,7 +849,6 @@
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
                 attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
                 break;
-            case TYPE_DREAM:
             case TYPE_WALLPAPER:
                 // Dreams and wallpapers don't have an app window token and can thus not be
                 // letterboxed. Hence always let them extend under the cutout.
@@ -1226,13 +1223,6 @@
                 if (DEBUG_ANIM) Slog.i(TAG, "**** STARTING EXIT");
                 return R.anim.app_starting_exit;
             }
-        } else if (win.getAttrs().type == TYPE_DREAM && mDreamingLockscreen
-                && transit == TRANSIT_ENTER) {
-            // Special case: we are animating in a dream, while the keyguard
-            // is shown.  We don't want an animation on the dream, because
-            // we need it shown immediately with the keyguard animating away
-            // to reveal it.
-            return ANIMATION_NONE;
         }
 
         return ANIMATION_STYLEABLE;
@@ -1493,14 +1483,8 @@
      */
     public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
         displayFrames.onBeginLayout();
-        final InsetsState insetsState =
-                mDisplayContent.getInsetsStateController().getRawInsetsState();
-
-        // Reset the frame of IME so that the layout of windows above IME won't get influenced.
-        // Once we layout the IME, frames will be set again on the source.
-        insetsState.getSource(ITYPE_IME).setFrame(0, 0, 0, 0);
-
-        updateInsetsStateForDisplayCutout(displayFrames, insetsState);
+        updateInsetsStateForDisplayCutout(displayFrames,
+                mDisplayContent.getInsetsStateController().getRawInsetsState());
         mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
         mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
 
@@ -1527,25 +1511,7 @@
                 && (mNotificationShade.getAttrs().privateFlags
                 & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0;
 
-        // When the navigation bar isn't visible, we put up a fake input window to catch all
-        // touch events. This way we can detect when the user presses anywhere to bring back the
-        // nav bar and ensure the application doesn't see the event.
-        if (navVisible || navAllowedHidden) {
-            if (mInputConsumer != null) {
-                mInputConsumer.dismiss();
-                mHandler.sendMessage(
-                        mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
-                mInputConsumer = null;
-            }
-        } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
-            mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer(
-                    mHandler.getLooper(),
-                    INPUT_CONSUMER_NAVIGATION,
-                    HideNavInputEventReceiver::new);
-            // As long as mInputConsumer is active, hover events are not dispatched to the app
-            // and the pointer icon is likely to become stale. Hide it to avoid confusion.
-            InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
-        }
+        updateHideNavInputEventReceiver(navVisible, navAllowedHidden);
 
         // For purposes of positioning and showing the nav bar, if we have decided that it can't
         // be hidden (because of the screen aspect ratio), then take that into account.
@@ -1567,6 +1533,28 @@
         mLastNotificationShadeForcesShowingNavigation = notificationShadeForcesShowingNavigation;
     }
 
+    void updateHideNavInputEventReceiver(boolean navVisible, boolean navAllowedHidden) {
+        // When the navigation bar isn't visible, we put up a fake input window to catch all
+        // touch events. This way we can detect when the user presses anywhere to bring back the
+        // nav bar and ensure the application doesn't see the event.
+        if (navVisible || navAllowedHidden) {
+            if (mInputConsumer != null) {
+                mInputConsumer.dismiss();
+                mHandler.sendMessage(
+                        mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
+                mInputConsumer = null;
+            }
+        } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
+            mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer(
+                    mHandler.getLooper(),
+                    INPUT_CONSUMER_NAVIGATION,
+                    HideNavInputEventReceiver::new);
+            // As long as mInputConsumer is active, hover events are not dispatched to the app
+            // and the pointer icon is likely to become stale. Hide it to avoid confusion.
+            InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
+        }
+    }
+
     private static void updateInsetsStateForDisplayCutout(DisplayFrames displayFrames,
             InsetsState state) {
         if (displayFrames.mDisplayCutout.getDisplayCutout().isEmpty()) {
@@ -2531,7 +2519,7 @@
             if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
                 mForceStatusBar = true;
             }
-            if (attrs.type == TYPE_DREAM) {
+            if (win.isDreamWindow()) {
                 // If the lockscreen was showing when the dream started then wait
                 // for the dream to draw before hiding the lockscreen.
                 if (!mDreamingLockscreen
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 60b817c..af89a05 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -484,11 +484,8 @@
             prepareNormalRotationAnimation();
         }
 
-        // TODO(b/147469351): Remove the restriction.
-        if (mDisplayContent.mFixedRotationLaunchingApp == null) {
-            // Give a remote handler (system ui) some time to reposition things.
-            startRemoteRotation(oldRotation, mRotation);
-        }
+        // Give a remote handler (system ui) some time to reposition things.
+        startRemoteRotation(oldRotation, mRotation);
 
         return true;
     }
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index bb02789..fda70d1 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -25,6 +25,7 @@
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
 
@@ -91,6 +92,13 @@
                 || focusedWin != getNavControlTarget(focusedWin)
                 || focusedWin.getRequestedInsetsState().getSource(ITYPE_NAVIGATION_BAR)
                         .isVisible());
+        updateHideNavInputEventReceiver();
+    }
+
+    private void updateHideNavInputEventReceiver() {
+        mPolicy.updateHideNavInputEventReceiver(!isHidden(ITYPE_NAVIGATION_BAR),
+                mFocusedWin != null
+                        && mFocusedWin.mAttrs.insetsFlags.behavior != BEHAVIOR_SHOW_BARS_BY_TOUCH);
     }
 
     boolean isHidden(@InternalInsetsType int type) {
@@ -169,6 +177,7 @@
         if (windowState == getNavControlTarget(mFocusedWin)) {
             mNavBar.setVisible(state.getSource(ITYPE_NAVIGATION_BAR).isVisible());
         }
+        updateHideNavInputEventReceiver();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index a4bdfb3..04454a5 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -88,8 +88,8 @@
         final InsetsSourceProvider provider = target.getControllableInsetProvider();
         final @InternalInsetsType int type = provider != null
                 ? provider.getSource().getType() : ITYPE_INVALID;
-        return getInsetsForTypeAndWindowingMode(type, target.getWindowingMode(),
-                target.isAlwaysOnTop());
+        return getInsetsForDispatchInner(type, target.getWindowingMode(), target.isAlwaysOnTop(),
+                isAboveIme(target));
     }
 
     InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
@@ -97,13 +97,24 @@
         final WindowToken token = mDisplayContent.getWindowToken(attrs.token);
         final @WindowingMode int windowingMode = token != null
                 ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
-        final boolean alwaysOnTop = token != null
-                ? token.isAlwaysOnTop() : false;
-        return getInsetsForTypeAndWindowingMode(type, windowingMode, alwaysOnTop);
+        final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
+        return getInsetsForDispatchInner(type, windowingMode, alwaysOnTop, isAboveIme(token));
+    }
+
+    private boolean isAboveIme(WindowContainer target) {
+        final WindowState imeWindow = mDisplayContent.mInputMethodWindow;
+        if (target == null || imeWindow == null) {
+            return false;
+        }
+        if (target instanceof WindowState) {
+            final WindowState win = (WindowState) target;
+            return win.needsRelativeLayeringToIme() || !win.mBehindIme;
+        }
+        return false;
     }
 
     private static @InternalInsetsType int getInsetsTypeForWindowType(int type) {
-        switch(type) {
+        switch (type) {
             case TYPE_STATUS_BAR:
                 return ITYPE_STATUS_BAR;
             case TYPE_NAVIGATION_BAR:
@@ -116,8 +127,8 @@
     }
 
     /** @see #getInsetsForDispatch */
-    private InsetsState getInsetsForTypeAndWindowingMode(@InternalInsetsType int type,
-            @WindowingMode int windowingMode, boolean isAlwaysOnTop) {
+    private InsetsState getInsetsForDispatchInner(@InternalInsetsType int type,
+            @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) {
         InsetsState state = mState;
 
         if (type != ITYPE_INVALID) {
@@ -158,6 +169,11 @@
             state.removeSource(ITYPE_NAVIGATION_BAR);
         }
 
+        if (aboveIme) {
+            state = new InsetsState(state);
+            state.removeSource(ITYPE_IME);
+        }
+
         return state;
     }
 
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 44034ed..6b39fd2 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -406,12 +406,11 @@
             // show on top of the lock screen. In this can we want to dismiss the docked
             // stack since it will be complicated/risky to try to put the activity on top
             // of the lock screen in the right fullscreen configuration.
-            final ActivityStack stack =
-                    mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask();
-            if (stack == null) {
+            final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
+            if (!display.isSplitScreenModeActivated()) {
                 return;
             }
-            mRootWindowContainer.getDefaultDisplay().onSplitScreenModeDismissed();
+            display.onSplitScreenModeDismissed();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index bd5666d..12be9df 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -21,9 +21,11 @@
 import static android.app.ActivityManager.RECENT_WITH_EXCLUDED;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -1298,6 +1300,7 @@
         switch (task.getActivityType()) {
             case ACTIVITY_TYPE_HOME:
             case ACTIVITY_TYPE_RECENTS:
+            case ACTIVITY_TYPE_DREAM:
                 // Ignore certain activity types completely
                 return false;
             case ACTIVITY_TYPE_ASSISTANT:
@@ -1307,6 +1310,7 @@
                         == FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) {
                     return false;
                 }
+                break;
         }
 
         // Ignore certain windowing modes
@@ -1314,23 +1318,21 @@
             case WINDOWING_MODE_PINNED:
                 return false;
             case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
-                if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "\ttop=" + task.getStack().getTopMostTask());
+                if (DEBUG_RECENTS_TRIM_TASKS) {
+                    Slog.d(TAG, "\ttop=" + task.getStack().getTopMostTask());
+                }
                 final ActivityStack stack = task.getStack();
                 if (stack != null && stack.getTopMostTask() == task) {
                     // Only the non-top task of the primary split screen mode is visible
                     return false;
                 }
-        }
-
-        // Tasks managed by/associated with an ActivityView should be excluded from recents.
-        // singleTaskInstance is set on the VirtualDisplay managed by ActivityView
-        // TODO(b/126185105): Find a different signal to use besides isSingleTaskInstance
-        final ActivityStack stack = task.getStack();
-        if (stack != null) {
-            DisplayContent display = stack.getDisplay();
-            if (display != null && display.isSingleTaskInstance()) {
-                return false;
-            }
+                break;
+            case WINDOWING_MODE_MULTI_WINDOW:
+                // Ignore tasks that are always on top
+                if (task.isAlwaysOnTop()) {
+                    return false;
+                }
+                break;
         }
 
         // If we're in lock task mode, ignore the root task
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index adafdec..057592c 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -388,17 +388,24 @@
                     // surfaces needs to be done immediately.
                     mWindowManager.executeAppTransition();
 
-                    if (targetStack.getTile() != null) {
+                    final Task rootTask = targetStack.getRootTask();
+                    if (rootTask.isOrganized()) {
                         // Client state may have changed during the recents animation, so force
                         // send task info so the client can synchronize its state.
                         mService.mTaskOrganizerController.dispatchTaskInfoChanged(
-                                targetStack.mTile, true /* force */);
+                                rootTask, true /* force */);
                     }
                 } catch (Exception e) {
                     Slog.e(TAG, "Failed to clean up recents activity", e);
                     throw e;
                 } finally {
                     mService.continueWindowLayout();
+                    // Make sure the surfaces are updated with the latest state. Sometimes the
+                    // surface placement may be skipped if display configuration is changed (i.e.
+                    // {@link DisplayContent#mWaitingForConfig} is true).
+                    if (mWindowManager.mRoot.isLayoutNeeded()) {
+                        mWindowManager.mRoot.performSurfacePlacement();
+                    }
                     Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 }
             });
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 3e5cb50..a30b70d 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -667,7 +667,7 @@
                         mTargetActivityRecord.token);
             }
             if (mTargetActivityRecord.hasFixedRotationTransform()) {
-                mTargetActivityRecord.clearFixedRotationTransform();
+                mTargetActivityRecord.finishFixedRotationTransform();
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 45f8a15..420997a 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -20,7 +20,6 @@
 import static com.android.server.wm.ActivityStack.TAG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.wm.Task.REPARENT_LEAVE_STACK_IN_PLACE;
 
 import android.app.ActivityOptions;
 import android.content.Intent;
@@ -233,29 +232,6 @@
         }
 
         final ActivityTaskManagerService atmService = mTargetStack.mAtmService;
-        final ArrayList<Task> createdTasks = new ArrayList<>();
-        while (!mPendingReparentActivities.isEmpty()) {
-            final ActivityRecord r = mPendingReparentActivities.remove(0);
-            final ActivityRecord bottom = mTargetStack.getBottomMostActivity();
-            final Task targetTask;
-            if (bottom != null && r.taskAffinity.equals(bottom.getTask().affinity)) {
-                // If the activity currently at the bottom has the same task affinity as
-                // the one we are moving, then merge it into the same task.
-                targetTask = bottom.getTask();
-                if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
-                        + r + " out to bottom task " + targetTask);
-            } else {
-                targetTask = mTargetStack.reuseOrCreateTask(
-                        r.info, null /*intent*/, false /*toTop*/);
-                targetTask.affinityIntent = r.intent;
-                createdTasks.add(targetTask);
-                if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
-                        + r + " out to new task " + targetTask);
-            }
-            r.reparent(targetTask, 0 /* position */, "resetTargetTaskIfNeeded");
-            atmService.mStackSupervisor.mRecentTasks.add(targetTask);
-        }
-
         DisplayContent display = mTargetStack.getDisplay();
         final boolean singleTaskInstanceDisplay = display.isSingleTaskInstance();
         if (singleTaskInstanceDisplay) {
@@ -264,16 +240,33 @@
 
         final int windowingMode = mTargetStack.getWindowingMode();
         final int activityType = mTargetStack.getActivityType();
-        if (!singleTaskInstanceDisplay && !display.alwaysCreateStack(windowingMode, activityType)) {
-            return;
-        }
 
-        while (!createdTasks.isEmpty()) {
-            final Task targetTask = createdTasks.remove(createdTasks.size() - 1);
-            final ActivityStack targetStack = display.getOrCreateStack(
-                    windowingMode, activityType, false /* onTop */);
-            targetTask.reparent(targetStack, false /* toTop */, REPARENT_LEAVE_STACK_IN_PLACE,
-                    false /* animate */, true /* deferResume */, "resetTargetTask");
+        while (!mPendingReparentActivities.isEmpty()) {
+            final ActivityRecord r = mPendingReparentActivities.remove(0);
+            final boolean alwaysCreateTask = DisplayContent.alwaysCreateStack(windowingMode,
+                    activityType);
+            final Task task = alwaysCreateTask
+                    ? display.getBottomMostTask() : mTargetStack.getBottomMostTask();
+            Task targetTask = null;
+            if (task != null && r.taskAffinity.equals(task.affinity)) {
+                // If the activity currently at the bottom has the same task affinity as
+                // the one we are moving, then merge it into the same task.
+                targetTask = task;
+                if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
+                        + r + " out to bottom task " + targetTask);
+            }
+            if (targetTask == null) {
+                if (alwaysCreateTask) {
+                    targetTask = display.getOrCreateStack(windowingMode, activityType,
+                            false /* onTop */);
+                } else {
+                    targetTask = mTargetStack.reuseOrCreateTask(r.info, null /*intent*/,
+                            false /*toTop*/);
+                }
+                targetTask.affinityIntent = r.intent;
+            }
+            r.reparent(targetTask, 0 /* position */, "resetTargetTaskIfNeeded");
+            atmService.mStackSupervisor.mRecentTasks.add(targetTask);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ebf1bc9..76c16d4 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -35,7 +36,6 @@
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
 import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
@@ -795,10 +795,10 @@
         return leakedSurface || killedApps;
     }
 
-    void performSurfacePlacement(boolean recoveringMemory) {
+    void performSurfacePlacement() {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
         try {
-            performSurfacePlacementNoTrace(recoveringMemory);
+            performSurfacePlacementNoTrace();
         } finally {
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
@@ -806,7 +806,7 @@
 
     // "Something has changed!  Let's make it correct now."
     // TODO: Super crazy long method that should be broken down...
-    void performSurfacePlacementNoTrace(boolean recoveringMemory) {
+    void performSurfacePlacementNoTrace() {
         if (DEBUG_WINDOW_TRACE) Slog.v(TAG, "performSurfacePlacementInner: entry. Called by "
                 + Debug.getCallers(3));
 
@@ -841,7 +841,7 @@
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
         mWmService.openSurfaceTransaction();
         try {
-            applySurfaceChangesTransaction(recoveringMemory);
+            applySurfaceChangesTransaction();
         } catch (RuntimeException e) {
             Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
         } finally {
@@ -1041,7 +1041,7 @@
         }
     }
 
-    private void applySurfaceChangesTransaction(boolean recoveringMemory) {
+    private void applySurfaceChangesTransaction() {
         mHoldScreenWindow = null;
         mObscuringWindow = null;
 
@@ -1064,7 +1064,7 @@
         final int count = mChildren.size();
         for (int j = 0; j < count; ++j) {
             final DisplayContent dc = mChildren.get(j);
-            dc.applySurfaceChangesTransaction(recoveringMemory);
+            dc.applySurfaceChangesTransaction();
         }
 
         // Give the display manager a chance to adjust properties like display rotation if it needs
@@ -1137,7 +1137,7 @@
                 // While a dream or keyguard is showing, obscure ordinary application content on
                 // secondary displays (by forcibly enabling mirroring unless there is other content
                 // we want to show) but still allow opaque keyguard dialogs to be shown.
-                if (type == TYPE_DREAM || mWmService.mPolicy.isKeyguardShowing()) {
+                if (w.isDreamWindow() || mWmService.mPolicy.isKeyguardShowing()) {
                     mObscureApplicationContentOnSecondaryDisplays = true;
                 }
                 displayHasContent = true;
@@ -1970,8 +1970,7 @@
         final int focusStackId = topFocusedStack != null
                 ? topFocusedStack.getRootTaskId() : INVALID_TASK_ID;
         // We dismiss the docked stack whenever we switch users.
-        final ActivityStack dockedStack = getDefaultDisplay().getRootSplitScreenPrimaryTask();
-        if (dockedStack != null) {
+        if (getDefaultDisplay().isSplitScreenModeActivated()) {
             getDefaultDisplay().onSplitScreenModeDismissed();
         }
         // Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
@@ -2110,20 +2109,18 @@
             final ActivityStack stack;
             if (singleActivity) {
                 stack = r.getRootTask();
+                stack.setWindowingMode(WINDOWING_MODE_PINNED);
             } else {
-                // In the case of multiple activities, we will create a new stack for it and then
-                // move the PIP activity into the stack.
-                // We will then perform a windowing mode change for both scenarios.
-                stack = display.createStack(
-                        r.getRootTask().getRequestedOverrideWindowingMode(),
-                        r.getActivityType(), ON_TOP, r.info, r.intent);
+                // In the case of multiple activities, we will create a new task for it and then
+                // move the PIP activity into the task.
+                stack = display.createStack(WINDOWING_MODE_PINNED, r.getActivityType(), ON_TOP,
+                        r.info, r.intent, false /* createdByOrganizer */);
+
                 // There are multiple activities in the task and moving the top activity should
                 // reveal/leave the other activities in their original task.
                 r.reparent(stack, MAX_VALUE, "moveActivityToStack");
             }
 
-            stack.setWindowingMode(WINDOWING_MODE_PINNED);
-
             // Reset the state that indicates it can enter PiP while pausing after we've moved it
             // to the pinned stack
             r.supportsEnterPipOnTaskSwitch = false;
@@ -2291,6 +2288,9 @@
                                 TRANSIT_SHOW_SINGLE_TASK_DISPLAY, false);
                     }
                     stack.awakeFromSleepingLocked();
+                    if (display.isSingleTaskInstance()) {
+                        display.executeAppTransition();
+                    }
                     if (stack.isFocusedStackOnDisplay()
                             && !mStackSupervisor.getKeyguardController()
                             .isKeyguardOrAodShowing(display.mDisplayId)) {
@@ -2799,16 +2799,19 @@
         if (stack == null && r != null) {
             stack = r.getRootTask();
         }
+        int windowingMode = launchParams != null ? launchParams.mWindowingMode
+                : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
         if (stack != null) {
             display = stack.getDisplay();
             if (display != null && canLaunchOnDisplay(r, display.mDisplayId)) {
-                int windowingMode = launchParams != null ? launchParams.mWindowingMode
-                        : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
                 if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
                     windowingMode = display.resolveWindowingMode(r, options, candidateTask,
                             activityType);
                 }
-                if (stack.isCompatible(windowingMode, activityType)) {
+                // Always allow organized tasks that created by organizer since the activity type
+                // of an organized task is decided by the activity type of its top child, which
+                // could be incompatible with the given windowing mode and activity type.
+                if (stack.isCompatible(windowingMode, activityType) || stack.mCreatedByOrganizer) {
                     return stack;
                 }
                 if (windowingMode == WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
@@ -2826,6 +2829,10 @@
 
         if (display == null || !canLaunchOnDisplay(r, display.mDisplayId)) {
             display = getDefaultDisplay();
+            if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+                windowingMode = display.resolveWindowingMode(r, options, candidateTask,
+                        activityType);
+            }
         }
 
         return display.getOrCreateStack(r, options, candidateTask, activityType, onTop);
@@ -2847,7 +2854,8 @@
      * @param candidateTask The possible task the activity might be put in.
      * @return Existing stack if there is a valid one, new dynamic stack if it is valid or null.
      */
-    private ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
+    @VisibleForTesting
+    ActivityStack getValidLaunchStackOnDisplay(int displayId, @NonNull ActivityRecord r,
             @Nullable Task candidateTask, @Nullable ActivityOptions options,
             @Nullable LaunchParamsController.LaunchParams launchParams) {
         final DisplayContent displayContent = getDisplayContentOrCreate(displayId);
@@ -2868,6 +2876,13 @@
             if (attachedDisplayId == INVALID_DISPLAY || attachedDisplayId == displayId) {
                 return candidateTask.getStack();
             }
+            // Or the candidate task is already a root task that can be reused by reparenting
+            // it to the target display.
+            if (candidateTask.isRootTask()) {
+                final ActivityStack stack = candidateTask.getStack();
+                displayContent.moveStackToDisplay(stack, true /* onTop */);
+                return stack;
+            }
         }
 
         int windowingMode;
@@ -2915,11 +2930,10 @@
             case ACTIVITY_TYPE_HOME: return r.isActivityTypeHome();
             case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
             case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
+            case ACTIVITY_TYPE_DREAM: return r.isActivityTypeDream();
         }
-        // TODO(task-hierarchy): Find another way to differentiate tile from normal stack once it is
-        //                       part of the hierarchy
-        if (stack instanceof TaskTile) {
-            // Don't launch directly into tiles.
+        if (stack.mCreatedByOrganizer) {
+            // Don't launch directly into task created by organizer...but why can't we?
             return false;
         }
         // There is a 1-to-1 relationship between stack and task when not in
@@ -3381,11 +3395,10 @@
 
     @VisibleForTesting
     void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list,
-            @WindowConfiguration.ActivityType int ignoreActivityType,
-            @WindowConfiguration.WindowingMode int ignoreWindowingMode, int callingUid,
-            boolean allowed, boolean crossUser, ArraySet<Integer> profileIds) {
-        mStackSupervisor.getRunningTasks().getTasks(maxNum, list, ignoreActivityType,
-                ignoreWindowingMode, this, callingUid, allowed, crossUser, profileIds);
+            boolean filterOnlyVisibleRecents, int callingUid, boolean allowed, boolean crossUser,
+            ArraySet<Integer> profileIds) {
+        mStackSupervisor.getRunningTasks().getTasks(maxNum, list, filterOnlyVisibleRecents, this,
+                callingUid, allowed, crossUser, profileIds);
     }
 
     void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 02077fb..3509ba72 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -16,12 +16,10 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.WindowConfiguration.ActivityType;
-import android.app.WindowConfiguration.WindowingMode;
 import android.os.UserHandle;
 import android.util.ArraySet;
 
@@ -49,13 +47,13 @@
     private boolean mCrossUser;
     private ArraySet<Integer> mProfileIds;
     private boolean mAllowed;
-    private int mIgnoreActivityType;
-    private int mIgnoreWindowingMode;
+    private boolean mFilterOnlyVisibleRecents;
     private ActivityStack mTopDisplayFocusStack;
+    private RecentTasks mRecentTasks;
 
-    void getTasks(int maxNum, List<RunningTaskInfo> list, @ActivityType int ignoreActivityType,
-            @WindowingMode int ignoreWindowingMode, RootWindowContainer root,
-            int callingUid, boolean allowed, boolean crossUser, ArraySet<Integer> profileIds) {
+    void getTasks(int maxNum, List<RunningTaskInfo> list, boolean filterOnlyVisibleRecents,
+            RootWindowContainer root, int callingUid, boolean allowed, boolean crossUser,
+            ArraySet<Integer> profileIds) {
         // Return early if there are no tasks to fetch
         if (maxNum <= 0) {
             return;
@@ -68,9 +66,9 @@
         mCrossUser = crossUser;
         mProfileIds = profileIds;
         mAllowed = allowed;
-        mIgnoreActivityType = ignoreActivityType;
-        mIgnoreWindowingMode = ignoreWindowingMode;
+        mFilterOnlyVisibleRecents = filterOnlyVisibleRecents;
         mTopDisplayFocusStack = root.getTopDisplayFocusedStack();
+        mRecentTasks = root.mService.getRecentTasks();
 
         final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
                 PooledLambda.__(Task.class));
@@ -107,14 +105,12 @@
                 return;
             }
         }
-        if (mIgnoreActivityType != ACTIVITY_TYPE_UNDEFINED
-                && task.getActivityType() == mIgnoreActivityType) {
-            // Skip ignored activity type
-            return;
-        }
-        if (mIgnoreWindowingMode != WINDOWING_MODE_UNDEFINED
-                && task.getWindowingMode() == mIgnoreWindowingMode) {
-            // Skip ignored windowing mode
+        if (mFilterOnlyVisibleRecents
+                && task.getActivityType() != ACTIVITY_TYPE_HOME
+                && task.getActivityType() != ACTIVITY_TYPE_RECENTS
+                && !mRecentTasks.isVisibleRecentTask(task)) {
+            // Skip if this task wouldn't be visibile (ever) from recents, with an exception for the
+            // home & recent tasks
             return;
         }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 55dc694..91b4ec9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -20,6 +20,9 @@
 import static android.app.ActivityTaskManager.RESIZE_MODE_FORCED;
 import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
 import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
@@ -114,7 +117,6 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Debug;
 import android.os.IBinder;
@@ -129,11 +131,11 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
-import android.window.ITaskOrganizer;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationTarget;
 import android.view.Surface;
 import android.view.SurfaceControl;
+import android.window.ITaskOrganizer;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
@@ -256,7 +258,7 @@
     boolean autoRemoveRecents;  // If true, we should automatically remove the task from
                                 // recents when activity finishes
     boolean askedCompatMode;// Have asked the user about compat mode for this task.
-    boolean hasBeenVisible; // Set if any activities in the task have been visible to the user.
+    private boolean mHasBeenVisible; // Set if any activities in the task have been visible
 
     String stringName;      // caching of toString() result.
     boolean mUserSetupComplete; // The user set-up is complete as of the last time the task activity
@@ -481,6 +483,10 @@
      */
     ITaskOrganizer mTaskOrganizer;
     private int mLastTaskOrganizerWindowingMode = -1;
+    /**
+     * Prevent duplicate calls to onTaskAppeared.
+     */
+    boolean mTaskAppearedSent;
 
     /**
      * Last Picture-in-Picture params applicable to the task. Updated when the app
@@ -489,6 +495,17 @@
     PictureInPictureParams mPictureInPictureParams = new PictureInPictureParams.Builder().build();
 
     /**
+     * This task was created by the task organizer which has the following implementations.
+     * <ul>
+     *     <lis>The task won't be removed when it is empty. Removal has to be an explicit request
+     *     from the task organizer.</li>
+     *     <li>Unlike other non-root tasks, it's direct children are visible to the task
+     *     organizer for ordering purposes.</li>
+     * </ul>
+     */
+    boolean mCreatedByOrganizer;
+
+    /**
      * Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
      * ActivityInfo, Intent, TaskDescription)} instead.
      */
@@ -578,6 +595,14 @@
         voiceInteractor = _voiceInteractor;
         setIntent(activity, intent, info);
         setMinDimensions(info);
+        // Before we began to reuse a root task (old ActivityStack) as the leaf task, we used to
+        // create a leaf task in this case. Therefore now we won't send out the task created
+        // notification when we decide to reuse it here, so we send out the notification below.
+        // The reason why the created notification sent out when root task is created doesn't work
+        // is that realActivity isn't set until setIntent() method above is called for the first
+        // time. Eventually this notification will be removed when we can populate those information
+        // when root task is created.
+        mAtmService.getTaskChangeNotificationController().notifyTaskCreated(mTaskId, realActivity);
         return this;
     }
 
@@ -586,11 +611,7 @@
             return;
         }
 
-        // TODO(xutan): Removed type check after stack and task is merged.
-        // Before the real merge of stack and task, we need to avoid saving state of stacks. Once
-        // the merge is finished we can just pass DisplayContent because both windowing mode and
-        // bounds are set in the merged task.
-        if (oldParent instanceof ActivityStack) {
+        if (isLeafTask()) {
             // This task is going away, so save the last state if necessary.
             saveLaunchingStateIfNeeded(((WindowContainer) oldParent).getDisplayContent());
         }
@@ -1349,7 +1370,7 @@
         if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
             return applicationType;
         }
-        return getChildAt(0).getActivityType();
+        return getTopChild().getActivityType();
     }
 
     @Override
@@ -1362,6 +1383,12 @@
 
         ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addChild: %s at top.", this);
 
+        // A rootable task that is now being added to be the child of an organized task. Making
+        // sure the stack references is keep updated.
+        if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) {
+            mDisplayContent.addStackReferenceIfNeeded((ActivityStack) child);
+        }
+
         // Make sure the list of display UID whitelists is updated
         // now that this record is in a new task.
         mRootWindowContainer.updateUIDsPresentOnDisplay();
@@ -1402,6 +1429,11 @@
 
     @Override
     void removeChild(WindowContainer child) {
+        // A rootable child task that is now being removed from an organized task. Making sure
+        // the stack references is keep updated.
+        if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) {
+            mDisplayContent.removeStackReferenceIfNeeded((ActivityStack) child);
+        }
         removeChild(child, "removeChild");
     }
 
@@ -1449,8 +1481,9 @@
                 mStackSupervisor.removeTask(this, false /* killProcess */,
                         !REMOVE_FROM_RECENTS, reason);
             }
-        } else if (!mReuseTask) {
+        } else if (!mReuseTask && !mCreatedByOrganizer) {
             // Remove entire task if it doesn't have any activity left and it isn't marked for reuse
+            // or created by task organizer.
             if (!isRootTask) {
                 getStack().removeChild(this, reason);
             }
@@ -1488,7 +1521,7 @@
         // We will automatically remove the task either if it has explicitly asked for
         // this, or it is empty and has never contained an activity that got shown to
         // the user.
-        return autoRemoveRecents || (!hasChild() && !hasBeenVisible);
+        return autoRemoveRecents || (!hasChild() && !getHasBeenVisible());
     }
 
     /**
@@ -1862,7 +1895,12 @@
         final Task parentTask = getParent().asTask();
         if (parentTask != null) {
             parentTask.onActivityStateChanged(record, state, reason);
-            return;
+            // We still want to update the resumed activity if the parent task is created by
+            // organizer in order to keep the information synced once got reparented out from the
+            // organized task.
+            if (!parentTask.mCreatedByOrganizer) {
+                return;
+            }
         }
 
         if (record == mResumedActivity && state != RESUMED) {
@@ -1996,7 +2034,7 @@
     }
 
     private void saveLaunchingStateIfNeeded(DisplayContent display) {
-        if (!hasBeenVisible) {
+        if (!getHasBeenVisible()) {
             // Not ever visible to user.
             return;
         }
@@ -2296,18 +2334,30 @@
         return Configuration.reduceScreenLayout(sourceScreenLayout, longSize, shortSize);
     }
 
-    void resolveTileOverrideConfiguration(Configuration newParentConfig) {
+    private void resolveOrganizedOverrideConfiguration(Configuration newParentConfig) {
         super.resolveOverrideConfiguration(newParentConfig);
+        if (!isOrganized()) {
+            return;
+        }
+
+        final Task root = getRootTask();
+        if (root == this) {
+            return;
+        }
+
+        // Ensure to have the same windowing mode for the child tasks that controlled by task org.
+        getResolvedOverrideConfiguration().windowConfiguration
+                .setWindowingMode(root.getWindowingMode());
     }
 
     @Override
     void resolveOverrideConfiguration(Configuration newParentConfig) {
-        if (!isLeafTask()) {
-            resolveTileOverrideConfiguration(newParentConfig);
+        if (!isLeafTask() || mCreatedByOrganizer) {
+            resolveOrganizedOverrideConfiguration(newParentConfig);
             return;
         }
         mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
-        resolveTileOverrideConfiguration(newParentConfig);
+        resolveOrganizedOverrideConfiguration(newParentConfig);
         int windowingMode =
                 getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
         if (windowingMode == WINDOWING_MODE_UNDEFINED) {
@@ -2396,7 +2446,9 @@
     }
 
     Rect updateOverrideConfigurationFromLaunchBounds() {
-        final Rect bounds = getLaunchBounds();
+        // If the task is controlled by another organized task, do not set override
+        // configurations and let its parent (organized task) to control it;
+        final Rect bounds = isOrganized() && !isRootTask() ? null : getLaunchBounds();
         setBounds(bounds);
         if (bounds != null && !bounds.isEmpty()) {
             // TODO: Review if we actually want to do this - we are setting the launch bounds
@@ -2580,7 +2632,7 @@
         // preserve POSITION_BOTTOM/POSITION_TOP positions if they are still valid.
         if (suggestedPosition == POSITION_BOTTOM && minPosition == 0) {
             return POSITION_BOTTOM;
-        } else if (suggestedPosition == POSITION_TOP && maxPosition == (size - 1)) {
+        } else if (suggestedPosition == POSITION_TOP && maxPosition >= (size - 1)) {
             return POSITION_TOP;
         }
         // Reset position based on minimum/maximum possible positions.
@@ -3199,6 +3251,20 @@
     }
 
     @Override
+    int getOrientation(int candidate) {
+        return canSpecifyOrientation() ? super.getOrientation(candidate) : SCREEN_ORIENTATION_UNSET;
+    }
+
+    private boolean canSpecifyOrientation() {
+        final int windowingMode = getWindowingMode();
+        final int activityType = getActivityType();
+        return windowingMode == WINDOWING_MODE_FULLSCREEN
+                || activityType == ACTIVITY_TYPE_HOME
+                || activityType == ACTIVITY_TYPE_RECENTS
+                || activityType == ACTIVITY_TYPE_ASSISTANT;
+    }
+
+    @Override
     boolean fillsParent() {
         return matchParentBounds();
     }
@@ -3364,14 +3430,12 @@
         info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
         info.configuration.setTo(getConfiguration());
         info.token = mRemoteToken;
-        // Get's the first non-undefined activity type among this and children. Can't use
-        // configuration.windowConfiguration because that would only be this level.
-        info.topActivityType = getActivityType();
 
         //TODO (AM refactor): Just use local once updateEffectiveIntent is run during all child
         //                    order changes.
         final Task top = getTopMostTask();
         info.resizeMode = top != null ? top.mResizeMode : mResizeMode;
+        info.topActivityType = top.getActivityType();
 
         if (mPictureInPictureParams.empty()) {
             info.pictureInPictureParams = null;
@@ -3402,10 +3466,6 @@
         return this;
     }
 
-    TaskTile asTile() {
-        return null;
-    }
-
     // TODO(task-merge): Figure-out how this should work with hierarchy tasks.
     boolean shouldBeVisible(ActivityRecord starting) {
         return true;
@@ -3502,7 +3562,7 @@
             pw.print(prefix); pw.print("mRootProcess="); pw.println(mRootProcess);
         }
         pw.print(prefix); pw.print("taskId=" + mTaskId); pw.println(" stackId=" + getRootTaskId());
-        pw.print(prefix + "hasBeenVisible=" + hasBeenVisible);
+        pw.print(prefix + "mHasBeenVisible=" + getHasBeenVisible());
         pw.print(" mResizeMode=" + ActivityInfo.resizeModeToString(mResizeMode));
         pw.print(" mSupportsPictureInPicture=" + mSupportsPictureInPicture);
         pw.print(" isResizeable=" + isResizeable());
@@ -3701,8 +3761,9 @@
     }
 
     static Task create(ActivityTaskManagerService service, int taskId, int activityType,
-            ActivityInfo info, Intent intent) {
-        return getTaskFactory().create(service, taskId, activityType, info, intent);
+            ActivityInfo info, Intent intent, boolean createdByOrganizer) {
+        return getTaskFactory().create(service, taskId, activityType, info, intent,
+                createdByOrganizer);
     }
 
     static Task create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
@@ -3724,8 +3785,9 @@
      */
     static class TaskFactory {
         Task create(ActivityTaskManagerService service, int taskId, int activityType,
-                ActivityInfo info, Intent intent) {
-            return new ActivityStack(service, taskId, activityType, info, intent);
+                ActivityInfo info, Intent intent, boolean createdByOrganizer) {
+            return new ActivityStack(service, taskId, activityType, info, intent,
+                    createdByOrganizer);
         }
 
         Task create(ActivityTaskManagerService service, int taskId, ActivityInfo info,
@@ -4001,34 +4063,70 @@
     @Override
     boolean isOrganized() {
         final Task rootTask = getRootTask();
-        // if the rootTask is a "child" of a tile, then don't consider it a root task.
-        // TODO: remove this along with removing tile.
-        if (((ActivityStack) rootTask).getTile() != null) {
+        if (rootTask.mTaskOrganizer == null) {
+            // You are obviously not organized...
             return false;
         }
-        return rootTask == this && rootTask.mTaskOrganizer != null;
+        if (rootTask == this) {
+            // Root tasks can be organized.
+            return true;
+        }
+        if (rootTask.mCreatedByOrganizer && getParent() == rootTask) {
+            // Direct children of tasks added by the organizer can the organized.
+            return true;
+        }
+
+        return false;
     }
 
     @Override
     protected void reparentSurfaceControl(SurfaceControl.Transaction t, SurfaceControl newParent) {
         /**
-         * Avoid yanking back control from the TaskOrganizer, which has presumably reparented the
-         * Surface in to its own hierarchy.
+         * Avoid reparenting SurfaceControl of the organized tasks that are always on top, since
+         * the surfaces should be controlled by the organizer itself, like bubbles.
          */
-        if (isOrganized()) {
+        if (isOrganized() && isAlwaysOnTop()) {
             return;
         }
         super.reparentSurfaceControl(t, newParent);
     }
 
+    void setHasBeenVisible(boolean hasBeenVisible) {
+        mHasBeenVisible = hasBeenVisible;
+        if (hasBeenVisible) {
+            sendTaskAppeared();
+            if (!isRootTask()) {
+                getRootTask().setHasBeenVisible(true);
+            }
+        }
+    }
+
+    boolean getHasBeenVisible() {
+        return mHasBeenVisible;
+    }
+
+    /** In the case that these three conditions are true, we want to send the Task to
+     * the organizer:
+     *     1. We have a SurfaceControl
+     *     2. An organizer has been set
+     *     3. We have finished drawing
+     * Any time any of these conditions are updated, the updating code should call
+     * sendTaskAppeared.
+     */
+    private boolean taskAppearedReady() {
+        return mSurfaceControl != null && mTaskOrganizer != null && getHasBeenVisible();
+    }
+
     private void sendTaskAppeared() {
-        if (mSurfaceControl != null && mTaskOrganizer != null) {
+        if (taskAppearedReady() && !mTaskAppearedSent) {
+            mTaskAppearedSent = true;
             mAtmService.mTaskOrganizerController.onTaskAppeared(mTaskOrganizer, this);
         }
     }
 
     private void sendTaskVanished() {
-        if (mTaskOrganizer != null) {
+        if (mTaskOrganizer != null && mTaskAppearedSent) {
+            mTaskAppearedSent = false;
             mAtmService.mTaskOrganizerController.onTaskVanished(mTaskOrganizer, this);
         }
    }
@@ -4045,11 +4143,14 @@
         return true;
     }
 
-    // Called on Binder death.
-    void taskOrganizerDied() {
+    void taskOrganizerUnregistered() {
         mTaskOrganizer = null;
+        mTaskAppearedSent = false;
         mLastTaskOrganizerWindowingMode = -1;
         onTaskOrganizerChanged();
+        if (mCreatedByOrganizer) {
+            removeImmediately();
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 5523678..4382e9d 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -27,19 +28,22 @@
 
 import android.annotation.Nullable;
 import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
-import android.window.ITaskOrganizerController;
+import android.util.SparseArray;
 import android.window.ITaskOrganizer;
+import android.window.ITaskOrganizerController;
 import android.window.IWindowContainer;
 
 import com.android.internal.util.ArrayUtils;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.WeakHashMap;
 
@@ -49,6 +53,7 @@
  */
 class TaskOrganizerController extends ITaskOrganizerController.Stub {
     private static final String TAG = "TaskOrganizerController";
+    private static final LinkedList<TaskOrganizerState> EMPTY_LIST = new LinkedList<>();
 
     /**
      * Masks specifying which configurations are important to report back to an organizer when
@@ -71,32 +76,20 @@
         @Override
         public void binderDied() {
             synchronized (mGlobalLock) {
-                final TaskOrganizerState state =
-                    mTaskOrganizerStates.get(mTaskOrganizer.asBinder());
-                state.releaseTasks();
-                mTaskOrganizerStates.remove(mTaskOrganizer.asBinder());
-                if (mTaskOrganizersForWindowingMode.get(mWindowingMode) == mTaskOrganizer) {
-                    mTaskOrganizersForWindowingMode.remove(mWindowingMode);
-                }
+                final TaskOrganizerState state = mTaskOrganizerStates.remove(
+                        mTaskOrganizer.asBinder());
+                state.dispose();
             }
         }
     };
 
-    class TaskOrganizerState {
-        ITaskOrganizer mOrganizer;
-        DeathRecipient mDeathRecipient;
-        int mWindowingMode;
+    private class TaskOrganizerState {
+        private final ITaskOrganizer mOrganizer;
+        private final DeathRecipient mDeathRecipient;
+        private final int mWindowingMode;
+        private final ArrayList<Task> mOrganizedTasks = new ArrayList<>();
 
-        ArrayList<Task> mOrganizedTasks = new ArrayList<>();
-
-        // Save the TaskOrganizer which we replaced registration for
-        // so it can be re-registered if we unregister.
-        TaskOrganizerState mReplacementFor;
-        boolean mDisposed = false;
-
-
-        TaskOrganizerState(ITaskOrganizer organizer, int windowingMode,
-                @Nullable TaskOrganizerState replacing) {
+        TaskOrganizerState(ITaskOrganizer organizer, int windowingMode) {
             mOrganizer = organizer;
             mDeathRecipient = new DeathRecipient(organizer, windowingMode);
             try {
@@ -105,7 +98,6 @@
                 Slog.e(TAG, "TaskOrganizer failed to register death recipient");
             }
             mWindowingMode = windowingMode;
-            mReplacementFor = replacing;
         }
 
         void addTask(Task t) {
@@ -127,35 +119,26 @@
         }
 
         void dispose() {
-            mDisposed = true;
             releaseTasks();
-            handleReplacement();
+            mTaskOrganizersForWindowingMode.get(mWindowingMode).remove(this);
         }
 
-        void releaseTasks() {
+        private void releaseTasks() {
             for (int i = mOrganizedTasks.size() - 1; i >= 0; i--) {
                 final Task t = mOrganizedTasks.get(i);
-                t.taskOrganizerDied();
                 removeTask(t);
-            }
-        }
-
-        void handleReplacement() {
-            if (mReplacementFor != null && !mReplacementFor.mDisposed) {
-                mTaskOrganizersForWindowingMode.put(mWindowingMode, mReplacementFor);
+                t.taskOrganizerUnregistered();
             }
         }
 
         void unlinkDeath() {
-            mDisposed = true;
             mOrganizer.asBinder().unlinkToDeath(mDeathRecipient, 0);
         }
-    };
+    }
 
-
-    final HashMap<Integer, TaskOrganizerState> mTaskOrganizersForWindowingMode = new HashMap();
-    final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap();
-
+    private final SparseArray<LinkedList<TaskOrganizerState>> mTaskOrganizersForWindowingMode =
+            new SparseArray<>();
+    private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>();
     private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
     private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>();
 
@@ -194,11 +177,17 @@
                     Slog.w(TAG, "Task organizer already exists for windowing mode: "
                             + windowingMode);
                 }
-                final TaskOrganizerState previousState =
-                        mTaskOrganizersForWindowingMode.get(windowingMode);
-                final TaskOrganizerState state = new TaskOrganizerState(organizer, windowingMode,
-                        previousState);
-                mTaskOrganizersForWindowingMode.put(windowingMode, state);
+
+                LinkedList<TaskOrganizerState> states;
+                if (mTaskOrganizersForWindowingMode.contains(windowingMode)) {
+                    states = mTaskOrganizersForWindowingMode.get(windowingMode);
+                } else {
+                    states = new LinkedList<>();
+                    mTaskOrganizersForWindowingMode.put(windowingMode, states);
+                }
+                final TaskOrganizerState previousState = states.peekLast();
+                final TaskOrganizerState state = new TaskOrganizerState(organizer, windowingMode);
+                states.add(state);
                 mTaskOrganizerStates.put(organizer.asBinder(), state);
 
                 if (previousState == null) {
@@ -219,16 +208,14 @@
 
     @Override
     public void unregisterTaskOrganizer(ITaskOrganizer organizer) {
-        final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
+        final TaskOrganizerState state = mTaskOrganizerStates.remove(organizer.asBinder());
         state.unlinkDeath();
-        if (mTaskOrganizersForWindowingMode.get(state.mWindowingMode) == state) {
-            mTaskOrganizersForWindowingMode.remove(state.mWindowingMode);
-        }
         state.dispose();
     }
 
     ITaskOrganizer getTaskOrganizer(int windowingMode) {
-        final TaskOrganizerState state = mTaskOrganizersForWindowingMode.get(windowingMode);
+        final TaskOrganizerState state = mTaskOrganizersForWindowingMode.get(windowingMode,
+                EMPTY_LIST).peekLast();
         if (state == null) {
             return null;
         }
@@ -255,11 +242,12 @@
                 if (display == null) {
                     return null;
                 }
-                final int nextId = display.getNextStackId();
-                TaskTile tile = new TaskTile(mService, nextId, windowingMode);
-                display.addTile(tile);
-                RunningTaskInfo out = tile.getTaskInfo();
-                mLastSentTaskInfos.put(tile, out);
+
+                final Task task = display.getOrCreateStack(windowingMode, ACTIVITY_TYPE_UNDEFINED,
+                        false /* onTop */, new Intent(), null /* candidateTask */,
+                        true /* createdByOrganizer */);
+                RunningTaskInfo out = task.getTaskInfo();
+                mLastSentTaskInfos.put(task, out);
                 return out;
             }
         } finally {
@@ -273,11 +261,13 @@
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                TaskTile tile = TaskTile.forToken(token.asBinder());
-                if (tile == null) {
-                    return false;
+                final Task task = WindowContainer.fromBinder(token.asBinder()).asTask();
+                if (task == null) return false;
+                if (!task.mCreatedByOrganizer) {
+                    throw new IllegalArgumentException(
+                            "Attempt to delete task not created by organizer task=" + task);
                 }
-                tile.removeImmediately();
+                task.removeImmediately();
                 return true;
             }
         } finally {
@@ -358,12 +348,7 @@
                 if (task == null) {
                     return null;
                 }
-                ActivityStack rootTask = (ActivityStack) task.getRootTask();
-                final TaskTile tile = rootTask.getTile();
-                if (tile != null) {
-                    rootTask = tile;
-                }
-                return rootTask.mRemoteToken;
+                return task.getRootTask().mRemoteToken;
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -371,7 +356,7 @@
     }
 
     @Override
-    public void setLaunchRoot(int displayId, @Nullable IWindowContainer tile) {
+    public void setLaunchRoot(int displayId, @Nullable IWindowContainer token) {
         enforceStackPermission("setLaunchRoot()");
         final long origId = Binder.clearCallingIdentity();
         try {
@@ -380,16 +365,21 @@
                 if (display == null) {
                     return;
                 }
-                TaskTile taskTile = tile == null ? null : TaskTile.forToken(tile.asBinder());
-                if (taskTile == null) {
-                    display.mLaunchTile = null;
+                Task task = token == null
+                        ? null : WindowContainer.fromBinder(token.asBinder()).asTask();
+                if (task == null) {
+                    display.mLaunchRootTask = null;
                     return;
                 }
-                if (taskTile.getDisplay() != display) {
-                    throw new RuntimeException("Can't set launch root for display " + displayId
-                            + " to task on display " + taskTile.getDisplay().getDisplayId());
+                if (!task.mCreatedByOrganizer) {
+                    throw new IllegalArgumentException("Attempt to set task not created by "
+                            + "organizer as launch root task=" + task);
                 }
-                display.mLaunchTile = taskTile;
+                if (task.getDisplayContent() != display) {
+                    throw new RuntimeException("Can't set launch root for display " + displayId
+                            + " to task on display " + task.getDisplayContent().getDisplayId());
+                }
+                display.mLaunchRootTask = task;
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -411,25 +401,25 @@
                     Slog.e(TAG, "Can't get children of " + parent + " because it is not valid.");
                     return null;
                 }
-                // For now, only support returning children of persistent root tasks (of which the
-                // only current implementation is TaskTile).
-                if (!(container instanceof TaskTile)) {
+                final Task task = container.asTask();
+                if (task == null) {
+                    Slog.e(TAG, container + " is not a task...");
+                    return null;
+                }
+                // For now, only support returning children of tasks created by the organizer.
+                if (!task.mCreatedByOrganizer) {
                     Slog.w(TAG, "Can only get children of root tasks created via createRootTask");
                     return null;
                 }
                 ArrayList<RunningTaskInfo> out = new ArrayList<>();
-                // Tiles aren't real parents, so we need to go through stacks on the display to
-                // ensure correct ordering.
-                final DisplayContent dc = container.getDisplayContent();
-                for (int i = dc.getStackCount() - 1; i >= 0; --i) {
-                    final ActivityStack as = dc.getStackAt(i);
-                    if (as.getTile() == container) {
-                        if (activityTypes != null
-                                && !ArrayUtils.contains(activityTypes, as.getActivityType())) {
-                            continue;
-                        }
-                        out.add(as.getTaskInfo());
+                for (int i = task.getChildCount() - 1; i >= 0; --i) {
+                    final Task child = task.getChildAt(i).asTask();
+                    if (child == null) continue;
+                    if (activityTypes != null
+                            && !ArrayUtils.contains(activityTypes, child.getActivityType())) {
+                        continue;
                     }
+                    out.add(child.getTaskInfo());
                 }
                 return out;
             }
@@ -451,12 +441,7 @@
                 }
                 ArrayList<RunningTaskInfo> out = new ArrayList<>();
                 for (int i = dc.getStackCount() - 1; i >= 0; --i) {
-                    final ActivityStack task = dc.getStackAt(i);
-                    if (task.getTile() != null) {
-                        // a tile is supposed to look like a parent, so don't include their
-                        // "children" here. They can be accessed via getChildTasks()
-                        continue;
-                    }
+                    final Task task = dc.getStackAt(i);
                     if (activityTypes != null
                             && !ArrayUtils.contains(activityTypes, task.getActivityType())) {
                         continue;
diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java
deleted file mode 100644
index 51142b1..0000000
--- a/services/core/java/com/android/server/wm/TaskTile.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * Copyright (C) 2020 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.wm;
-
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-
-import android.app.ActivityManager;
-import android.app.TaskInfo;
-import android.app.WindowConfiguration;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.IBinder;
-import android.util.Slog;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.function.Consumer;
-
-/**
- * A Tile. Right now this acts as a proxy for manipulating non-child stacks. Eventually, this
- * can become an actual parent.
- */
-// TODO(task-hierarchy): Remove when tasks can nest >2 or when single tasks can handle their
-//                       own lifecycles.
-public class TaskTile extends ActivityStack {
-    private static final String TAG = "TaskTile";
-    final ArrayList<WindowContainer> mChildren = new ArrayList<>();
-
-    private static ActivityInfo createEmptyActivityInfo() {
-        ActivityInfo info = new ActivityInfo();
-        info.applicationInfo = new ApplicationInfo();
-        return info;
-    }
-
-    TaskTile(ActivityTaskManagerService atmService, int id, int windowingMode) {
-        super(atmService, id, new Intent() /*intent*/,  null /*affinityIntent*/, null /*affinity*/,
-                null /*rootAffinity*/, null /*realActivity*/, null /*origActivity*/,
-                false /*rootWasReset*/, false /*autoRemoveRecents*/, false /*askedCompatMode*/,
-                0 /*userId*/, 0 /*effectiveUid*/, null /*lastDescription*/,
-                System.currentTimeMillis(), true /*neverRelinquishIdentity*/,
-                new ActivityManager.TaskDescription(), id, INVALID_TASK_ID, INVALID_TASK_ID,
-                0 /*taskAffiliationColor*/, 0 /*callingUid*/, "" /*callingPackage*/,
-                null /*callingFeatureId*/, RESIZE_MODE_RESIZEABLE,
-                false /*supportsPictureInPicture*/, false /*_realActivitySuspended*/,
-                false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE,
-                createEmptyActivityInfo(), null /*voiceSession*/, null /*voiceInteractor*/,
-                null /*stack*/);
-        getRequestedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
-    }
-
-    @Override
-    void onDisplayChanged(DisplayContent dc) {
-        mDisplayContent = null;
-        if (dc != null) {
-            dc.getPendingTransaction().merge(getPendingTransaction());
-        }
-        mDisplayContent = dc;
-        // Virtual parent, so don't notify children.
-    }
-
-    @Override
-    TaskTile asTile() {
-        return this;
-    }
-
-    @Override
-    protected void addChild(WindowContainer child, Comparator<WindowContainer> comparator) {
-        throw new RuntimeException("Improper use of addChild() on Tile");
-    }
-
-    @Override
-    void addChild(WindowContainer child, int index) {
-        mChildren.add(child);
-        if (child instanceof ActivityStack) {
-            ((ActivityStack) child).setTile(this);
-        }
-        mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
-                this, false /* force */);
-    }
-
-    @Override
-    void removeChild(WindowContainer child) {
-        if (child instanceof ActivityStack) {
-            ((ActivityStack) child).setTile(null);
-        }
-        mChildren.remove(child);
-        mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
-                this, false /* force */);
-    }
-
-    void removeAllChildren() {
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowContainer child = mChildren.get(i);
-            if (child instanceof ActivityStack) {
-                ((ActivityStack) child).setTile(null);
-            }
-        }
-        mChildren.clear();
-        mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
-                this, false /* force */);
-    }
-
-    @Override
-    protected int getChildCount() {
-        // Currently 0 as this isn't a proper hierarchy member yet.
-        return 0;
-    }
-
-    @Override
-    public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) {
-        Configuration c = new Configuration(getRequestedOverrideConfiguration());
-        c.windowConfiguration.setWindowingMode(windowingMode);
-        onRequestedOverrideConfigurationChanged(c);
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newParentConfig) {
-        super.onConfigurationChanged(newParentConfig);
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowContainer child = mChildren.get(i);
-            child.onConfigurationChanged(child.getParent().getConfiguration());
-        }
-    }
-
-    void forAllTileActivities(Consumer<ActivityRecord> callback) {
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            mChildren.get(i).forAllActivities(callback, true /* traverseTopToBottom */);
-        }
-    }
-
-    /**
-     * Until this can be part of the hierarchy, the Stack level can use this utility during
-     * resolveOverrideConfig to simulate inheritance.
-     */
-    void updateResolvedConfig(Configuration inOutResolvedConfig) {
-        Rect resolveBounds = inOutResolvedConfig.windowConfiguration.getBounds();
-        if (resolveBounds.isEmpty()) {
-            resolveBounds.set(getRequestedOverrideBounds());
-        }
-        int stackMode = inOutResolvedConfig.windowConfiguration.getWindowingMode();
-        if (stackMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED
-                || stackMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN) {
-            // Also replace FULLSCREEN because we interpret FULLSCREEN as "fill parent"
-            inOutResolvedConfig.windowConfiguration.setWindowingMode(
-                    getRequestedOverrideWindowingMode());
-        }
-        if (inOutResolvedConfig.smallestScreenWidthDp
-                == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
-            inOutResolvedConfig.smallestScreenWidthDp =
-                    getRequestedOverrideConfiguration().smallestScreenWidthDp;
-        }
-        if (inOutResolvedConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
-            inOutResolvedConfig.screenWidthDp = getRequestedOverrideConfiguration().screenWidthDp;
-        }
-        if (inOutResolvedConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
-            inOutResolvedConfig.screenHeightDp = getRequestedOverrideConfiguration().screenHeightDp;
-        }
-        Rect resolveAppBounds = inOutResolvedConfig.windowConfiguration.getAppBounds();
-        if (resolveAppBounds == null || resolveAppBounds.isEmpty()) {
-            inOutResolvedConfig.windowConfiguration.setAppBounds(
-                    getRequestedOverrideConfiguration().windowConfiguration.getAppBounds());
-        }
-    }
-
-    @Override
-    void fillTaskInfo(TaskInfo info) {
-        super.fillTaskInfo(info);
-        WindowContainer top = null;
-        // Check mChildren.isEmpty directly because hasChild() -> getChildCount() always returns 0
-        if (!mChildren.isEmpty()) {
-            // Find the top-most root task which is a virtual child of this Tile. Because this is a
-            // virtual parent, the mChildren order here isn't changed during hierarchy operations.
-            WindowContainer parent = mChildren.get(0).getParent();
-            for (int i = parent.getChildCount() - 1; i >= 0; --i) {
-                if (mChildren.contains(parent.getChildAt(i))) {
-                    top = parent.getChildAt(i);
-                    break;
-                }
-            }
-        }
-        final Task topTask = top == null ? null : top.getTopMostTask();
-        boolean isResizable = topTask == null || topTask.isResizeable();
-        info.resizeMode = isResizable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE;
-        info.topActivityType = top == null ? ACTIVITY_TYPE_UNDEFINED : top.getActivityType();
-    }
-
-    @Override
-    void removeImmediately() {
-        removeAllChildren();
-        super.removeImmediately();
-    }
-
-    @Override
-    void taskOrganizerDied() {
-        super.taskOrganizerDied();
-        removeImmediately();
-    }
-
-    static TaskTile forToken(IBinder token) {
-        try {
-            return (TaskTile) ((RemoteToken) token).getContainer();
-        } catch (ClassCastException e) {
-            Slog.w(TAG, "Bad tile token: " + token, e);
-            return null;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 57d0a33..29a2e18 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -142,11 +142,13 @@
             mFindResults.setUseTopWallpaperAsTarget(true);
         }
 
-        final boolean keyguardGoingAwayWithWallpaper = (w.mActivityRecord != null
-                && w.mActivityRecord.isAnimating(TRANSITION | PARENTS)
-                && AppTransition.isKeyguardGoingAwayTransit(w.mActivityRecord.getTransit())
-                && (w.mActivityRecord.getTransitFlags()
-                        & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0);
+        final WindowContainer animatingContainer = w.mActivityRecord != null
+                ? w.mActivityRecord.getAnimatingContainer() : null;
+        final boolean keyguardGoingAwayWithWallpaper = (animatingContainer != null
+                && animatingContainer.isAnimating(TRANSITION | PARENTS)
+                && AppTransition.isKeyguardGoingAwayTransit(animatingContainer.mTransit)
+                && (animatingContainer.mTransitFlags
+                & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0);
 
         boolean needsShowWhenLockedWallpaper = false;
         if ((w.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0
@@ -166,8 +168,6 @@
 
         final RecentsAnimationController recentsAnimationController =
                 mService.getRecentsAnimationController();
-        final WindowContainer animatingContainer =
-                w.mActivityRecord != null ? w.mActivityRecord.getAnimatingContainer() : null;
         final boolean animationWallpaper = animatingContainer != null
                 && animatingContainer.getAnimation() != null
                 && animatingContainer.getAnimation().getShowWallpaper();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 23ba528..b1f22f8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -58,7 +58,6 @@
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
@@ -1482,12 +1481,6 @@
                             + "%s.  Aborting.", attrs.token);
                     return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                 }
-            } else if (rootType == TYPE_DREAM) {
-                if (token.windowType != TYPE_DREAM) {
-                    ProtoLog.w(WM_ERROR, "Attempted to add Dream window with bad token "
-                            + "%s.  Aborting.", attrs.token);
-                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
-                }
             } else if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
                 if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {
                     ProtoLog.w(WM_ERROR,
@@ -1723,11 +1716,6 @@
                     + "%s.  Aborting.", tokenForLog);
             return false;
         }
-        if (rootType == TYPE_DREAM) {
-            ProtoLog.w(WM_ERROR, "Attempted to add Dream window with unknown token "
-                    + "%s.  Aborting.", tokenForLog);
-            return false;
-        }
         if (rootType == TYPE_QS_DIALOG) {
             ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with unknown token "
                     + "%s.  Aborting.", tokenForLog);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index e416e80..5f21e17 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -159,17 +159,7 @@
                                 false /* preserveWindow */);
                         try {
                             for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
-                                final WindowContainer wc = haveConfigChanges.valueAt(i);
-                                final Task task = wc.asTask();
-                                final TaskTile tile = task != null ? task.asTile() : null;
-                                if (tile != null) {
-                                    // Special case for tile. Can't override normal forAllActivities
-                                    // because it generates duplicate calls and messes up existing
-                                    // code-paths.
-                                    tile.forAllTileActivities(f);
-                                } else {
-                                    wc.forAllActivities(f);
-                                }
+                                haveConfigChanges.valueAt(i).forAllActivities(f);
                             }
                         } finally {
                             f.recycle();
@@ -223,51 +213,65 @@
 
     private int sanitizeAndApplyHierarchyOp(WindowContainer container,
             WindowContainerTransaction.HierarchyOp hop) {
-        if (!(container instanceof Task)) {
+        final Task task = container.asTask();
+        if (task == null) {
             throw new IllegalArgumentException("Invalid container in hierarchy op");
         }
-        if (container.getDisplayContent() == null) {
-            Slog.w(TAG, "Container is no longer attached: " + container);
+        final DisplayContent dc = task.getDisplayContent();
+        if (dc == null) {
+            Slog.w(TAG, "Container is no longer attached: " + task);
             return 0;
         }
+        final ActivityStack as = (ActivityStack) task;
+
         if (hop.isReparent()) {
-            // special case for tiles since they are "virtual" parents
-            if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
-                ActivityStack as = (ActivityStack) container;
-                TaskTile newParent = hop.getNewParent() == null ? null
-                        : (TaskTile) WindowContainer.fromBinder(hop.getNewParent());
-                if (as.getTile() != newParent) {
-                    if (as.getTile() != null) {
-                        as.getTile().removeChild(as);
+            final boolean isNonOrganizedRootableTask =
+                    (task.isRootTask() && !task.mCreatedByOrganizer)
+                            || task.getParent().asTask().mCreatedByOrganizer;
+            if (isNonOrganizedRootableTask) {
+                Task newParent = hop.getNewParent() == null ? null
+                        : WindowContainer.fromBinder(hop.getNewParent()).asTask();
+                if (task.getParent() != newParent) {
+                    if (newParent == null) {
+                        // Re-parent task to display as a root task.
+                        dc.moveStackToDisplay(as, hop.getToTop());
+                    } else if (newParent.inMultiWindowMode() && !task.isResizeable()
+                            && task.isLeafTask()) {
+                        Slog.w(TAG, "Can't support task that doesn't support multi-window mode in"
+                                + " multi-window mode... newParent=" + newParent + " task=" + task);
+                        return 0;
+                    } else {
+                        // Clear the window crop on root task since it may not be updated after
+                        // reparent (no longer be a root task)
+                        task.getSurfaceControl().setWindowCrop(null);
+                        task.reparent((ActivityStack) newParent,
+                                hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
+                                false /*moveParents*/, "sanitizeAndApplyHierarchyOp");
                     }
-                    if (newParent != null) {
-                        if (!as.affectedBySplitScreenResize()) {
-                            return 0;
-                        }
-                        newParent.addChild(as, POSITION_TOP);
-                    }
-                }
-                if (hop.getToTop()) {
-                    as.getDisplay().positionStackAtTop(as, false /* includingParents */);
                 } else {
-                    as.getDisplay().positionStackAtBottom(as);
+                    final ActivityStack rootTask =
+                            (ActivityStack) (newParent != null ? newParent : task.getRootTask());
+                    if (hop.getToTop()) {
+                        as.getDisplay().positionStackAtTop(rootTask, false /* includingParents */);
+                    } else {
+                        as.getDisplay().positionStackAtBottom(rootTask);
+                    }
                 }
-            } else if (container instanceof Task) {
+            } else {
                 throw new RuntimeException("Reparenting leaf Tasks is not supported now.");
             }
         } else {
             // Ugh, of course ActivityStack has its own special reorder logic...
-            if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
-                ActivityStack as = (ActivityStack) container;
+            if (task.isRootTask()) {
                 if (hop.getToTop()) {
-                    as.getDisplay().positionStackAtTop(as, false /* includingParents */);
+                    dc.positionStackAtTop(as, false /* includingParents */);
                 } else {
-                    as.getDisplay().positionStackAtBottom(as);
+                    dc.positionStackAtBottom(as);
                 }
             } else {
-                container.getParent().positionChildAt(
+                task.getParent().positionChildAt(
                         hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
-                        container, false /* includingParents */);
+                        task, false /* includingParents */);
             }
         }
         return TRANSACT_EFFECTS_LIFECYCLE;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 161152b..b448740 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -20,6 +20,7 @@
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.OP_NONE;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.os.PowerManager.DRAW_WAKE_LOCK;
@@ -131,6 +132,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
 import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
@@ -138,7 +140,6 @@
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
-import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT;
 import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
@@ -608,6 +609,11 @@
     boolean mSeamlesslyRotated = false;
 
     /**
+     * Indicates if this window is behind IME. Only windows behind IME can get insets from IME.
+     */
+    boolean mBehindIme = false;
+
+    /**
      * Surface insets from the previous call to relayout(), used to track
      * if we are changing the Surface insets.
      */
@@ -1689,6 +1695,11 @@
         return !isParentWindowHidden() || isAnimating(TRANSITION | PARENTS);
     }
 
+    boolean isDreamWindow() {
+        return mActivityRecord != null
+               && mActivityRecord.getActivityType() == ACTIVITY_TYPE_DREAM;
+    }
+
     /**
      * Whether this window's drawn state might affect the drawn states of the app token.
      *
@@ -2270,9 +2281,9 @@
             return false;
         }
 
-        if (PixelFormat.formatHasAlpha(mAttrs.format) && mAttrs.alpha == 0) {
-            // Support legacy use cases where completely transparent windows can still be ime target
-            // with FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set.
+        if (PixelFormat.formatHasAlpha(mAttrs.format)) {
+            // Support legacy use cases where transparent windows can still be ime target with
+            // FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set.
             // Certain apps listen for IME insets using transparent windows and ADJUST_NOTHING to
             // manually synchronize app content to IME animation b/144619551.
             // TODO(b/145812508): remove this once new focus management is complete b/141738570
@@ -3338,7 +3349,7 @@
         }
 
         final ActivityStack stack = task.getStack();
-        if (stack == null) {
+        if (stack == null || stack.mCreatedByOrganizer) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 0cfdebc..6b9fbcb 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -157,9 +157,7 @@
 
         mInLayout = true;
 
-        boolean recoveringMemory = false;
         if (!mService.mForceRemoves.isEmpty()) {
-            recoveringMemory = true;
             // Wait a little bit for things to settle down, and off we go.
             while (!mService.mForceRemoves.isEmpty()) {
                 final WindowState ws = mService.mForceRemoves.remove(0);
@@ -177,7 +175,7 @@
         }
 
         try {
-            mService.mRoot.performSurfacePlacement(recoveringMemory);
+            mService.mRoot.performSurfacePlacement();
 
             mInLayout = false;
 
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 850c362..3c2b6ec 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -480,26 +480,42 @@
     }
 
     /**
-     * Clears the transformation and continue updating the orientation change of display. Only the
-     * state owner can clear the transform state.
+     * Finishes the transform and continue updating the orientation change of display. Only the
+     * state owner can finish the transform state.
      */
-    void clearFixedRotationTransform() {
-        final FixedRotationTransformState state = mFixedRotationTransformState;
-        if (state == null || state.mOwner != this) {
+    void finishFixedRotationTransform() {
+        if (mFixedRotationTransformState == null || mFixedRotationTransformState.mOwner != this) {
             return;
         }
-        state.resetTransform();
-        // Clear the flag so if the display will be updated to the same orientation, the transform
-        // won't take effect. The state is cleared at the end, because it is used to indicate that
-        // other windows can use seamless rotation when applying rotation to display.
-        state.mIsTransforming = false;
         final boolean changed =
                 mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(this);
-        // If it is not the launching app or the display is not rotated, make sure the merged
-        // override configuration is restored from parent.
+        // If it is not the launching app or the display is not rotated, make sure the transform is
+        // cleared and the configuration is restored from parent.
         if (!changed) {
-            onMergedOverrideConfigurationChanged();
+            clearFixedRotationTransform(null /* applyDisplayRotation */);
+            onConfigurationChanged(getParent().getConfiguration());
         }
+    }
+
+    /**
+     * Clears the transform and apply display rotation if the action is given. The caller needs to
+     * refresh the configuration of this container after this method call.
+     */
+    void clearFixedRotationTransform(Runnable applyDisplayRotation) {
+        final FixedRotationTransformState state = mFixedRotationTransformState;
+        if (state == null) {
+            return;
+        }
+
+        state.resetTransform();
+        // Clear the flag so if the display will be updated to the same orientation, the transform
+        // won't take effect.
+        state.mIsTransforming = false;
+        if (applyDisplayRotation != null) {
+            applyDisplayRotation.run();
+        }
+        // The state is cleared at the end, because it is used to indicate that other windows can
+        // use seamless rotation when applying rotation to display.
         for (int i = state.mAssociatedTokens.size() - 1; i >= 0; i--) {
             state.mAssociatedTokens.get(i).mFixedRotationTransformState = null;
         }
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 822f383..e4061b4 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -2068,8 +2068,8 @@
     class_gnssClock = (jclass) env->NewGlobalRef(gnssClockClass);
     method_gnssClockCtor = env->GetMethodID(class_gnssClock, "<init>", "()V");
 
-    jclass gnssConfiguration_halInterfaceVersionClass =
-            env->FindClass("com/android/server/location/GnssConfiguration$HalInterfaceVersion");
+    jclass gnssConfiguration_halInterfaceVersionClass = env->FindClass(
+            "com/android/server/location/gnss/GnssConfiguration$HalInterfaceVersion");
     class_gnssConfiguration_halInterfaceVersion =
             (jclass) env->NewGlobalRef(gnssConfiguration_halInterfaceVersionClass);
     method_halInterfaceVersionCtor =
@@ -3738,39 +3738,29 @@
 };
 
 static const JNINativeMethod sConfigurationMethods[] = {
-     /* name, signature, funcPtr */
-    {"native_get_gnss_configuration_version",
-            "()Lcom/android/server/location/GnssConfiguration$HalInterfaceVersion;",
-            reinterpret_cast<void *>(
-                    android_location_GnssConfiguration_get_gnss_configuration_version)},
-    {"native_set_supl_es",
-            "(I)Z",
-            reinterpret_cast<void *>(android_location_GnssConfiguration_set_supl_es)},
-    {"native_set_supl_version",
-            "(I)Z",
-            reinterpret_cast<void *>(android_location_GnssConfiguration_set_supl_version)},
-    {"native_set_supl_mode",
-            "(I)Z",
-            reinterpret_cast<void *>(android_location_GnssConfiguration_set_supl_mode)},
-    {"native_set_lpp_profile",
-            "(I)Z",
-            reinterpret_cast<void *>(android_location_GnssConfiguration_set_lpp_profile)},
-    {"native_set_gnss_pos_protocol_select",
-            "(I)Z",
-            reinterpret_cast<void *>(
-                    android_location_GnssConfiguration_set_gnss_pos_protocol_select)},
-    {"native_set_gps_lock",
-            "(I)Z",
-            reinterpret_cast<void *>(android_location_GnssConfiguration_set_gps_lock)},
-    {"native_set_emergency_supl_pdn",
-            "(I)Z",
-            reinterpret_cast<void *>(android_location_GnssConfiguration_set_emergency_supl_pdn)},
-    {"native_set_satellite_blacklist",
-            "([I[I)Z",
-            reinterpret_cast<void *>(android_location_GnssConfiguration_set_satellite_blacklist)},
-    {"native_set_es_extension_sec",
-            "(I)Z",
-            reinterpret_cast<void *>(android_location_GnssConfiguration_set_es_extension_sec)},
+        /* name, signature, funcPtr */
+        {"native_get_gnss_configuration_version",
+         "()Lcom/android/server/location/gnss/GnssConfiguration$HalInterfaceVersion;",
+         reinterpret_cast<void*>(
+                 android_location_GnssConfiguration_get_gnss_configuration_version)},
+        {"native_set_supl_es", "(I)Z",
+         reinterpret_cast<void*>(android_location_GnssConfiguration_set_supl_es)},
+        {"native_set_supl_version", "(I)Z",
+         reinterpret_cast<void*>(android_location_GnssConfiguration_set_supl_version)},
+        {"native_set_supl_mode", "(I)Z",
+         reinterpret_cast<void*>(android_location_GnssConfiguration_set_supl_mode)},
+        {"native_set_lpp_profile", "(I)Z",
+         reinterpret_cast<void*>(android_location_GnssConfiguration_set_lpp_profile)},
+        {"native_set_gnss_pos_protocol_select", "(I)Z",
+         reinterpret_cast<void*>(android_location_GnssConfiguration_set_gnss_pos_protocol_select)},
+        {"native_set_gps_lock", "(I)Z",
+         reinterpret_cast<void*>(android_location_GnssConfiguration_set_gps_lock)},
+        {"native_set_emergency_supl_pdn", "(I)Z",
+         reinterpret_cast<void*>(android_location_GnssConfiguration_set_emergency_supl_pdn)},
+        {"native_set_satellite_blacklist", "([I[I)Z",
+         reinterpret_cast<void*>(android_location_GnssConfiguration_set_satellite_blacklist)},
+        {"native_set_es_extension_sec", "(I)Z",
+         reinterpret_cast<void*>(android_location_GnssConfiguration_set_es_extension_sec)},
 };
 
 static const JNINativeMethod sVisibilityControlMethods[] = {
@@ -3782,53 +3772,27 @@
 };
 
 int register_android_server_location_GnssLocationProvider(JNIEnv* env) {
-    jniRegisterNativeMethods(env, "com/android/server/location/GnssAntennaInfoProvider",
+    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssAntennaInfoProvider",
                              sAntennaInfoMethods, NELEM(sAntennaInfoMethods));
-    jniRegisterNativeMethods(
-            env,
-            "com/android/server/location/GnssBatchingProvider",
-            sMethodsBatching,
-            NELEM(sMethodsBatching));
-    jniRegisterNativeMethods(
-            env,
-            "com/android/server/location/GnssGeofenceProvider",
-            sGeofenceMethods,
-            NELEM(sGeofenceMethods));
-    jniRegisterNativeMethods(
-            env,
-            "com/android/server/location/GnssMeasurementsProvider",
-            sMeasurementMethods,
-            NELEM(sMeasurementMethods));
-    jniRegisterNativeMethods(
-            env,
-            "com/android/server/location/GnssMeasurementCorrectionsProvider",
-            sMeasurementCorrectionsMethods,
-            NELEM(sMeasurementCorrectionsMethods));
-    jniRegisterNativeMethods(
-            env,
-            "com/android/server/location/GnssNavigationMessageProvider",
-            sNavigationMessageMethods,
-            NELEM(sNavigationMessageMethods));
-    jniRegisterNativeMethods(
-            env,
-            "com/android/server/location/GnssNetworkConnectivityHandler",
-            sNetworkConnectivityMethods,
-            NELEM(sNetworkConnectivityMethods));
-    jniRegisterNativeMethods(
-            env,
-            "com/android/server/location/GnssConfiguration",
-            sConfigurationMethods,
-            NELEM(sConfigurationMethods));
-    jniRegisterNativeMethods(
-            env,
-            "com/android/server/location/GnssVisibilityControl",
-            sVisibilityControlMethods,
-            NELEM(sVisibilityControlMethods));
-    return jniRegisterNativeMethods(
-            env,
-            "com/android/server/location/GnssLocationProvider",
-            sMethods,
-            NELEM(sMethods));
+    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssBatchingProvider",
+                             sMethodsBatching, NELEM(sMethodsBatching));
+    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssGeofenceProvider",
+                             sGeofenceMethods, NELEM(sGeofenceMethods));
+    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssMeasurementsProvider",
+                             sMeasurementMethods, NELEM(sMeasurementMethods));
+    jniRegisterNativeMethods(env,
+                             "com/android/server/location/gnss/GnssMeasurementCorrectionsProvider",
+                             sMeasurementCorrectionsMethods, NELEM(sMeasurementCorrectionsMethods));
+    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssNavigationMessageProvider",
+                             sNavigationMessageMethods, NELEM(sNavigationMessageMethods));
+    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssNetworkConnectivityHandler",
+                             sNetworkConnectivityMethods, NELEM(sNetworkConnectivityMethods));
+    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssConfiguration",
+                             sConfigurationMethods, NELEM(sConfigurationMethods));
+    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssVisibilityControl",
+                             sVisibilityControlMethods, NELEM(sVisibilityControlMethods));
+    return jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssLocationProvider",
+                                    sMethods, NELEM(sMethods));
 }
 
 } /* namespace android */
diff --git a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
index 725036c..e9a5e58 100644
--- a/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
+++ b/services/core/jni/com_android_server_pm_PackageManagerShellCommandDataLoader.cpp
@@ -227,56 +227,40 @@
     return result;
 }
 
+enum MetadataMode : int8_t {
+    STDIN = 0,
+    LOCAL_FILE = 1,
+    DATA_ONLY_STREAMING = 2,
+    STREAMING = 3,
+};
+
 struct InputDesc {
     unique_fd fd;
     IncFsSize size;
     IncFsBlockKind kind = INCFS_BLOCK_KIND_DATA;
     bool waitOnEof = false;
     bool streaming = false;
+    MetadataMode mode = STDIN;
 };
 using InputDescs = std::vector<InputDesc>;
 
-static inline InputDescs openInputs(JNIEnv* env, const JniIds& jni, jobject shellCommand,
-                                    IncFsSize size, IncFsSpan metadata) {
+template <class T>
+std::optional<T> read(IncFsSpan& data) {
+    if (data.size < (int32_t)sizeof(T)) {
+        return {};
+    }
+    T res;
+    memcpy(&res, data.data, sizeof(res));
+    data.data += sizeof(res);
+    data.size -= sizeof(res);
+    return res;
+}
+
+static inline InputDescs openLocalFile(JNIEnv* env, const JniIds& jni, jobject shellCommand,
+                                       IncFsSize size, const std::string& filePath) {
     InputDescs result;
     result.reserve(2);
 
-    if (metadata.size == 0 || *metadata.data == '-') {
-        // stdin
-        auto fd = convertPfdToFdAndDup(
-                env, jni,
-                env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
-                                            jni.pmscdGetStdInPFD, shellCommand));
-        if (fd.ok()) {
-            result.push_back(InputDesc{
-                    .fd = std::move(fd),
-                    .size = size,
-                    .waitOnEof = true,
-            });
-        }
-        return result;
-    }
-    if (*metadata.data == '+') {
-        // verity tree from stdin, rest is streaming
-        auto fd = convertPfdToFdAndDup(
-                env, jni,
-                env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
-                                            jni.pmscdGetStdInPFD, shellCommand));
-        if (fd.ok()) {
-            auto treeSize = verityTreeSizeForFile(size);
-            result.push_back(InputDesc{
-                    .fd = std::move(fd),
-                    .size = treeSize,
-                    .kind = INCFS_BLOCK_KIND_HASH,
-                    .waitOnEof = true,
-                    .streaming = true,
-            });
-        }
-        return result;
-    }
-
-    // local file and possibly signature
-    const std::string filePath(metadata.data, metadata.size);
     const std::string idsigPath = filePath + ".idsig";
 
     auto idsigFd = convertPfdToFdAndDup(
@@ -314,6 +298,59 @@
     return result;
 }
 
+static inline InputDescs openInputs(JNIEnv* env, const JniIds& jni, jobject shellCommand,
+                                    IncFsSize size, IncFsSpan metadata) {
+    auto mode = read<int8_t>(metadata).value_or(STDIN);
+    if (mode == LOCAL_FILE) {
+        // local file and possibly signature
+        return openLocalFile(env, jni, shellCommand, size,
+                             std::string(metadata.data, metadata.size));
+    }
+
+    auto fd = convertPfdToFdAndDup(
+            env, jni,
+            env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
+                                        jni.pmscdGetStdInPFD, shellCommand));
+    if (!fd.ok()) {
+        return {};
+    }
+
+    InputDescs result;
+    switch (mode) {
+        case STDIN: {
+            result.push_back(InputDesc{
+                    .fd = std::move(fd),
+                    .size = size,
+                    .waitOnEof = true,
+            });
+            break;
+        }
+        case DATA_ONLY_STREAMING: {
+            // verity tree from stdin, rest is streaming
+            auto treeSize = verityTreeSizeForFile(size);
+            result.push_back(InputDesc{
+                    .fd = std::move(fd),
+                    .size = treeSize,
+                    .kind = INCFS_BLOCK_KIND_HASH,
+                    .waitOnEof = true,
+                    .streaming = true,
+                    .mode = DATA_ONLY_STREAMING,
+            });
+            break;
+        }
+        case STREAMING: {
+            result.push_back(InputDesc{
+                    .fd = std::move(fd),
+                    .size = 0,
+                    .streaming = true,
+                    .mode = STREAMING,
+            });
+            break;
+        }
+    }
+    return result;
+}
+
 static inline JNIEnv* GetJNIEnvironment(JavaVM* vm) {
     JNIEnv* env;
     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
@@ -390,6 +427,7 @@
         blocks.reserve(BLOCKS_COUNT);
 
         unique_fd streamingFd;
+        MetadataMode streamingMode;
         for (auto&& file : addedFiles) {
             auto inputs = openInputs(env, jni, shellCommand, file.size, file.metadata);
             if (inputs.empty()) {
@@ -411,6 +449,7 @@
             for (auto&& input : inputs) {
                 if (input.streaming && !streamingFd.ok()) {
                     streamingFd.reset(dup(input.fd));
+                    streamingMode = input.mode;
                 }
                 if (!copyToIncFs(incfsFd, input.size, input.kind, input.fd, input.waitOnEof,
                                  &buffer, &blocks)) {
@@ -425,7 +464,7 @@
 
         if (streamingFd.ok()) {
             ALOGE("onPrepareImage: done, proceeding to streaming.");
-            return initStreaming(std::move(streamingFd));
+            return initStreaming(std::move(streamingFd), streamingMode);
         }
 
         ALOGE("onPrepareImage: done.");
@@ -564,7 +603,7 @@
     }
 
     // Streaming.
-    bool initStreaming(unique_fd inout) {
+    bool initStreaming(unique_fd inout, MetadataMode mode) {
         mEventFd.reset(eventfd(0, EFD_CLOEXEC));
         if (mEventFd < 0) {
             ALOGE("Failed to create eventfd.");
@@ -591,8 +630,8 @@
             }
         }
 
-        mReceiverThread =
-                std::thread([this, io = std::move(inout)]() mutable { receiver(std::move(io)); });
+        mReceiverThread = std::thread(
+                [this, io = std::move(inout), mode]() mutable { receiver(std::move(io), mode); });
         ALOGI("Started streaming...");
         return true;
     }
@@ -624,7 +663,7 @@
         }
     }
 
-    void receiver(unique_fd inout) {
+    void receiver(unique_fd inout, MetadataMode mode) {
         std::vector<uint8_t> data;
         std::vector<IncFsDataBlock> instructions;
         std::unordered_map<FileIdx, unique_fd> writeFds;
@@ -667,7 +706,7 @@
                     break;
                 }
                 const FileIdx fileIdx = header.fileIdx;
-                const android::dataloader::FileId fileId = convertFileIndexToFileId(fileIdx);
+                const android::dataloader::FileId fileId = convertFileIndexToFileId(mode, fileIdx);
                 if (!android::incfs::isValidFileId(fileId)) {
                     ALOGE("Unknown data destination for file ID %d. "
                           "Ignore.",
@@ -679,7 +718,7 @@
                 if (writeFd < 0) {
                     writeFd.reset(this->mIfs->openWrite(fileId));
                     if (writeFd < 0) {
-                        ALOGE("Failed to open file %d for writing (%d). Aboring.", header.fileIdx,
+                        ALOGE("Failed to open file %d for writing (%d). Aborting.", header.fileIdx,
                               -writeFd);
                         break;
                     }
@@ -716,9 +755,11 @@
     }
 
     FileIdx convertFileIdToFileIndex(android::dataloader::FileId fileId) {
-        // FileId is a string in format '+FileIdx\0'.
+        // FileId has format '\2FileIdx'.
         const char* meta = (const char*)&fileId;
-        if (*meta != '+') {
+
+        int8_t mode = *meta;
+        if (mode != DATA_ONLY_STREAMING && mode != STREAMING) {
             return -1;
         }
 
@@ -732,10 +773,10 @@
         return FileIdx(fileIdx);
     }
 
-    android::dataloader::FileId convertFileIndexToFileId(FileIdx fileIdx) {
+    android::dataloader::FileId convertFileIndexToFileId(MetadataMode mode, FileIdx fileIdx) {
         IncFsFileId fileId = {};
         char* meta = (char*)&fileId;
-        *meta = '+';
+        *meta = mode;
         if (auto [p, ec] = std::to_chars(meta + 1, meta + sizeof(fileId), fileIdx);
             ec != std::errc()) {
             return {};
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 023a1e8..b0eb148 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4434,6 +4434,11 @@
                     clearDeviceOwnerLocked(getDeviceOwnerAdminLocked(), userHandle);
                 }
                 if (isProfileOwner(adminReceiver, userHandle)) {
+                    if (isProfileOwnerOfOrganizationOwnedDevice(userHandle)) {
+                        mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
+                                false,
+                                UserHandle.of(getProfileParentId(userHandle)));
+                    }
                     final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver,
                             userHandle, /* parent */ false);
                     clearProfileOwnerLocked(admin, userHandle);
@@ -7166,7 +7171,9 @@
         ActiveAdmin admin;
         synchronized (getLockObject()) {
             if (who == null) {
-                if ((frpManagementAgentUid != mInjector.binderGetCallingUid())) {
+                if ((frpManagementAgentUid != mInjector.binderGetCallingUid())
+                        && (mContext.checkCallingPermission(permission.MASTER_CLEAR)
+                        != PackageManager.PERMISSION_GRANTED)) {
                     throw new SecurityException(
                             "Must be called by the FRP management agent on device");
                 }
@@ -9177,8 +9184,6 @@
             return true;
         }
 
-        Log.w(LOG_TAG, String.format("Package %s (uid=%d, pid=%d) cannot access Device IDs",
-                    packageName, uid, pid));
         return false;
     }
 
@@ -13070,26 +13075,25 @@
             final boolean addingProfileRestricted = mUserManager.hasUserRestriction(
                     UserManager.DISALLOW_ADD_MANAGED_PROFILE, callingUserHandle);
 
-            UserInfo parentUser = mUserManager.getProfileParent(callingUserId);
-            final boolean addingProfileRestrictedOnParent = (parentUser != null)
-                    && mUserManager.hasUserRestriction(
-                            UserManager.DISALLOW_ADD_MANAGED_PROFILE,
-                            UserHandle.of(parentUser.id));
+            if (mUserManager.getUserInfo(callingUserId).isProfile()) {
+                Slog.i(LOG_TAG,
+                        String.format("Calling user %d is a profile, cannot add another.",
+                                callingUserId));
+                // The check is called from inside a managed profile. A managed profile cannot
+                // be provisioned from within another managed profile.
+                return CODE_CANNOT_ADD_MANAGED_PROFILE;
+            }
 
-            Slog.i(LOG_TAG, String.format(
-                    "When checking for managed profile provisioning: Has device owner? %b, adding"
-                            + " profile restricted? %b, adding profile restricted on parent? %b",
-                    hasDeviceOwner, addingProfileRestricted, addingProfileRestrictedOnParent));
-
-            // If there's a device owner, the restriction on adding a managed profile must be set
-            // somewhere.
-            if (hasDeviceOwner && !addingProfileRestricted && !addingProfileRestrictedOnParent) {
+            // If there's a device owner, the restriction on adding a managed profile must be set.
+            if (hasDeviceOwner && !addingProfileRestricted) {
                 Slog.wtf(LOG_TAG, "Has a device owner but no restriction on adding a profile.");
             }
 
-            // Do not allow adding a managed profile if there's a restriction, either on the current
-            // user or its parent user.
-            if (addingProfileRestricted || addingProfileRestrictedOnParent) {
+            // Do not allow adding a managed profile if there's a restriction.
+            if (addingProfileRestricted) {
+                Slog.i(LOG_TAG, String.format(
+                        "Adding a profile is restricted: User %s Has device owner? %b",
+                        callingUserHandle, hasDeviceOwner));
                 return CODE_CANNOT_ADD_MANAGED_PROFILE;
             }
             // If there's a restriction on removing the managed profile then we have to take it
@@ -13098,6 +13102,8 @@
                     !mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE,
                     callingUserHandle);
             if (!mUserManager.canAddMoreManagedProfiles(callingUserId, canRemoveProfile)) {
+                Slog.i(LOG_TAG, String.format(
+                        "Cannot add more profiles: Can remove current? %b", canRemoveProfile));
                 return CODE_CANNOT_ADD_MANAGED_PROFILE;
             }
         } finally {
@@ -15734,9 +15740,11 @@
             }
         }
 
+        final int suspendedState = suspended
+                ? PERSONAL_APPS_SUSPENDED_EXPLICITLY
+                : PERSONAL_APPS_NOT_SUSPENDED;
         mInjector.binderWithCleanCallingIdentity(
-                () -> applyPersonalAppsSuspension(
-                        callingUserId, PERSONAL_APPS_SUSPENDED_EXPLICITLY));
+                () -> applyPersonalAppsSuspension(callingUserId, suspendedState));
 
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_PERSONAL_APPS_SUSPENDED)
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index ed85b93..ae27c7a 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -286,7 +286,6 @@
             dprintf(fd, "\t\t\tpackageName: %s\n", params.packageName.c_str());
             dprintf(fd, "\t\t\tclassName: %s\n", params.className.c_str());
             dprintf(fd, "\t\t\targuments: %s\n", params.arguments.c_str());
-            dprintf(fd, "\t\t\tdynamicArgs: %d\n", int(params.dynamicArgs.size()));
         }
         dprintf(fd, "\t\tstorages (%d):\n", int(mnt.storages.size()));
         for (auto&& [storageId, storage] : mnt.storages) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b019e9d..c631eeb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -144,7 +144,6 @@
 import com.android.server.recoverysystem.RecoverySystemService;
 import com.android.server.restrictions.RestrictionsManagerService;
 import com.android.server.role.RoleManagerService;
-import com.android.server.rollback.RollbackManagerService;
 import com.android.server.security.FileIntegrityService;
 import com.android.server.security.KeyAttestationApplicationIdProviderService;
 import com.android.server.security.KeyChainSystemService;
@@ -294,6 +293,8 @@
             "com.android.server.DeviceIdleController";
     private static final String BLOB_STORE_MANAGER_SERVICE_CLASS =
             "com.android.server.blob.BlobStoreManagerService";
+    private static final String ROLLBACK_MANAGER_SERVICE_CLASS =
+            "com.android.server.rollback.RollbackManagerService";
 
     private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";
 
@@ -964,7 +965,7 @@
 
         // Manages apk rollbacks.
         t.traceBegin("StartRollbackManagerService");
-        mSystemServiceManager.startService(RollbackManagerService.class);
+        mSystemServiceManager.startService(ROLLBACK_MANAGER_SERVICE_CLASS);
         t.traceEnd();
 
         // Service to capture bugreports.
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index ae8d5743..136ee91 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -26,6 +26,7 @@
 import android.app.Person;
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetEvent;
+import android.app.usage.UsageEvents;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -70,6 +71,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
@@ -237,6 +239,27 @@
         eventHistory.addEvent(new Event(System.currentTimeMillis(), eventType));
     }
 
+    /**
+     * Queries events for moving app to foreground between {@code startTime} and {@code endTime}.
+     */
+    @NonNull
+    public List<UsageEvents.Event> queryAppMovingToForegroundEvents(@UserIdInt int callingUserId,
+            long startTime, long endTime) {
+        return UsageStatsQueryHelper.queryAppMovingToForegroundEvents(callingUserId, startTime,
+                endTime);
+    }
+
+    /**
+     * Queries launch counts of apps within {@code packageNameFilter} between {@code startTime}
+     * and {@code endTime}.
+     */
+    @NonNull
+    public Map<String, Integer> queryAppLaunchCount(@UserIdInt int callingUserId, long startTime,
+            long endTime, Set<String> packageNameFilter) {
+        return UsageStatsQueryHelper.queryAppLaunchCount(callingUserId, startTime, endTime,
+                packageNameFilter);
+    }
+
     /** Prunes the data for the specified user. */
     public void pruneDataForUser(@UserIdInt int userId, @NonNull CancellationSignal signal) {
         UserData userData = getUnlockedUserData(userId);
@@ -382,7 +405,13 @@
         }
     }
 
-    private int mimeTypeToShareEventType(String mimeType) {
+    /**
+     * Converts {@code mimeType} to {@link Event.EventType}.
+     */
+    public int mimeTypeToShareEventType(String mimeType) {
+        if (mimeType == null) {
+            return Event.TYPE_SHARE_OTHER;
+        }
         if (mimeType.startsWith("text/")) {
             return Event.TYPE_SHARE_TEXT;
         } else if (mimeType.startsWith("image/")) {
diff --git a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
index 72f1abb..6e6fea9 100644
--- a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
@@ -19,6 +19,8 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ComponentName;
 import android.content.LocusId;
@@ -27,7 +29,10 @@
 
 import com.android.server.LocalServices;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.function.Function;
 
 /** A helper class that queries {@link UsageStatsManagerInternal}. */
@@ -46,7 +51,7 @@
      */
     UsageStatsQueryHelper(@UserIdInt int userId,
             Function<String, PackageData> packageDataGetter) {
-        mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
+        mUsageStatsManagerInternal = getUsageStatsManagerInternal();
         mUserId = userId;
         mPackageDataGetter = packageDataGetter;
     }
@@ -106,6 +111,53 @@
         return mLastEventTimestamp;
     }
 
+    /**
+     * Queries {@link UsageStatsManagerInternal} events for moving app to foreground between
+     * {@code startTime} and {@code endTime}.
+     *
+     * @return a list containing events moving app to foreground.
+     */
+    static List<UsageEvents.Event> queryAppMovingToForegroundEvents(@UserIdInt int userId,
+            long startTime, long endTime) {
+        List<UsageEvents.Event> res = new ArrayList<>();
+        UsageEvents usageEvents = getUsageStatsManagerInternal().queryEventsForUser(userId,
+                startTime, endTime,
+                UsageEvents.HIDE_SHORTCUT_EVENTS | UsageEvents.HIDE_LOCUS_EVENTS);
+        if (usageEvents == null) {
+            return res;
+        }
+        while (usageEvents.hasNextEvent()) {
+            UsageEvents.Event e = new UsageEvents.Event();
+            usageEvents.getNextEvent(e);
+            if (e.getEventType() == UsageEvents.Event.ACTIVITY_RESUMED) {
+                res.add(e);
+            }
+        }
+        return res;
+    }
+
+    /**
+     * Queries {@link UsageStatsManagerInternal} for launch count of apps within {@code
+     * packageNameFilter} between {@code startTime} and {@code endTime}.obfuscateInstantApps
+     *
+     * @return a map which keys are package names and values are app launch counts.
+     */
+    static Map<String, Integer> queryAppLaunchCount(@UserIdInt int userId, long startTime,
+            long endTime, Set<String> packageNameFilter) {
+        List<UsageStats> stats = getUsageStatsManagerInternal().queryUsageStatsForUser(userId,
+                UsageStatsManager.INTERVAL_BEST, startTime, endTime,
+                /* obfuscateInstantApps= */ false);
+        Map<String, Integer> aggregatedStats = new ArrayMap<>();
+        for (UsageStats stat : stats) {
+            String packageName = stat.getPackageName();
+            if (packageNameFilter.contains(packageName)) {
+                aggregatedStats.put(packageName,
+                        aggregatedStats.getOrDefault(packageName, 0) + stat.getAppLaunchCount());
+            }
+        }
+        return aggregatedStats;
+    }
+
     private void onInAppConversationEnded(@NonNull PackageData packageData,
             @NonNull UsageEvents.Event endEvent) {
         ComponentName activityName =
@@ -138,4 +190,8 @@
                 EventStore.CATEGORY_LOCUS_ID_BASED, locusId.getId());
         eventHistory.addEvent(event);
     }
+
+    private static UsageStatsManagerInternal getUsageStatsManagerInternal() {
+        return LocalServices.getService(UsageStatsManagerInternal.class);
+    }
 }
diff --git a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
index 8e5d75b..d09d0b3 100644
--- a/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
+++ b/services/people/java/com/android/server/people/prediction/ShareTargetPredictor.java
@@ -27,13 +27,11 @@
 import android.content.IntentFilter;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager.ShareShortcutInfo;
-import android.util.Range;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ChooserActivity;
 import com.android.server.people.data.ConversationInfo;
 import com.android.server.people.data.DataManager;
-import com.android.server.people.data.Event;
 import com.android.server.people.data.EventHistory;
 import com.android.server.people.data.PackageData;
 
@@ -42,6 +40,9 @@
 import java.util.List;
 import java.util.function.Consumer;
 
+/**
+ * Predictor that predicts the {@link AppTarget} the user is most likely to open on share sheet.
+ */
 class ShareTargetPredictor extends AppTargetPredictor {
 
     private final IntentFilter mIntentFilter;
@@ -66,7 +67,9 @@
     @Override
     void predictTargets() {
         List<ShareTarget> shareTargets = getDirectShareTargets();
-        rankTargets(shareTargets);
+        SharesheetModelScorer.computeScore(shareTargets, getShareEventType(mIntentFilter),
+                System.currentTimeMillis());
+        Collections.sort(shareTargets, (t1, t2) -> -Float.compare(t1.getScore(), t2.getScore()));
         List<AppTarget> res = new ArrayList<>();
         for (int i = 0; i < Math.min(getPredictionContext().getPredictedTargetCount(),
                 shareTargets.size()); i++) {
@@ -80,36 +83,16 @@
     @Override
     void sortTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
         List<ShareTarget> shareTargets = getAppShareTargets(targets);
-        rankTargets(shareTargets);
+        SharesheetModelScorer.computeScoreForAppShare(shareTargets,
+                getShareEventType(mIntentFilter), getPredictionContext().getPredictedTargetCount(),
+                System.currentTimeMillis(), getDataManager(),
+                mCallingUserId);
+        Collections.sort(shareTargets, (t1, t2) -> -Float.compare(t1.getScore(), t2.getScore()));
         List<AppTarget> appTargetList = new ArrayList<>();
         shareTargets.forEach(t -> appTargetList.add(t.getAppTarget()));
         callback.accept(appTargetList);
     }
 
-    private void rankTargets(List<ShareTarget> shareTargets) {
-        // Rank targets based on recency of sharing history only for the moment.
-        // TODO: Take more factors into ranking, e.g. frequency, mime type, foreground app.
-        Collections.sort(shareTargets, (t1, t2) -> {
-            if (t1.getEventHistory() == null) {
-                return 1;
-            }
-            if (t2.getEventHistory() == null) {
-                return -1;
-            }
-            Range<Long> timeSlot1 = t1.getEventHistory().getEventIndex(
-                    Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot();
-            Range<Long> timeSlot2 = t2.getEventHistory().getEventIndex(
-                    Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot();
-            if (timeSlot1 == null) {
-                return 1;
-            } else if (timeSlot2 == null) {
-                return -1;
-            } else {
-                return -Long.compare(timeSlot1.getUpper(), timeSlot2.getUpper());
-            }
-        });
-    }
-
     private List<ShareTarget> getDirectShareTargets() {
         List<ShareTarget> shareTargets = new ArrayList<>();
         List<ShareShortcutInfo> shareShortcuts =
@@ -153,6 +136,11 @@
         return shareTargets;
     }
 
+    private int getShareEventType(IntentFilter intentFilter) {
+        String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null;
+        return getDataManager().mimeTypeToShareEventType(mimeType);
+    }
+
     @VisibleForTesting
     static class ShareTarget {
 
@@ -162,13 +150,16 @@
         private final EventHistory mEventHistory;
         @Nullable
         private final ConversationInfo mConversationInfo;
+        private float mScore;
 
-        private ShareTarget(@NonNull AppTarget appTarget,
+        @VisibleForTesting
+        ShareTarget(@NonNull AppTarget appTarget,
                 @Nullable EventHistory eventHistory,
                 @Nullable ConversationInfo conversationInfo) {
             mAppTarget = appTarget;
             mEventHistory = eventHistory;
             mConversationInfo = conversationInfo;
+            mScore = 0f;
         }
 
         @NonNull
@@ -188,5 +179,15 @@
         ConversationInfo getConversationInfo() {
             return mConversationInfo;
         }
+
+        @VisibleForTesting
+        float getScore() {
+            return mScore;
+        }
+
+        @VisibleForTesting
+        void setScore(float score) {
+            mScore = score;
+        }
     }
 }
diff --git a/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java b/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java
new file mode 100644
index 0000000..0ac5724
--- /dev/null
+++ b/services/people/java/com/android/server/people/prediction/SharesheetModelScorer.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2020 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.people.prediction;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.usage.UsageEvents;
+import android.util.ArrayMap;
+import android.util.Pair;
+import android.util.Range;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.ChooserActivity;
+import com.android.server.people.data.DataManager;
+import com.android.server.people.data.Event;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.PriorityQueue;
+import java.util.concurrent.TimeUnit;
+
+/** Ranking scorer for Sharesheet targets. */
+class SharesheetModelScorer {
+
+    private static final String TAG = "SharesheetModelScorer";
+    private static final boolean DEBUG = false;
+    private static final Integer RECENCY_SCORE_COUNT = 6;
+    private static final float RECENCY_INITIAL_BASE_SCORE = 0.4F;
+    private static final float RECENCY_SCORE_INITIAL_DECAY = 0.05F;
+    private static final float RECENCY_SCORE_SUBSEQUENT_DECAY = 0.02F;
+    private static final long ONE_MONTH_WINDOW = TimeUnit.DAYS.toMillis(30);
+    private static final long FOREGROUND_APP_PROMO_TIME_WINDOW = TimeUnit.MINUTES.toMillis(10);
+    private static final float FREQUENTLY_USED_APP_SCORE_DECAY = 0.9F;
+    @VisibleForTesting
+    static final float FOREGROUND_APP_WEIGHT = 0F;
+    @VisibleForTesting
+    static final String CHOOSER_ACTIVITY = ChooserActivity.class.getSimpleName();
+
+    // Keep constructor private to avoid class being instantiated.
+    private SharesheetModelScorer() {
+    }
+
+    /**
+     * Computes each target's recency, frequency and frequency of the same {@code shareEventType}
+     * based on past sharing history. Update {@link ShareTargetPredictor.ShareTargetScore}.
+     */
+    static void computeScore(List<ShareTargetPredictor.ShareTarget> shareTargets,
+            int shareEventType, long now) {
+        if (shareTargets.isEmpty()) {
+            return;
+        }
+        float totalFreqScore = 0f;
+        int freqScoreCount = 0;
+        float totalMimeFreqScore = 0f;
+        int mimeFreqScoreCount = 0;
+        // Top of this heap has lowest rank.
+        PriorityQueue<Pair<ShareTargetRankingScore, Range<Long>>> recencyMinHeap =
+                new PriorityQueue<>(RECENCY_SCORE_COUNT,
+                        Comparator.comparingLong(p -> p.second.getUpper()));
+        List<ShareTargetRankingScore> scoreList = new ArrayList<>(shareTargets.size());
+        for (ShareTargetPredictor.ShareTarget target : shareTargets) {
+            ShareTargetRankingScore shareTargetScore = new ShareTargetRankingScore();
+            scoreList.add(shareTargetScore);
+            if (target.getEventHistory() == null) {
+                continue;
+            }
+            // Counts frequency
+            List<Range<Long>> timeSlots = target.getEventHistory().getEventIndex(
+                    Event.SHARE_EVENT_TYPES).getActiveTimeSlots();
+            if (!timeSlots.isEmpty()) {
+                for (Range<Long> timeSlot : timeSlots) {
+                    shareTargetScore.incrementFrequencyScore(
+                            getFreqDecayedOnElapsedTime(now - timeSlot.getLower()));
+                }
+                totalFreqScore += shareTargetScore.getFrequencyScore();
+                freqScoreCount++;
+            }
+            // Counts frequency for sharing same mime type
+            List<Range<Long>> timeSlotsOfSameType = target.getEventHistory().getEventIndex(
+                    shareEventType).getActiveTimeSlots();
+            if (!timeSlotsOfSameType.isEmpty()) {
+                for (Range<Long> timeSlot : timeSlotsOfSameType) {
+                    shareTargetScore.incrementMimeFrequencyScore(
+                            getFreqDecayedOnElapsedTime(now - timeSlot.getLower()));
+                }
+                totalMimeFreqScore += shareTargetScore.getMimeFrequencyScore();
+                mimeFreqScoreCount++;
+            }
+            // Records most recent targets
+            Range<Long> mostRecentTimeSlot = target.getEventHistory().getEventIndex(
+                    Event.SHARE_EVENT_TYPES).getMostRecentActiveTimeSlot();
+            if (mostRecentTimeSlot == null) {
+                continue;
+            }
+            if (recencyMinHeap.size() < RECENCY_SCORE_COUNT
+                    || mostRecentTimeSlot.getUpper() > recencyMinHeap.peek().second.getUpper()) {
+                if (recencyMinHeap.size() == RECENCY_SCORE_COUNT) {
+                    recencyMinHeap.poll();
+                }
+                recencyMinHeap.offer(new Pair(shareTargetScore, mostRecentTimeSlot));
+            }
+        }
+        // Calculates recency score
+        while (!recencyMinHeap.isEmpty()) {
+            float recencyScore = RECENCY_INITIAL_BASE_SCORE;
+            if (recencyMinHeap.size() > 1) {
+                recencyScore = RECENCY_INITIAL_BASE_SCORE - RECENCY_SCORE_INITIAL_DECAY
+                        - RECENCY_SCORE_SUBSEQUENT_DECAY * (recencyMinHeap.size() - 2);
+            }
+            recencyMinHeap.poll().first.setRecencyScore(recencyScore);
+        }
+
+        Float avgFreq = freqScoreCount != 0 ? totalFreqScore / freqScoreCount : 0f;
+        Float avgMimeFreq = mimeFreqScoreCount != 0 ? totalMimeFreqScore / mimeFreqScoreCount : 0f;
+        for (int i = 0; i < scoreList.size(); i++) {
+            ShareTargetPredictor.ShareTarget target = shareTargets.get(i);
+            ShareTargetRankingScore targetScore = scoreList.get(i);
+            // Normalizes freq and mimeFreq score
+            targetScore.setFrequencyScore(normalizeFreqScore(
+                    avgFreq.equals(0f) ? 0f : targetScore.getFrequencyScore() / avgFreq));
+            targetScore.setMimeFrequencyScore(normalizeMimeFreqScore(avgMimeFreq.equals(0f) ? 0f
+                    : targetScore.getMimeFrequencyScore() / avgMimeFreq));
+            // Calculates total score
+            targetScore.setTotalScore(
+                    probOR(probOR(targetScore.getRecencyScore(), targetScore.getFrequencyScore()),
+                            targetScore.getMimeFrequencyScore()));
+            target.setScore(targetScore.getTotalScore());
+
+            if (DEBUG) {
+                Slog.d(TAG, String.format(
+                        "SharesheetModel: packageName: %s, className: %s, shortcutId: %s, "
+                                + "recency:%.2f, freq_all:%.2f, freq_mime:%.2f, total:%.2f",
+                        target.getAppTarget().getPackageName(),
+                        target.getAppTarget().getClassName(),
+                        target.getAppTarget().getShortcutInfo() != null
+                                ? target.getAppTarget().getShortcutInfo().getId() : null,
+                        targetScore.getRecencyScore(),
+                        targetScore.getFrequencyScore(),
+                        targetScore.getMimeFrequencyScore(),
+                        targetScore.getTotalScore()));
+            }
+        }
+    }
+
+    /**
+     * Computes ranking score for app sharing. Update {@link ShareTargetPredictor.ShareTargetScore}.
+     */
+    static void computeScoreForAppShare(List<ShareTargetPredictor.ShareTarget> shareTargets,
+            int shareEventType, int targetsLimit, long now, @NonNull DataManager dataManager,
+            @UserIdInt int callingUserId) {
+        computeScore(shareTargets, shareEventType, now);
+        postProcess(shareTargets, targetsLimit, dataManager, callingUserId);
+    }
+
+    private static void postProcess(List<ShareTargetPredictor.ShareTarget> shareTargets,
+            int targetsLimit, @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
+        // Populates a map which key is package name and value is list of shareTargets descended
+        // on total score.
+        Map<String, List<ShareTargetPredictor.ShareTarget>> shareTargetMap = new ArrayMap<>();
+        for (ShareTargetPredictor.ShareTarget shareTarget : shareTargets) {
+            String packageName = shareTarget.getAppTarget().getPackageName();
+            shareTargetMap.computeIfAbsent(packageName, key -> new ArrayList<>());
+            List<ShareTargetPredictor.ShareTarget> targetsList = shareTargetMap.get(packageName);
+            int index = 0;
+            while (index < targetsList.size()) {
+                if (shareTarget.getScore() > targetsList.get(index).getScore()) {
+                    break;
+                }
+                index++;
+            }
+            targetsList.add(index, shareTarget);
+        }
+        promoteForegroundApp(shareTargetMap, dataManager, callingUserId);
+        promoteFrequentlyUsedApps(shareTargetMap, targetsLimit, dataManager, callingUserId);
+    }
+
+    /**
+     * Promotes frequently used sharing apps, if recommended apps based on sharing history have not
+     * reached the limit (e.g. user did not share any content in last couple weeks)
+     */
+    private static void promoteFrequentlyUsedApps(
+            Map<String, List<ShareTargetPredictor.ShareTarget>> shareTargetMap, int targetsLimit,
+            @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
+        int validPredictionNum = 0;
+        float minValidScore = 1f;
+        for (List<ShareTargetPredictor.ShareTarget> targets : shareTargetMap.values()) {
+            for (ShareTargetPredictor.ShareTarget target : targets) {
+                if (target.getScore() > 0f) {
+                    validPredictionNum++;
+                    minValidScore = Math.min(target.getScore(), minValidScore);
+                }
+            }
+        }
+        // Skips if recommended apps based on sharing history have already reached the limit.
+        if (validPredictionNum >= targetsLimit) {
+            return;
+        }
+        long now = System.currentTimeMillis();
+        Map<String, Integer> appLaunchCountsMap = dataManager.queryAppLaunchCount(
+                callingUserId, now - ONE_MONTH_WINDOW, now, shareTargetMap.keySet());
+        List<Pair<String, Integer>> appLaunchCounts = new ArrayList<>();
+        for (Map.Entry<String, Integer> entry : appLaunchCountsMap.entrySet()) {
+            if (entry.getValue() > 0) {
+                appLaunchCounts.add(new Pair(entry.getKey(), entry.getValue()));
+            }
+        }
+        Collections.sort(appLaunchCounts, (p1, p2) -> -Integer.compare(p1.second, p2.second));
+        for (Pair<String, Integer> entry : appLaunchCounts) {
+            if (!shareTargetMap.containsKey(entry.first)) {
+                continue;
+            }
+            ShareTargetPredictor.ShareTarget target = shareTargetMap.get(entry.first).get(0);
+            if (target.getScore() > 0f) {
+                continue;
+            }
+            minValidScore *= FREQUENTLY_USED_APP_SCORE_DECAY;
+            target.setScore(minValidScore);
+            if (DEBUG) {
+                Slog.d(TAG, String.format(
+                        "SharesheetModel: promoteFrequentUsedApps packageName: %s, className: %s,"
+                                + " total:%.2f",
+                        target.getAppTarget().getPackageName(),
+                        target.getAppTarget().getClassName(),
+                        target.getScore()));
+            }
+            validPredictionNum++;
+            if (validPredictionNum == targetsLimit) {
+                return;
+            }
+        }
+    }
+
+    /**
+     * Promotes the foreground app just prior to source sharing app. Share often occurs between
+     * two apps the user is switching.
+     */
+    private static void promoteForegroundApp(
+            Map<String, List<ShareTargetPredictor.ShareTarget>> shareTargetMap,
+            @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
+        String sharingForegroundApp = findSharingForegroundApp(shareTargetMap, dataManager,
+                callingUserId);
+        if (sharingForegroundApp != null) {
+            ShareTargetPredictor.ShareTarget target = shareTargetMap.get(sharingForegroundApp).get(
+                    0);
+            target.setScore(probOR(target.getScore(), FOREGROUND_APP_WEIGHT));
+            if (DEBUG) {
+                Slog.d(TAG, String.format(
+                        "SharesheetModel: promoteForegroundApp packageName: %s, className: %s, "
+                                + "total:%.2f",
+                        target.getAppTarget().getPackageName(),
+                        target.getAppTarget().getClassName(),
+                        target.getScore()));
+            }
+        }
+    }
+
+    /**
+     * Find the foreground app just prior to source sharing app from usageStatsManager. Returns null
+     * if it is not available.
+     */
+    @Nullable
+    private static String findSharingForegroundApp(
+            Map<String, List<ShareTargetPredictor.ShareTarget>> shareTargetMap,
+            @NonNull DataManager dataManager, @UserIdInt int callingUserId) {
+        String sharingForegroundApp = null;
+        long now = System.currentTimeMillis();
+        List<UsageEvents.Event> events = dataManager.queryAppMovingToForegroundEvents(
+                callingUserId, now - FOREGROUND_APP_PROMO_TIME_WINDOW, now);
+        String sourceApp = null;
+        for (int i = events.size() - 1; i >= 0; i--) {
+            String className = events.get(i).getClassName();
+            String packageName = events.get(i).getPackageName();
+            if (packageName == null || (className != null && className.contains(CHOOSER_ACTIVITY))
+                    || packageName.contains(CHOOSER_ACTIVITY)) {
+                continue;
+            }
+            if (sourceApp == null) {
+                sourceApp = packageName;
+            } else if (!packageName.equals(sourceApp) && shareTargetMap.containsKey(packageName)) {
+                sharingForegroundApp = packageName;
+                break;
+            }
+        }
+        return sharingForegroundApp;
+    }
+
+    /**
+     * Probabilistic OR (also known as the algebraic sum). If a <= 1 and b <= 1, the result will be
+     * <= 1.0.
+     */
+    private static float probOR(float a, float b) {
+        return 1f - (1f - a) * (1f - b);
+    }
+
+    /** Counts frequency of share targets. Decays frequency for old shares. */
+    private static float getFreqDecayedOnElapsedTime(long elapsedTimeMillis) {
+        Duration duration = Duration.ofMillis(elapsedTimeMillis);
+        if (duration.compareTo(Duration.ofDays(1)) <= 0) {
+            return 1.0f;
+        } else if (duration.compareTo(Duration.ofDays(3)) <= 0) {
+            return 0.9f;
+        } else if (duration.compareTo(Duration.ofDays(7)) <= 0) {
+            return 0.8f;
+        } else if (duration.compareTo(Duration.ofDays(14)) <= 0) {
+            return 0.7f;
+        } else {
+            return 0.6f;
+        }
+    }
+
+    /** Normalizes frequency score. */
+    private static float normalizeFreqScore(double freqRatio) {
+        if (freqRatio >= 2.5) {
+            return 0.2f;
+        } else if (freqRatio >= 1.5) {
+            return 0.15f;
+        } else if (freqRatio >= 1.0) {
+            return 0.1f;
+        } else if (freqRatio >= 0.75) {
+            return 0.05f;
+        } else {
+            return 0f;
+        }
+    }
+
+    /** Normalizes mimetype-specific frequency score. */
+    private static float normalizeMimeFreqScore(double freqRatio) {
+        if (freqRatio >= 2.0) {
+            return 0.2f;
+        } else if (freqRatio >= 1.2) {
+            return 0.15f;
+        } else if (freqRatio > 0.0) {
+            return 0.1f;
+        } else {
+            return 0f;
+        }
+    }
+
+    private static class ShareTargetRankingScore {
+
+        private float mRecencyScore = 0f;
+        private float mFrequencyScore = 0f;
+        private float mMimeFrequencyScore = 0f;
+        private float mTotalScore = 0f;
+
+        float getTotalScore() {
+            return mTotalScore;
+        }
+
+        void setTotalScore(float totalScore) {
+            mTotalScore = totalScore;
+        }
+
+        float getRecencyScore() {
+            return mRecencyScore;
+        }
+
+        void setRecencyScore(float recencyScore) {
+            mRecencyScore = recencyScore;
+        }
+
+        float getFrequencyScore() {
+            return mFrequencyScore;
+        }
+
+        void setFrequencyScore(float frequencyScore) {
+            mFrequencyScore = frequencyScore;
+        }
+
+        void incrementFrequencyScore(float incremental) {
+            mFrequencyScore += incremental;
+        }
+
+        float getMimeFrequencyScore() {
+            return mMimeFrequencyScore;
+        }
+
+        void setMimeFrequencyScore(float mimeFrequencyScore) {
+            mMimeFrequencyScore = mimeFrequencyScore;
+        }
+
+        void incrementMimeFrequencyScore(float incremental) {
+            mMimeFrequencyScore += incremental;
+        }
+    }
+}
diff --git a/services/robotests/src/com/android/server/location/GnssAntennaInfoProviderTest.java b/services/robotests/src/com/android/server/location/gnss/GnssAntennaInfoProviderTest.java
similarity index 97%
rename from services/robotests/src/com/android/server/location/GnssAntennaInfoProviderTest.java
rename to services/robotests/src/com/android/server/location/gnss/GnssAntennaInfoProviderTest.java
index 76f7ad6..d8acd6e 100644
--- a/services/robotests/src/com/android/server/location/GnssAntennaInfoProviderTest.java
+++ b/services/robotests/src/com/android/server/location/gnss/GnssAntennaInfoProviderTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.location;
+package com.android.server.location.gnss;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -41,7 +41,8 @@
 @Presubmit
 public class GnssAntennaInfoProviderTest {
     @Mock
-    private GnssAntennaInfoProvider.GnssAntennaInfoProviderNative mMockNative;
+    private GnssAntennaInfoProvider.GnssAntennaInfoProviderNative
+            mMockNative;
     private GnssAntennaInfoProvider mTestProvider;
 
     /** Setup. */
diff --git a/services/robotests/src/com/android/server/location/GnssBatchingProviderTest.java b/services/robotests/src/com/android/server/location/gnss/GnssBatchingProviderTest.java
similarity index 76%
rename from services/robotests/src/com/android/server/location/GnssBatchingProviderTest.java
rename to services/robotests/src/com/android/server/location/gnss/GnssBatchingProviderTest.java
index d58c3f7..25d6aa4 100644
--- a/services/robotests/src/com/android/server/location/GnssBatchingProviderTest.java
+++ b/services/robotests/src/com/android/server/location/gnss/GnssBatchingProviderTest.java
@@ -1,4 +1,20 @@
-package com.android.server.location;
+/*
+ * Copyright (C) 2020 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.location.gnss;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -11,7 +27,7 @@
 
 import android.platform.test.annotations.Presubmit;
 
-import com.android.server.location.GnssBatchingProvider.GnssBatchingProviderNative;
+import com.android.server.location.gnss.GnssBatchingProvider.GnssBatchingProviderNative;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/services/robotests/src/com/android/server/location/GnssGeofenceProviderTest.java b/services/robotests/src/com/android/server/location/gnss/GnssGeofenceProviderTest.java
similarity index 86%
rename from services/robotests/src/com/android/server/location/GnssGeofenceProviderTest.java
rename to services/robotests/src/com/android/server/location/gnss/GnssGeofenceProviderTest.java
index 30c7336..4a533aa 100644
--- a/services/robotests/src/com/android/server/location/GnssGeofenceProviderTest.java
+++ b/services/robotests/src/com/android/server/location/gnss/GnssGeofenceProviderTest.java
@@ -1,4 +1,20 @@
-package com.android.server.location;
+/*
+ * Copyright (C) 2020 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.location.gnss;
 
 import static org.mockito.ArgumentMatchers.anyDouble;
 import static org.mockito.ArgumentMatchers.anyInt;
diff --git a/services/robotests/src/com/android/server/location/GnssMeasurementsProviderTest.java b/services/robotests/src/com/android/server/location/gnss/GnssMeasurementsProviderTest.java
similarity index 78%
rename from services/robotests/src/com/android/server/location/GnssMeasurementsProviderTest.java
rename to services/robotests/src/com/android/server/location/gnss/GnssMeasurementsProviderTest.java
index b349b67..e7d9ef8 100644
--- a/services/robotests/src/com/android/server/location/GnssMeasurementsProviderTest.java
+++ b/services/robotests/src/com/android/server/location/gnss/GnssMeasurementsProviderTest.java
@@ -1,4 +1,20 @@
-package com.android.server.location;
+/*
+ * Copyright (C) 2020 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.location.gnss;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -26,7 +42,8 @@
 @Presubmit
 public class GnssMeasurementsProviderTest {
     @Mock
-    private GnssMeasurementsProvider.GnssMeasurementProviderNative mMockNative;
+    private GnssMeasurementsProvider.GnssMeasurementProviderNative
+            mMockNative;
     private GnssMeasurementsProvider mTestProvider;
 
     @Before
diff --git a/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java b/services/robotests/src/com/android/server/location/gnss/GnssNavigationMessageProviderTest.java
similarity index 78%
rename from services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java
rename to services/robotests/src/com/android/server/location/gnss/GnssNavigationMessageProviderTest.java
index aa2a96e..c21db73 100644
--- a/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java
+++ b/services/robotests/src/com/android/server/location/gnss/GnssNavigationMessageProviderTest.java
@@ -1,4 +1,20 @@
-package com.android.server.location;
+/*
+ * Copyright (C) 2020 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.location.gnss;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -25,7 +41,8 @@
 @Presubmit
 public class GnssNavigationMessageProviderTest {
     @Mock
-    private GnssNavigationMessageProvider.GnssNavigationMessageProviderNative mMockNative;
+    private GnssNavigationMessageProvider.GnssNavigationMessageProviderNative
+            mMockNative;
     private GnssNavigationMessageProvider mTestProvider;
 
     @Before
diff --git a/services/robotests/src/com/android/server/location/GnssPositionModeTest.java b/services/robotests/src/com/android/server/location/gnss/GnssPositionModeTest.java
similarity index 73%
rename from services/robotests/src/com/android/server/location/GnssPositionModeTest.java
rename to services/robotests/src/com/android/server/location/gnss/GnssPositionModeTest.java
index f37f50e..7117ff9 100644
--- a/services/robotests/src/com/android/server/location/GnssPositionModeTest.java
+++ b/services/robotests/src/com/android/server/location/gnss/GnssPositionModeTest.java
@@ -1,4 +1,20 @@
-package com.android.server.location;
+/*
+ * Copyright (C) 2020 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.location.gnss;
 
 import static com.google.common.truth.Truth.assertThat;
 
diff --git a/services/robotests/src/com/android/server/location/GnssSatelliteBlacklistHelperTest.java b/services/robotests/src/com/android/server/location/gnss/GnssSatelliteBlacklistHelperTest.java
similarity index 86%
rename from services/robotests/src/com/android/server/location/GnssSatelliteBlacklistHelperTest.java
rename to services/robotests/src/com/android/server/location/gnss/GnssSatelliteBlacklistHelperTest.java
index ba4a753..7c73a2f 100644
--- a/services/robotests/src/com/android/server/location/GnssSatelliteBlacklistHelperTest.java
+++ b/services/robotests/src/com/android/server/location/gnss/GnssSatelliteBlacklistHelperTest.java
@@ -1,4 +1,20 @@
-package com.android.server.location;
+/*
+ * Copyright (C) 2020 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.location.gnss;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -35,7 +51,8 @@
 
     private ContentResolver mContentResolver;
     @Mock
-    private GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback mCallback;
+    private GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback
+            mCallback;
 
     @Before
     public void setUp() {
diff --git a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java b/services/robotests/src/com/android/server/location/gnss/NtpTimeHelperTest.java
similarity index 83%
rename from services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
rename to services/robotests/src/com/android/server/location/gnss/NtpTimeHelperTest.java
index 9c5d4ad..9b59aad 100644
--- a/services/robotests/src/com/android/server/location/NtpTimeHelperTest.java
+++ b/services/robotests/src/com/android/server/location/gnss/NtpTimeHelperTest.java
@@ -1,4 +1,20 @@
-package com.android.server.location;
+/*
+ * Copyright (C) 2020 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.location.gnss;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -10,7 +26,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.NtpTrustedTime;
 
-import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback;
+import com.android.server.location.gnss.NtpTimeHelper.InjectNtpTimeCallback;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index acdb6814..138f982 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -17,6 +17,7 @@
 package com.android.server.pm;
 
 import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES;
 import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
 import static android.content.Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND;
@@ -104,6 +105,7 @@
     private final CrossProfileAppsServiceImpl mCrossProfileAppsServiceImpl =
             new CrossProfileAppsServiceImpl(mContext, mInjector);
     private final Map<UserHandle, Set<Intent>> mSentUserBroadcasts = new HashMap<>();
+    private final Map<Integer, List<ApplicationInfo>> installedApplications = new HashMap<>();
 
     @Mock private PackageManagerInternal mPackageManagerInternal;
     @Mock private IPackageManager mIPackageManager;
@@ -112,12 +114,18 @@
     @Before
     public void initializeMocks() throws Exception {
         MockitoAnnotations.initMocks(this);
+        initializeInstalledApplicationsMock();
         mockCrossProfileAppInstalledAndEnabledOnEachProfile();
         mockCrossProfileAppRequestsInteractAcrossProfiles();
         mockCrossProfileAppRegistersBroadcastReceiver();
         mockCrossProfileAppWhitelisted();
     }
 
+    private void initializeInstalledApplicationsMock() {
+        when(mPackageManagerInternal.getInstalledApplications(anyInt(), anyInt(), eq(CALLING_UID)))
+                .thenAnswer(invocation -> installedApplications.get(invocation.getArgument(1)));
+    }
+
     private void mockCrossProfileAppInstalledAndEnabledOnEachProfile() {
         // They are enabled by default, so we simply have to ensure that a package info with an
         // application info is returned.
@@ -138,11 +146,14 @@
         when(mPackageManagerInternal.getPackage(uid))
                 .thenReturn(((ParsedPackage) PackageImpl.forTesting(CROSS_PROFILE_APP_PACKAGE_NAME)
                         .hideAsParsed()).hideAsFinal());
+        installedApplications.putIfAbsent(userId, new ArrayList<>());
+        installedApplications.get(userId).add(packageInfo.applicationInfo);
     }
 
     private PackageInfo buildTestPackageInfo() {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.applicationInfo = new ApplicationInfo();
+        packageInfo.applicationInfo.packageName = CROSS_PROFILE_APP_PACKAGE_NAME;
         return packageInfo;
     }
 
@@ -451,6 +462,13 @@
                 .isTrue();
     }
 
+    @Test
+    public void clearInteractAcrossProfilesAppOps() {
+        explicitlySetInteractAcrossProfilesAppOp(MODE_ALLOWED);
+        mCrossProfileAppsServiceImpl.clearInteractAcrossProfilesAppOps();
+        assertThat(getCrossProfileAppOp()).isEqualTo(MODE_DEFAULT);
+    }
+
     private void explicitlySetInteractAcrossProfilesAppOp(@Mode int mode) {
         explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, mode);
     }
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 449e75c..e58e911 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -91,7 +91,15 @@
         enabled: false,
     },
 
-    data: [":JobTestApp"],
+    data: [
+        ":JobTestApp",
+    ],
+
+    java_resources: [
+        ":PackageParserTestApp1",
+        ":PackageParserTestApp2",
+        ":PackageParserTestApp3",
+    ],
     resource_zips: [":FrameworksServicesTests_apks_as_resources"],
 }
 
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 1212f20..109c119 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -15,218 +15,227 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.frameworks.servicestests">
+     package="com.android.frameworks.servicestests">
 
-    <uses-permission android:name="android.permission.READ_LOGS" />
-    <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" />
-    <uses-permission android:name="android.permission.ACCOUNT_MANAGER" />
-    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
-    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
-    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
-    <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
-    <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
-    <uses-permission android:name="android.permission.WAKE_LOCK" />
-    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
-    <uses-permission android:name="android.permission.REAL_GET_TASKS" />
-    <uses-permission android:name="android.permission.GET_DETAILED_TASKS" />
-    <uses-permission android:name="android.permission.REORDER_TASKS" />
-    <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
-    <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
-    <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
-    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
-    <uses-permission android:name="android.permission.MANAGE_USERS" />
-    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
-    <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
-    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
-    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
-    <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
-    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
-    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
-    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
-    <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
-    <uses-permission android:name="android.permission.DELETE_PACKAGES" />
-    <uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
-    <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
+    <uses-permission android:name="android.permission.READ_LOGS"/>
+    <uses-permission android:name="android.permission.ACCESS_VR_MANAGER"/>
+    <uses-permission android:name="android.permission.ACCOUNT_MANAGER"/>
+    <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.BROADCAST_STICKY"/>
+    <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS"/>
+    <uses-permission android:name="android.permission.MANAGE_APP_TOKENS"/>
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
+    <uses-permission android:name="android.permission.REAL_GET_TASKS"/>
+    <uses-permission android:name="android.permission.GET_DETAILED_TASKS"/>
+    <uses-permission android:name="android.permission.REORDER_TASKS"/>
+    <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY"/>
+    <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
+    <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY"/>
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.MANAGE_USERS"/>
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+    <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS"/>
+    <uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD"/>
+    <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT"/>
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"/>
+    <uses-permission android:name="android.permission.INSTALL_PACKAGES"/>
+    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION"/>
+    <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
+    <uses-permission android:name="android.permission.DELETE_PACKAGES"/>
+    <uses-permission android:name="android.permission.GET_APP_OPS_STATS"/>
+    <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS"/>
     <uses-permission android:name="android.permission.MANAGE_APP_OPS_MODES"/>
-    <uses-permission android:name="android.permission.DEVICE_POWER" />
-    <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
-    <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
-    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
-    <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
-    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
-    <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.STORAGE_INTERNAL" />
-    <uses-permission android:name="android.permission.WATCH_APPOPS" />
+    <uses-permission android:name="android.permission.DEVICE_POWER"/>
+    <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
+    <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"/>
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+    <uses-permission android:name="android.permission.STATUS_BAR_SERVICE"/>
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER"/>
+    <uses-permission android:name="android.permission.READ_FRAME_BUFFER"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.STORAGE_INTERNAL"/>
+    <uses-permission android:name="android.permission.WATCH_APPOPS"/>
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.SUSPEND_APPS"/>
-    <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
-    <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
+    <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
+    <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
     <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/>
-    <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
-    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
-    <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
+    <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/>
+    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
+    <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG"/>
     <uses-permission android:name="android.permission.HARDWARE_TEST"/>
     <uses-permission android:name="android.permission.BLUETOOTH"/>
-    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
-    <uses-permission android:name="android.permission.DUMP" />
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+    <uses-permission android:name="android.permission.DUMP"/>
     <uses-permission android:name="android.permission.READ_DREAM_STATE"/>
     <uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
 
     <!-- Uses API introduced in O (26) -->
     <uses-sdk android:minSdkVersion="1"
-          android:targetSdkVersion="26"/>
+         android:targetSdkVersion="26"/>
 
     <application android:testOnly="true">
-        <uses-library android:name="android.test.runner" />
+        <uses-library android:name="android.test.runner"/>
 
         <service android:name="com.android.server.accounts.TestAccountType1AuthenticatorService"
-            android:exported="false">
+             android:exported="false">
           <intent-filter>
-            <action android:name="android.accounts.AccountAuthenticator" />
+            <action android:name="android.accounts.AccountAuthenticator"/>
           </intent-filter>
           <meta-data android:name="android.accounts.AccountAuthenticator"
-              android:resource="@xml/test_account_type1_authenticator" />
+               android:resource="@xml/test_account_type1_authenticator"/>
         </service>
 
         <service android:name="com.android.server.accounts.TestAccountType2AuthenticatorService"
-            android:exported="false">
+             android:exported="false">
           <intent-filter>
-            <action android:name="android.accounts.AccountAuthenticator" />
+            <action android:name="android.accounts.AccountAuthenticator"/>
           </intent-filter>
           <meta-data android:name="android.accounts.AccountAuthenticator"
-              android:resource="@xml/test_account_type2_authenticator" />
+               android:resource="@xml/test_account_type2_authenticator"/>
         </service>
 
         <receiver android:name="com.android.server.devicepolicy.ApplicationRestrictionsTest$AdminReceiver"
-                android:permission="android.permission.BIND_DEVICE_ADMIN">
+             android:permission="android.permission.BIND_DEVICE_ADMIN">
             <meta-data android:name="android.app.device_admin"
-                       android:resource="@xml/device_admin_sample" />
+                 android:resource="@xml/device_admin_sample"/>
             <intent-filter>
-                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
             </intent-filter>
         </receiver>
 
         <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin1"
-            android:permission="android.permission.BIND_DEVICE_ADMIN">
+             android:permission="android.permission.BIND_DEVICE_ADMIN">
             <meta-data android:name="android.app.device_admin"
-                android:resource="@xml/device_admin_sample" />
+                 android:resource="@xml/device_admin_sample"/>
             <intent-filter>
-                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
             </intent-filter>
         </receiver>
 
         <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin2"
-            android:permission="android.permission.BIND_DEVICE_ADMIN">
+             android:permission="android.permission.BIND_DEVICE_ADMIN">
             <meta-data android:name="android.app.device_admin"
-                android:resource="@xml/device_admin_sample" />
+                 android:resource="@xml/device_admin_sample"/>
             <intent-filter>
-                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
             </intent-filter>
         </receiver>
 
         <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$Admin3"
-            android:permission="android.permission.BIND_DEVICE_ADMIN">
+             android:permission="android.permission.BIND_DEVICE_ADMIN">
             <meta-data android:name="android.app.device_admin"
-                android:resource="@xml/device_admin_sample" />
+                 android:resource="@xml/device_admin_sample"/>
             <intent-filter>
-                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
             </intent-filter>
         </receiver>
 
         <receiver android:name="com.android.server.devicepolicy.DummyDeviceAdmins$AdminNoPerm">
             <meta-data android:name="android.app.device_admin"
-                android:resource="@xml/device_admin_sample" />
+                 android:resource="@xml/device_admin_sample"/>
             <intent-filter>
-                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
             </intent-filter>
         </receiver>
 
         <service android:name="com.android.server.job.MockPriorityJobService"
-                 android:permission="android.permission.BIND_JOB_SERVICE" />
+             android:permission="android.permission.BIND_JOB_SERVICE"/>
 
-        <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity" />
-        <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2" />
-        <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3" />
+        <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity"/>
+        <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity2"/>
+        <activity android:name="com.android.server.pm.BaseShortcutManagerTest$ShortcutActivity3"/>
 
         <activity android:name="com.android.server.pm.ShortcutTestActivity"
-                 android:enabled="true" android:exported="true" />
+             android:enabled="true"
+             android:exported="true"/>
 
         <activity android:name="com.android.server.pm.SuspendedDetailsActivity"
-                  android:enabled="true"
-                  android:permission="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS">
+             android:enabled="true"
+             android:permission="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS">
             <intent-filter>
-                <action android:name="android.intent.action.SHOW_SUSPENDED_APP_DETAILS" />
-                <category android:name="android.intent.category.DEFAULT" />
+                <action android:name="android.intent.action.SHOW_SUSPENDED_APP_DETAILS"/>
+                <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
         </activity>
 
-        <activity android:name="com.android.server.accounts.AccountAuthenticatorDummyActivity" />
-        <activity android:name="com.android.server.adb.AdbDebuggingManagerTestActivity" />
+        <activity android:name="com.android.server.accounts.AccountAuthenticatorDummyActivity"/>
+        <activity android:name="com.android.server.adb.AdbDebuggingManagerTestActivity"/>
 
         <activity-alias android:name="a.ShortcutEnabled"
-            android:targetActivity="com.android.server.pm.ShortcutTestActivity"
-            android:enabled="true" android:exported="true">
+             android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+             android:enabled="true"
+             android:exported="true">
         </activity-alias>
         <activity-alias android:name="a.ShortcutDisabled"
-            android:targetActivity="com.android.server.pm.ShortcutTestActivity"
-            android:enabled="false" android:exported="true">
-            <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_5"/>
+             android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+             android:enabled="false"
+             android:exported="true">
+            <meta-data android:name="android.app.shortcuts"
+                 android:resource="@xml/shortcut_5"/>
         </activity-alias>
         <activity-alias android:name="a.ShortcutUnexported"
-            android:targetActivity="com.android.server.pm.ShortcutTestActivity"
-            android:enabled="true" android:exported="false">
-            <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_5"/>
+             android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+             android:enabled="true"
+             android:exported="false">
+            <meta-data android:name="android.app.shortcuts"
+                 android:resource="@xml/shortcut_5"/>
         </activity-alias>
         <activity-alias android:name="a.Shortcut1"
-            android:targetActivity="com.android.server.pm.ShortcutTestActivity"
-            android:enabled="true" android:exported="true">
-            <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcut_1"/>
+             android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+             android:enabled="true"
+             android:exported="true">
+            <meta-data android:name="android.app.shortcuts"
+                 android:resource="@xml/shortcut_1"/>
         </activity-alias>
         <activity-alias android:name="a.ShortcutConfigActivity"
-                        android:targetActivity="com.android.server.pm.ShortcutTestActivity">
+             android:targetActivity="com.android.server.pm.ShortcutTestActivity">
             <intent-filter>
-                <action android:name="android.intent.action.CREATE_SHORTCUT" />
+                <action android:name="android.intent.action.CREATE_SHORTCUT"/>
             </intent-filter>
         </activity-alias>
 
         <activity-alias android:name="a.DisabledMain"
-            android:targetActivity="com.android.server.pm.ShortcutTestActivity"
-            android:enabled="false" android:exported="true">
+             android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+             android:enabled="false"
+             android:exported="true">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.LAUNCHER" />
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity-alias>
 
         <activity-alias android:name="a.UnexportedMain"
-            android:targetActivity="com.android.server.pm.ShortcutTestActivity"
-            android:enabled="true" android:exported="false">
+             android:targetActivity="com.android.server.pm.ShortcutTestActivity"
+             android:enabled="true"
+             android:exported="false">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.LAUNCHER" />
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity-alias>
 
         <receiver android:name="com.android.server.appwidget.DummyAppWidget">
             <intent-filter>
-                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
             </intent-filter>
             <meta-data android:name="android.appwidget.provider"
-              android:resource="@xml/dummy_appwidget_info" />
+                 android:resource="@xml/dummy_appwidget_info"/>
         </receiver>
     </application>
 
-    <instrumentation
-        android:name="androidx.test.runner.AndroidJUnitRunner"
-        android:targetPackage="com.android.frameworks.servicestests"
-        android:label="Frameworks Services Tests" />
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+         android:targetPackage="com.android.frameworks.servicestests"
+         android:label="Frameworks Services Tests"/>
 </manifest>
diff --git a/services/tests/servicestests/src/com/android/server/MountServiceTests.java b/services/tests/servicestests/src/com/android/server/MountServiceTests.java
deleted file mode 100644
index b1b3174..0000000
--- a/services/tests/servicestests/src/com/android/server/MountServiceTests.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.os.FileUtils;
-import android.os.storage.OnObbStateChangeListener;
-import android.os.storage.StorageManager;
-import android.test.AndroidTestCase;
-import android.test.ComparisonFailure;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
-
-import com.android.frameworks.servicestests.R;
-
-import java.io.File;
-import java.io.InputStream;
-
-public class MountServiceTests extends AndroidTestCase {
-    private static final String TAG = "MountServiceTests";
-
-    private static final long MAX_WAIT_TIME = 25*1000;
-    private static final long WAIT_TIME_INCR = 5*1000;
-
-    private static final String OBB_MOUNT_PREFIX = "/mnt/obb/";
-
-    private static void assertStartsWith(String message, String prefix, String actual) {
-        if (!actual.startsWith(prefix)) {
-            throw new ComparisonFailure(message, prefix, actual);
-        }
-    }
-
-    private static class ObbObserver extends OnObbStateChangeListener {
-        private String path;
-
-        public int state = -1;
-        boolean done = false;
-
-        @Override
-        public void onObbStateChange(String path, int state) {
-            Log.d(TAG, "Received message.  path=" + path + ", state=" + state);
-            synchronized (this) {
-                this.path = path;
-                this.state = state;
-                done = true;
-                notifyAll();
-            }
-        }
-
-        public String getPath() {
-            assertTrue("Expected ObbObserver to have received a state change.", done);
-            return path;
-        }
-
-        public int getState() {
-            assertTrue("Expected ObbObserver to have received a state change.", done);
-            return state;
-        }
-
-        public void reset() {
-            this.path = null;
-            this.state = -1;
-            done = false;
-        }
-
-        public boolean isDone() {
-            return done;
-        }
-
-        public boolean waitForCompletion() {
-            long waitTime = 0;
-            synchronized (this) {
-                while (!isDone() && waitTime < MAX_WAIT_TIME) {
-                    try {
-                        wait(WAIT_TIME_INCR);
-                        waitTime += WAIT_TIME_INCR;
-                    } catch (InterruptedException e) {
-                        Log.i(TAG, "Interrupted during sleep", e);
-                    }
-                }
-            }
-
-            return isDone();
-        }
-    }
-
-    private File getFilePath(String name) {
-        final File filesDir = mContext.getFilesDir();
-        final File outFile = new File(filesDir, name);
-        return outFile;
-    }
-
-    private void copyRawToFile(int rawResId, File outFile) {
-        Resources res = mContext.getResources();
-        InputStream is = null;
-        try {
-            is = res.openRawResource(rawResId);
-        } catch (NotFoundException e) {
-            fail("Failed to load resource with id: " + rawResId);
-        }
-        FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
-                | FileUtils.S_IRWXO, -1, -1);
-        assertTrue(FileUtils.copyToFile(is, outFile));
-        FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
-                | FileUtils.S_IRWXO, -1, -1);
-    }
-
-    private StorageManager getStorageManager() {
-        return (StorageManager) getContext().getSystemService(Context.STORAGE_SERVICE);
-    }
-
-    private void mountObb(StorageManager sm, final int resource, final File file,
-            int expectedState) {
-        copyRawToFile(resource, file);
-
-        final ObbObserver observer = new ObbObserver();
-        assertTrue("mountObb call on " + file.getPath() + " should succeed",
-                sm.mountObb(file.getPath(), null, observer));
-
-        assertTrue("Mount should have completed",
-                observer.waitForCompletion());
-
-        if (expectedState == OnObbStateChangeListener.MOUNTED) {
-            assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath()));
-        }
-
-        assertEquals("Actual file and resolved file should be the same",
-                file.getPath(), observer.getPath());
-
-        assertEquals(expectedState, observer.getState());
-    }
-
-    private ObbObserver mountObbWithoutWait(final StorageManager sm, final int resource,
-            final File file) {
-        copyRawToFile(resource, file);
-
-        final ObbObserver observer = new ObbObserver();
-        assertTrue("mountObb call on " + file.getPath() + " should succeed", sm.mountObb(file
-                .getPath(), null, observer));
-
-        return observer;
-    }
-
-    private void waitForObbActionCompletion(final StorageManager sm, final File file,
-            final ObbObserver observer, int expectedState, boolean checkPath) {
-        assertTrue("Mount should have completed", observer.waitForCompletion());
-
-        assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath()));
-
-        if (checkPath) {
-            assertEquals("Actual file and resolved file should be the same", file.getPath(),
-                    observer.getPath());
-        }
-
-        assertEquals(expectedState, observer.getState());
-    }
-
-    private String checkMountedPath(final StorageManager sm, final File file) {
-        final String mountPath = sm.getMountedObbPath(file.getPath());
-        assertStartsWith("Path should be in " + OBB_MOUNT_PREFIX,
-                OBB_MOUNT_PREFIX,
-                mountPath);
-        return mountPath;
-    }
-
-    private void unmountObb(final StorageManager sm, final File file, int expectedState) {
-        final ObbObserver observer = new ObbObserver();
-
-        assertTrue("unmountObb call on test1.obb should succeed",
-                sm.unmountObb(file.getPath(), false, observer));
-
-        assertTrue("Unmount should have completed",
-                observer.waitForCompletion());
-
-        assertEquals(expectedState, observer.getState());
-
-        if (expectedState == OnObbStateChangeListener.UNMOUNTED) {
-            assertFalse("OBB should not be mounted", sm.isObbMounted(file.getPath()));
-        }
-    }
-
-    @LargeTest
-    public void testMountAndUnmountObbNormal() {
-        StorageManager sm = getStorageManager();
-
-        final File outFile = getFilePath("test1.obb");
-
-        mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.MOUNTED);
-
-        mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
-
-        final String mountPath = checkMountedPath(sm, outFile);
-        final File mountDir = new File(mountPath);
-
-        assertTrue("OBB mounted path should be a directory",
-                mountDir.isDirectory());
-
-        unmountObb(sm, outFile, OnObbStateChangeListener.UNMOUNTED);
-    }
-
-    @LargeTest
-    public void testAttemptMountNonObb() {
-        StorageManager sm = getStorageManager();
-
-        final File outFile = getFilePath("test1_nosig.obb");
-
-        try {
-            mountObb(sm, R.raw.test1_nosig, outFile, OnObbStateChangeListener.ERROR_INTERNAL);
-            fail("mountObb should've failed with an exception");
-        } catch (IllegalArgumentException e) {
-            // Expected
-        }
-
-        assertFalse("OBB should not be mounted",
-                sm.isObbMounted(outFile.getPath()));
-
-        assertNull("OBB's mounted path should be null",
-                sm.getMountedObbPath(outFile.getPath()));
-    }
-
-    @LargeTest
-    public void testAttemptMountObbWrongPackage() {
-        StorageManager sm = getStorageManager();
-
-        final File outFile = getFilePath("test1_wrongpackage.obb");
-
-        mountObb(sm, R.raw.test1_wrongpackage, outFile,
-                OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
-
-        assertFalse("OBB should not be mounted",
-                sm.isObbMounted(outFile.getPath()));
-
-        assertNull("OBB's mounted path should be null",
-                sm.getMountedObbPath(outFile.getPath()));
-    }
-
-    @LargeTest
-    public void testMountAndUnmountTwoObbs() {
-        StorageManager sm = getStorageManager();
-
-        final File file1 = getFilePath("test1.obb");
-        final File file2 = getFilePath("test2.obb");
-
-        ObbObserver oo1 = mountObbWithoutWait(sm, R.raw.test1, file1);
-        ObbObserver oo2 = mountObbWithoutWait(sm, R.raw.test1, file2);
-
-        Log.d(TAG, "Waiting for OBB #1 to complete mount");
-        waitForObbActionCompletion(sm, file1, oo1, OnObbStateChangeListener.MOUNTED, false);
-        Log.d(TAG, "Waiting for OBB #2 to complete mount");
-        waitForObbActionCompletion(sm, file2, oo2, OnObbStateChangeListener.MOUNTED, false);
-
-        final String mountPath1 = checkMountedPath(sm, file1);
-        final File mountDir1 = new File(mountPath1);
-        assertTrue("OBB mounted path should be a directory", mountDir1.isDirectory());
-
-        final String mountPath2 = checkMountedPath(sm, file2);
-        final File mountDir2 = new File(mountPath2);
-        assertTrue("OBB mounted path should be a directory", mountDir2.isDirectory());
-
-        unmountObb(sm, file1, OnObbStateChangeListener.UNMOUNTED);
-        unmountObb(sm, file2, OnObbStateChangeListener.UNMOUNTED);
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 3ad9054..d292526 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -64,10 +64,13 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.AdditionalMatchers;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Random;
+
 @SmallTest
 public class BiometricServiceTest {
 
@@ -347,9 +350,19 @@
     }
 
     @Test
-    public void testAuthenticate_happyPathWithoutConfirmation() throws Exception {
+    public void testAuthenticate_happyPathWithoutConfirmation_strongBiometric() throws Exception {
         setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+        testAuthenticate_happyPathWithoutConfirmation(true /* isStrongBiometric */);
+    }
 
+    @Test
+    public void testAuthenticate_happyPathWithoutConfirmation_weakBiometric() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_WEAK);
+        testAuthenticate_happyPathWithoutConfirmation(false /* isStrongBiometric */);
+    }
+
+    private void testAuthenticate_happyPathWithoutConfirmation(boolean isStrongBiometric)
+            throws Exception {
         // Start testing the happy path
         invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
                 null /* authenticators */);
@@ -397,9 +410,11 @@
                 anyLong() /* sessionId */);
 
         // Hardware authenticated
+        final byte[] HAT = generateRandomHAT();
         mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
                 false /* requireConfirmation */,
-                new byte[69] /* HAT */);
+                HAT,
+                isStrongBiometric /* isStrongBiometric */);
         waitForIdle();
         // Waiting for SystemUI to send dismissed callback
         assertEquals(mBiometricService.mCurrentAuthSession.mState,
@@ -413,7 +428,11 @@
                 null /* credentialAttestation */);
         waitForIdle();
         // HAT sent to keystore
-        verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
+        if (isStrongBiometric) {
+            verify(mBiometricService.mKeyStore).addAuthToken(AdditionalMatchers.aryEq(HAT));
+        } else {
+            verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class));
+        }
         // Send onAuthenticated to client
         verify(mReceiver1).onAuthenticationSucceeded(
                 BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
@@ -447,16 +466,29 @@
     }
 
     @Test
-    public void testAuthenticate_happyPathWithConfirmation() throws Exception {
+    public void testAuthenticate_happyPathWithConfirmation_strongBiometric() throws Exception {
         setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
+        testAuthenticate_happyPathWithConfirmation(true /* isStrongBiometric */);
+    }
+
+    @Test
+    public void testAuthenticate_happyPathWithConfirmation_weakBiometric() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_WEAK);
+        testAuthenticate_happyPathWithConfirmation(false /* isStrongBiometric */);
+    }
+
+    private void testAuthenticate_happyPathWithConfirmation(boolean isStrongBiometric)
+            throws Exception {
         invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
                 true /* requireConfirmation */, null /* authenticators */);
 
         // Test authentication succeeded goes to PENDING_CONFIRMATION and that the HAT is not
         // sent to KeyStore yet
+        final byte[] HAT = generateRandomHAT();
         mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
                 true /* requireConfirmation */,
-                new byte[69] /* HAT */);
+                HAT,
+                isStrongBiometric /* isStrongBiometric */);
         waitForIdle();
         // Waiting for SystemUI to send confirmation callback
         assertEquals(mBiometricService.mCurrentAuthSession.mState,
@@ -468,7 +500,11 @@
                 BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED,
                 null /* credentialAttestation */);
         waitForIdle();
-        verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
+        if (isStrongBiometric) {
+            verify(mBiometricService.mKeyStore).addAuthToken(AdditionalMatchers.aryEq(HAT));
+        } else {
+            verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class));
+        }
         verify(mReceiver1).onAuthenticationSucceeded(
                 BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC);
     }
@@ -909,7 +945,8 @@
 
         mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
                 true /* requireConfirmation */,
-                new byte[69] /* HAT */);
+                new byte[69] /* HAT */,
+                true /* isStrongBiometric */);
         mBiometricService.mInternalReceiver.onDialogDismissed(
                 BiometricPrompt.DISMISSED_REASON_USER_CANCEL, null /* credentialAttestation */);
         waitForIdle();
@@ -927,6 +964,7 @@
                 eq(BiometricAuthenticator.TYPE_FACE),
                 eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
                 eq(0 /* vendorCode */));
+        verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class));
         assertNull(mBiometricService.mCurrentAuthSession);
     }
 
@@ -1238,20 +1276,6 @@
                 mFingerprintAuthenticator);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testRegistrationWithUnsupportedStrength_throwsIllegalStateException()
-            throws Exception {
-        mBiometricService = new BiometricService(mContext, mInjector);
-        mBiometricService.onStart();
-
-        // Only STRONG and WEAK are supported. Let's enforce that CONVENIENCE cannot be
-        // registered. If there is a compelling reason, we can remove this constraint.
-        mBiometricService.mImpl.registerAuthenticator(
-                0 /* id */, 2 /* modality */,
-                Authenticators.BIOMETRIC_CONVENIENCE /* strength */,
-                mFingerprintAuthenticator);
-    }
-
     @Test(expected = IllegalArgumentException.class)
     public void testRegistrationWithNullAuthenticator_throwsIllegalArgumentException()
             throws Exception {
@@ -1508,4 +1532,13 @@
     private static void waitForIdle() {
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
     }
+
+    private byte[] generateRandomHAT() {
+        byte[] HAT = new byte[69];
+        Random random = new Random();
+        random.nextBytes(HAT);
+        return HAT;
+    }
+
+
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 7c6ac17..baf551e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3205,6 +3205,7 @@
         when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
                 .thenReturn(true);
         setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM);
+        when(getServices().userManager.getProfileParent(UserHandle.USER_SYSTEM)).thenReturn(null);
 
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
     }
@@ -3246,6 +3247,7 @@
         when(getServices().userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true))
                 .thenReturn(true);
         setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM);
+        when(getServices().userManager.getProfileParent(UserHandle.USER_SYSTEM)).thenReturn(null);
 
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
     }
@@ -3617,14 +3619,14 @@
 
         when(getServices().ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0))
                 .thenReturn(true);
-        when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(true);
+        when(getServices().userManagerForMock.isSplitSystemUser()).thenReturn(false);
         when(getServices().userManager.getProfileParent(DpmMockContext.CALLER_USER_HANDLE))
             .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
         when(getServices().userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
                 true)).thenReturn(true);
         setUserSetupCompleteForUser(false, DpmMockContext.CALLER_USER_HANDLE);
 
-        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
     }
 
     public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_primaryUser()
diff --git a/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
index a99c982..be2a5c5 100644
--- a/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
@@ -61,17 +61,9 @@
 
 import com.android.server.LocalServices;
 import com.android.server.location.AppForegroundHelper;
-import com.android.server.location.GnssAntennaInfoProvider;
-import com.android.server.location.GnssAntennaInfoProvider.GnssAntennaInfoProviderNative;
-import com.android.server.location.GnssBatchingProvider;
-import com.android.server.location.GnssCapabilitiesProvider;
-import com.android.server.location.GnssLocationProvider;
-import com.android.server.location.GnssMeasurementCorrectionsProvider;
-import com.android.server.location.GnssMeasurementsProvider;
-import com.android.server.location.GnssMeasurementsProvider.GnssMeasurementProviderNative;
-import com.android.server.location.GnssNavigationMessageProvider;
-import com.android.server.location.GnssNavigationMessageProvider.GnssNavigationMessageProviderNative;
-import com.android.server.location.GnssStatusListenerHelper;
+import com.android.server.location.gnss.GnssAntennaInfoProvider.GnssAntennaInfoProviderNative;
+import com.android.server.location.gnss.GnssMeasurementsProvider.GnssMeasurementProviderNative;
+import com.android.server.location.gnss.GnssNavigationMessageProvider.GnssNavigationMessageProviderNative;
 import com.android.server.location.LocationUsageLogger;
 import com.android.server.location.SettingsHelper;
 
diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
index 7934d33..03d9ad5 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
@@ -21,15 +21,16 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.Context;
 import android.content.LocusId;
@@ -50,6 +51,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.function.Predicate;
@@ -58,7 +60,8 @@
 public final class UsageStatsQueryHelperTest {
 
     private static final int USER_ID_PRIMARY = 0;
-    private static final String PKG_NAME = "pkg";
+    private static final String PKG_NAME_1 = "pkg_1";
+    private static final String PKG_NAME_2 = "pkg_2";
     private static final String ACTIVITY_NAME = "TestActivity";
     private static final String SHORTCUT_ID = "abc";
     private static final LocusId LOCUS_ID_1 = new LocusId("locus_1");
@@ -80,7 +83,7 @@
         File testDir = new File(ctx.getCacheDir(), "testdir");
         ScheduledExecutorService scheduledExecutorService = new MockScheduledExecutorService();
 
-        mPackageData = new TestPackageData(PKG_NAME, USER_ID_PRIMARY, pkg -> false, pkg -> false,
+        mPackageData = new TestPackageData(PKG_NAME_1, USER_ID_PRIMARY, pkg -> false, pkg -> false,
                 scheduledExecutorService, testDir);
         mPackageData.mConversationStore.mConversationInfo = new ConversationInfo.Builder()
                 .setShortcutId(SHORTCUT_ID)
@@ -173,10 +176,72 @@
         assertEquals(createInAppConversationEvent(130_000L, 30), events.get(2));
     }
 
+    @Test
+    public void testQueryAppMovingToForegroundEvents() {
+        addUsageEvents(
+                createShortcutInvocationEvent(100_000L),
+                createActivityResumedEvent(110_000L),
+                createActivityStoppedEvent(120_000L),
+                createActivityResumedEvent(130_000L));
+
+        List<UsageEvents.Event> events = mHelper.queryAppMovingToForegroundEvents(USER_ID_PRIMARY,
+                90_000L,
+                200_000L);
+
+        assertEquals(2, events.size());
+        assertEquals(UsageEvents.Event.ACTIVITY_RESUMED, events.get(0).getEventType());
+        assertEquals(110_000L, events.get(0).getTimeStamp());
+        assertEquals(UsageEvents.Event.ACTIVITY_RESUMED, events.get(1).getEventType());
+        assertEquals(130_000L, events.get(1).getTimeStamp());
+    }
+
+    @Test
+    public void testQueryAppLaunchCount() {
+
+        UsageStats packageStats1 = createUsageStats(PKG_NAME_1, 2);
+        UsageStats packageStats2 = createUsageStats(PKG_NAME_1, 3);
+        UsageStats packageStats3 = createUsageStats(PKG_NAME_2, 1);
+        when(mUsageStatsManagerInternal.queryUsageStatsForUser(anyInt(), anyInt(), anyLong(),
+                anyLong(), anyBoolean())).thenReturn(
+                List.of(packageStats1, packageStats2, packageStats3));
+
+        Map<String, Integer> appLaunchCounts = mHelper.queryAppLaunchCount(USER_ID_PRIMARY, 90_000L,
+                200_000L, Set.of(PKG_NAME_1, PKG_NAME_2));
+
+        assertEquals(2, appLaunchCounts.size());
+        assertEquals(5, (long) appLaunchCounts.get(PKG_NAME_1));
+        assertEquals(1, (long) appLaunchCounts.get(PKG_NAME_2));
+    }
+
+    @Test
+    public void testQueryAppLaunchCount_packageNameFiltered() {
+
+        UsageStats packageStats1 = createUsageStats(PKG_NAME_1, 2);
+        UsageStats packageStats2 = createUsageStats(PKG_NAME_1, 3);
+        UsageStats packageStats3 = createUsageStats(PKG_NAME_2, 1);
+        when(mUsageStatsManagerInternal.queryUsageStatsForUser(anyInt(), anyInt(), anyLong(),
+                anyLong(), anyBoolean())).thenReturn(
+                List.of(packageStats1, packageStats2, packageStats3));
+
+        Map<String, Integer> appLaunchCounts = mHelper.queryAppLaunchCount(USER_ID_PRIMARY, 90_000L,
+                200_000L,
+                Set.of(PKG_NAME_1));
+
+        assertEquals(1, appLaunchCounts.size());
+        assertEquals(5, (long) appLaunchCounts.get(PKG_NAME_1));
+    }
+
     private void addUsageEvents(UsageEvents.Event... events) {
         UsageEvents usageEvents = new UsageEvents(Arrays.asList(events), new String[]{});
         when(mUsageStatsManagerInternal.queryEventsForUser(anyInt(), anyLong(), anyLong(),
-                eq(UsageEvents.SHOW_ALL_EVENT_DATA))).thenReturn(usageEvents);
+                anyInt())).thenReturn(usageEvents);
+    }
+
+    private static UsageStats createUsageStats(String packageName, int launchCount) {
+        UsageStats packageStats = new UsageStats();
+        packageStats.mPackageName = packageName;
+        packageStats.mAppLaunchCount = launchCount;
+        return packageStats;
     }
 
     private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
@@ -203,9 +268,15 @@
         return e;
     }
 
+    private static UsageEvents.Event createActivityResumedEvent(long timestamp) {
+        UsageEvents.Event e = createUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, timestamp);
+        e.mClass = ACTIVITY_NAME;
+        return e;
+    }
+
     private static UsageEvents.Event createUsageEvent(int eventType, long timestamp) {
         UsageEvents.Event e = new UsageEvents.Event(eventType, timestamp);
-        e.mPackage = PKG_NAME;
+        e.mPackage = PKG_NAME_1;
         return e;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
index c6cd347..1480627 100644
--- a/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/ShareTargetPredictorTest.java
@@ -127,6 +127,9 @@
         when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
         when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
         when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+        when(mEventHistory1.getEventIndex(anyInt())).thenReturn(mEventIndex1);
+        when(mEventHistory2.getEventIndex(anyInt())).thenReturn(mEventIndex2);
+        when(mEventHistory3.getEventIndex(anyInt())).thenReturn(mEventIndex3);
         when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L));
         when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L));
         when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L));
@@ -183,6 +186,12 @@
         when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4);
         when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5);
         when(mEventHistory6.getEventIndex(anySet())).thenReturn(mEventIndex6);
+        when(mEventHistory1.getEventIndex(anyInt())).thenReturn(mEventIndex1);
+        when(mEventHistory2.getEventIndex(anyInt())).thenReturn(mEventIndex2);
+        when(mEventHistory3.getEventIndex(anyInt())).thenReturn(mEventIndex3);
+        when(mEventHistory4.getEventIndex(anyInt())).thenReturn(mEventIndex4);
+        when(mEventHistory5.getEventIndex(anyInt())).thenReturn(mEventIndex5);
+        when(mEventHistory6.getEventIndex(anyInt())).thenReturn(mEventIndex6);
         when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L));
         when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L));
         when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L));
@@ -220,19 +229,19 @@
     @Test
     public void testSortTargets() {
         AppTarget appTarget1 = new AppTarget.Builder(
-                    new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID))
+                new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID))
                 .setClassName(CLASS_1)
                 .build();
         AppTarget appTarget2 = new AppTarget.Builder(
-                    new AppTargetId("cls2#pkg1"), PACKAGE_1, UserHandle.of(USER_ID))
+                new AppTargetId("cls2#pkg1"), PACKAGE_1, UserHandle.of(USER_ID))
                 .setClassName(CLASS_2)
                 .build();
         AppTarget appTarget3 = new AppTarget.Builder(
-                    new AppTargetId("cls1#pkg2"), PACKAGE_2, UserHandle.of(USER_ID))
+                new AppTargetId("cls1#pkg2"), PACKAGE_2, UserHandle.of(USER_ID))
                 .setClassName(CLASS_1)
                 .build();
         AppTarget appTarget4 = new AppTarget.Builder(
-                    new AppTargetId("cls2#pkg2"), PACKAGE_2, UserHandle.of(USER_ID))
+                new AppTargetId("cls2#pkg2"), PACKAGE_2, UserHandle.of(USER_ID))
                 .setClassName(CLASS_2)
                 .build();
         AppTarget appTarget5 = new AppTarget.Builder(
@@ -251,6 +260,10 @@
         when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
         when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
         when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4);
+        when(mEventHistory1.getEventIndex(anyInt())).thenReturn(mEventIndex1);
+        when(mEventHistory2.getEventIndex(anyInt())).thenReturn(mEventIndex2);
+        when(mEventHistory3.getEventIndex(anyInt())).thenReturn(mEventIndex3);
+        when(mEventHistory4.getEventIndex(anyInt())).thenReturn(mEventIndex4);
         when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(1L, 2L));
         when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(2L, 3L));
         when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(new Range<>(3L, 4L));
@@ -265,14 +278,14 @@
                 appTarget4, appTarget3, appTarget2, appTarget1, appTarget5);
     }
 
-    private ShareShortcutInfo buildShareShortcut(
+    private static ShareShortcutInfo buildShareShortcut(
             String packageName, String className, String shortcutId) {
         ShortcutInfo shortcutInfo = buildShortcut(packageName, shortcutId);
         ComponentName componentName = new ComponentName(packageName, className);
         return new ShareShortcutInfo(shortcutInfo, componentName);
     }
 
-    private ShortcutInfo buildShortcut(String packageName, String shortcutId) {
+    private static ShortcutInfo buildShortcut(String packageName, String shortcutId) {
         Context mockContext = mock(Context.class);
         when(mockContext.getPackageName()).thenReturn(packageName);
         when(mockContext.getUserId()).thenReturn(USER_ID);
diff --git a/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java b/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java
new file mode 100644
index 0000000..9d96d6b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/prediction/SharesheetModelScorerTest.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2020 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.people.prediction;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anySet;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetId;
+import android.app.usage.UsageEvents;
+import android.os.UserHandle;
+import android.util.Range;
+
+import com.android.server.people.data.DataManager;
+import com.android.server.people.data.Event;
+import com.android.server.people.data.EventHistory;
+import com.android.server.people.data.EventIndex;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(JUnit4.class)
+public final class SharesheetModelScorerTest {
+
+    private static final int USER_ID = 0;
+    private static final String PACKAGE_1 = "pkg1";
+    private static final String PACKAGE_2 = "pkg2";
+    private static final String PACKAGE_3 = "pkg3";
+    private static final String CLASS_1 = "cls1";
+    private static final String CLASS_2 = "cls2";
+    private static final double DELTA = 1e-6;
+    private static final long NOW = System.currentTimeMillis();
+    private static final Range<Long> WITHIN_ONE_DAY = new Range(
+            NOW - Duration.ofHours(23).toMillis(),
+            NOW - Duration.ofHours(22).toMillis());
+    private static final Range<Long> TWO_DAYS_AGO = new Range(
+            NOW - Duration.ofHours(50).toMillis(),
+            NOW - Duration.ofHours(49).toMillis());
+    private static final Range<Long> FIVE_DAYS_AGO = new Range(
+            NOW - Duration.ofDays(6).toMillis(),
+            NOW - Duration.ofDays(5).toMillis());
+    private static final Range<Long> EIGHT_DAYS_AGO = new Range(
+            NOW - Duration.ofDays(9).toMillis(),
+            NOW - Duration.ofDays(8).toMillis());
+    private static final Range<Long> TWELVE_DAYS_AGO = new Range(
+            NOW - Duration.ofDays(13).toMillis(),
+            NOW - Duration.ofDays(12).toMillis());
+    private static final Range<Long> TWENTY_DAYS_AGO = new Range(
+            NOW - Duration.ofDays(21).toMillis(),
+            NOW - Duration.ofDays(20).toMillis());
+    private static final Range<Long> FOUR_WEEKS_AGO = new Range(
+            NOW - Duration.ofDays(29).toMillis(),
+            NOW - Duration.ofDays(28).toMillis());
+
+    @Mock
+    private DataManager mDataManager;
+    @Mock
+    private EventHistory mEventHistory1;
+    @Mock
+    private EventHistory mEventHistory2;
+    @Mock
+    private EventHistory mEventHistory3;
+    @Mock
+    private EventHistory mEventHistory4;
+    @Mock
+    private EventHistory mEventHistory5;
+    @Mock
+    private EventIndex mEventIndex1;
+    @Mock
+    private EventIndex mEventIndex2;
+    @Mock
+    private EventIndex mEventIndex3;
+    @Mock
+    private EventIndex mEventIndex4;
+    @Mock
+    private EventIndex mEventIndex5;
+    @Mock
+    private EventIndex mEventIndex6;
+    @Mock
+    private EventIndex mEventIndex7;
+    @Mock
+    private EventIndex mEventIndex8;
+    @Mock
+    private EventIndex mEventIndex9;
+    @Mock
+    private EventIndex mEventIndex10;
+
+    private ShareTargetPredictor.ShareTarget mShareTarget1;
+    private ShareTargetPredictor.ShareTarget mShareTarget2;
+    private ShareTargetPredictor.ShareTarget mShareTarget3;
+    private ShareTargetPredictor.ShareTarget mShareTarget4;
+    private ShareTargetPredictor.ShareTarget mShareTarget5;
+    private ShareTargetPredictor.ShareTarget mShareTarget6;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mShareTarget1 = new ShareTargetPredictor.ShareTarget(
+                new AppTarget.Builder(
+                        new AppTargetId("cls1#pkg1"), PACKAGE_1, UserHandle.of(USER_ID))
+                        .setClassName(CLASS_1).build(),
+                mEventHistory1, null);
+        mShareTarget2 = new ShareTargetPredictor.ShareTarget(
+                new AppTarget.Builder(new AppTargetId("cls2#pkg1"), PACKAGE_1,
+                        UserHandle.of(USER_ID)).setClassName(CLASS_2).build(),
+                mEventHistory2, null);
+        mShareTarget3 = new ShareTargetPredictor.ShareTarget(
+                new AppTarget.Builder(
+                        new AppTargetId("cls1#pkg2"), PACKAGE_2, UserHandle.of(USER_ID))
+                        .setClassName(CLASS_1).build(),
+                mEventHistory3, null);
+        mShareTarget4 = new ShareTargetPredictor.ShareTarget(
+                new AppTarget.Builder(
+                        new AppTargetId("cls2#pkg2"), PACKAGE_2, UserHandle.of(USER_ID))
+                        .setClassName(CLASS_2).build(),
+                mEventHistory4, null);
+        mShareTarget5 = new ShareTargetPredictor.ShareTarget(
+                new AppTarget.Builder(
+                        new AppTargetId("cls1#pkg3"), PACKAGE_3, UserHandle.of(USER_ID))
+                        .setClassName(CLASS_1).build(),
+                mEventHistory5, null);
+        mShareTarget6 = new ShareTargetPredictor.ShareTarget(
+                new AppTarget.Builder(
+                        new AppTargetId("cls2#pkg3"), PACKAGE_3, UserHandle.of(USER_ID))
+                        .setClassName(CLASS_2).build(),
+                null, null);
+    }
+
+    @Test
+    public void testComputeScore() {
+        // Frequency and recency
+        when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+        when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+        when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+        when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4);
+        when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5);
+
+        when(mEventIndex1.getActiveTimeSlots()).thenReturn(
+                List.of(WITHIN_ONE_DAY, TWO_DAYS_AGO, FIVE_DAYS_AGO));
+        when(mEventIndex2.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO));
+        when(mEventIndex3.getActiveTimeSlots()).thenReturn(List.of(FIVE_DAYS_AGO, TWENTY_DAYS_AGO));
+        when(mEventIndex4.getActiveTimeSlots()).thenReturn(
+                List.of(EIGHT_DAYS_AGO, TWELVE_DAYS_AGO, FOUR_WEEKS_AGO));
+        when(mEventIndex5.getActiveTimeSlots()).thenReturn(List.of());
+
+        when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(WITHIN_ONE_DAY);
+        when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(TWO_DAYS_AGO);
+        when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(FIVE_DAYS_AGO);
+        when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(EIGHT_DAYS_AGO);
+        when(mEventIndex5.getMostRecentActiveTimeSlot()).thenReturn(null);
+
+        // Frequency of the same mime type
+        when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6);
+        when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7);
+        when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8);
+        when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9);
+        when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10);
+
+        when(mEventIndex6.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO));
+        when(mEventIndex7.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO));
+        when(mEventIndex8.getActiveTimeSlots()).thenReturn(List.of());
+        when(mEventIndex9.getActiveTimeSlots()).thenReturn(List.of(EIGHT_DAYS_AGO));
+        when(mEventIndex10.getActiveTimeSlots()).thenReturn(List.of());
+
+        SharesheetModelScorer.computeScore(
+                List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5,
+                        mShareTarget6),
+                Event.TYPE_SHARE_TEXT,
+                NOW);
+
+        // Verification
+        assertEquals(0.514f, mShareTarget1.getScore(), DELTA);
+        assertEquals(0.475125f, mShareTarget2.getScore(), DELTA);
+        assertEquals(0.33f, mShareTarget3.getScore(), DELTA);
+        assertEquals(0.4411f, mShareTarget4.getScore(), DELTA);
+        assertEquals(0f, mShareTarget5.getScore(), DELTA);
+        assertEquals(0f, mShareTarget6.getScore(), DELTA);
+    }
+
+    @Test
+    public void testComputeScoreForAppShare() {
+        // Frequency and recency
+        when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+        when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+        when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+        when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4);
+        when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5);
+
+        when(mEventIndex1.getActiveTimeSlots()).thenReturn(
+                List.of(WITHIN_ONE_DAY, TWO_DAYS_AGO, FIVE_DAYS_AGO));
+        when(mEventIndex2.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO));
+        when(mEventIndex3.getActiveTimeSlots()).thenReturn(List.of(FIVE_DAYS_AGO, TWENTY_DAYS_AGO));
+        when(mEventIndex4.getActiveTimeSlots()).thenReturn(
+                List.of(EIGHT_DAYS_AGO, TWELVE_DAYS_AGO, FOUR_WEEKS_AGO));
+        when(mEventIndex5.getActiveTimeSlots()).thenReturn(List.of());
+
+        when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(WITHIN_ONE_DAY);
+        when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(TWO_DAYS_AGO);
+        when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(FIVE_DAYS_AGO);
+        when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(EIGHT_DAYS_AGO);
+        when(mEventIndex5.getMostRecentActiveTimeSlot()).thenReturn(null);
+
+        // Frequency of the same mime type
+        when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6);
+        when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7);
+        when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8);
+        when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9);
+        when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10);
+
+        when(mEventIndex6.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO));
+        when(mEventIndex7.getActiveTimeSlots()).thenReturn(List.of(TWO_DAYS_AGO, TWELVE_DAYS_AGO));
+        when(mEventIndex8.getActiveTimeSlots()).thenReturn(List.of());
+        when(mEventIndex9.getActiveTimeSlots()).thenReturn(List.of(EIGHT_DAYS_AGO));
+        when(mEventIndex10.getActiveTimeSlots()).thenReturn(List.of());
+
+        SharesheetModelScorer.computeScoreForAppShare(
+                List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5,
+                        mShareTarget6),
+                Event.TYPE_SHARE_TEXT, 20, NOW, mDataManager, USER_ID);
+
+        // Verification
+        assertEquals(0.514f, mShareTarget1.getScore(), DELTA);
+        assertEquals(0.475125f, mShareTarget2.getScore(), DELTA);
+        assertEquals(0.33f, mShareTarget3.getScore(), DELTA);
+        assertEquals(0.4411f, mShareTarget4.getScore(), DELTA);
+        assertEquals(0f, mShareTarget5.getScore(), DELTA);
+        assertEquals(0f, mShareTarget6.getScore(), DELTA);
+    }
+
+    @Test
+    public void testComputeScoreForAppShare_promoteFrequentlyUsedApps() {
+        when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+        when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+        when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+        when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4);
+        when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5);
+        when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6);
+        when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7);
+        when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8);
+        when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9);
+        when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10);
+        when(mDataManager.queryAppLaunchCount(anyInt(), anyLong(), anyLong(), anySet()))
+                .thenReturn(
+                        Map.of(PACKAGE_1, 1,
+                                PACKAGE_2, 2,
+                                PACKAGE_3, 3));
+
+        SharesheetModelScorer.computeScoreForAppShare(
+                List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5,
+                        mShareTarget6),
+                Event.TYPE_SHARE_TEXT, 20, NOW, mDataManager, USER_ID);
+
+        verify(mDataManager, times(1)).queryAppLaunchCount(anyInt(), anyLong(), anyLong(),
+                anySet());
+        assertEquals(0.9f, mShareTarget5.getScore(), DELTA);
+        assertEquals(0.81f, mShareTarget3.getScore(), DELTA);
+        assertEquals(0.729f, mShareTarget1.getScore(), DELTA);
+        assertEquals(0f, mShareTarget2.getScore(), DELTA);
+        assertEquals(0f, mShareTarget4.getScore(), DELTA);
+        assertEquals(0f, mShareTarget6.getScore(), DELTA);
+    }
+
+    @Test
+    public void testComputeScoreForAppShare_skipPromoteFrequentlyUsedAppsWhenReachesLimit() {
+        when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+        when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+        when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+        when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4);
+        when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5);
+        when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6);
+        when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7);
+        when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8);
+        when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9);
+        when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10);
+        when(mEventIndex1.getMostRecentActiveTimeSlot()).thenReturn(WITHIN_ONE_DAY);
+        when(mEventIndex2.getMostRecentActiveTimeSlot()).thenReturn(TWO_DAYS_AGO);
+        when(mEventIndex3.getMostRecentActiveTimeSlot()).thenReturn(FIVE_DAYS_AGO);
+        when(mEventIndex4.getMostRecentActiveTimeSlot()).thenReturn(EIGHT_DAYS_AGO);
+        when(mEventIndex5.getMostRecentActiveTimeSlot()).thenReturn(null);
+        when(mDataManager.queryAppLaunchCount(anyInt(), anyLong(), anyLong(), anySet()))
+                .thenReturn(
+                        Map.of(PACKAGE_1, 1,
+                                PACKAGE_2, 2,
+                                PACKAGE_3, 3));
+
+        SharesheetModelScorer.computeScoreForAppShare(
+                List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5,
+                        mShareTarget6),
+                Event.TYPE_SHARE_TEXT, 4, NOW, mDataManager, USER_ID);
+
+        verify(mDataManager, never()).queryAppLaunchCount(anyInt(), anyLong(), anyLong(), anySet());
+        assertEquals(0.4f, mShareTarget1.getScore(), DELTA);
+        assertEquals(0.35f, mShareTarget2.getScore(), DELTA);
+        assertEquals(0.33f, mShareTarget3.getScore(), DELTA);
+        assertEquals(0.31f, mShareTarget4.getScore(), DELTA);
+        assertEquals(0f, mShareTarget5.getScore(), DELTA);
+        assertEquals(0f, mShareTarget6.getScore(), DELTA);
+    }
+
+    @Test
+    public void testComputeScoreForAppShare_promoteForegroundApp() {
+        when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+        when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+        when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+        when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4);
+        when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5);
+        when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6);
+        when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7);
+        when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8);
+        when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9);
+        when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10);
+        when(mDataManager.queryAppMovingToForegroundEvents(anyInt(), anyLong(),
+                anyLong())).thenReturn(
+                List.of(createUsageEvent(PACKAGE_2),
+                        createUsageEvent(PACKAGE_3),
+                        createUsageEvent(SharesheetModelScorer.CHOOSER_ACTIVITY),
+                        createUsageEvent(PACKAGE_3),
+                        createUsageEvent(PACKAGE_3))
+        );
+
+        SharesheetModelScorer.computeScoreForAppShare(
+                List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5,
+                        mShareTarget6),
+                Event.TYPE_SHARE_TEXT, 20, NOW, mDataManager, USER_ID);
+
+        verify(mDataManager, times(1)).queryAppMovingToForegroundEvents(anyInt(), anyLong(),
+                anyLong());
+        assertEquals(0f, mShareTarget1.getScore(), DELTA);
+        assertEquals(0f, mShareTarget2.getScore(), DELTA);
+        assertEquals(SharesheetModelScorer.FOREGROUND_APP_WEIGHT, mShareTarget3.getScore(), DELTA);
+        assertEquals(0f, mShareTarget4.getScore(), DELTA);
+        assertEquals(0f, mShareTarget5.getScore(), DELTA);
+        assertEquals(0f, mShareTarget6.getScore(), DELTA);
+    }
+
+    @Test
+    public void testComputeScoreForAppShare_skipPromoteForegroundAppWhenNoValidForegroundApp() {
+        when(mEventHistory1.getEventIndex(anySet())).thenReturn(mEventIndex1);
+        when(mEventHistory2.getEventIndex(anySet())).thenReturn(mEventIndex2);
+        when(mEventHistory3.getEventIndex(anySet())).thenReturn(mEventIndex3);
+        when(mEventHistory4.getEventIndex(anySet())).thenReturn(mEventIndex4);
+        when(mEventHistory5.getEventIndex(anySet())).thenReturn(mEventIndex5);
+        when(mEventHistory1.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex6);
+        when(mEventHistory2.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex7);
+        when(mEventHistory3.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex8);
+        when(mEventHistory4.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex9);
+        when(mEventHistory5.getEventIndex(Event.TYPE_SHARE_TEXT)).thenReturn(mEventIndex10);
+        when(mDataManager.queryAppMovingToForegroundEvents(anyInt(), anyLong(),
+                anyLong())).thenReturn(
+                List.of(createUsageEvent(PACKAGE_3),
+                        createUsageEvent(PACKAGE_3),
+                        createUsageEvent(SharesheetModelScorer.CHOOSER_ACTIVITY),
+                        createUsageEvent(PACKAGE_3),
+                        createUsageEvent(PACKAGE_3))
+        );
+
+        SharesheetModelScorer.computeScoreForAppShare(
+                List.of(mShareTarget1, mShareTarget2, mShareTarget3, mShareTarget4, mShareTarget5,
+                        mShareTarget6),
+                Event.TYPE_SHARE_TEXT, 20, NOW, mDataManager, USER_ID);
+
+        verify(mDataManager, times(1)).queryAppMovingToForegroundEvents(anyInt(), anyLong(),
+                anyLong());
+        assertEquals(0f, mShareTarget1.getScore(), DELTA);
+        assertEquals(0f, mShareTarget2.getScore(), DELTA);
+        assertEquals(0f, mShareTarget3.getScore(), DELTA);
+        assertEquals(0f, mShareTarget4.getScore(), DELTA);
+        assertEquals(0f, mShareTarget5.getScore(), DELTA);
+        assertEquals(0f, mShareTarget6.getScore(), DELTA);
+    }
+
+    private static UsageEvents.Event createUsageEvent(String packageName) {
+        UsageEvents.Event e = new UsageEvents.Event();
+        e.mPackage = packageName;
+        return e;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
index 40ada2a..db1bbab 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -43,6 +43,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.servicestests.R;
+import com.android.server.pm.parsing.PackageParser2;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -63,6 +64,7 @@
     private static final int[] TEST_CHILD_SESSION_ID = {8888, 7777};
     private ApexManager mApexManager;
     private Context mContext;
+    private PackageParser2 mPackageParser2;
 
     private IApexService mApexService = mock(IApexService.class);
 
@@ -70,11 +72,14 @@
     public void setUp() throws RemoteException {
         mContext = InstrumentationRegistry.getInstrumentation().getContext();
         mApexManager = new ApexManager.ApexManagerImpl(mApexService);
+        mPackageParser2 = new PackageParser2(null, false, null, null, null);
     }
 
     @Test
     public void testGetPackageInfo_setFlagsMatchActivePackage() throws RemoteException {
         when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false));
+        mApexManager.scanApexPackagesTraced(mPackageParser2,
+                ParallelPackageParser.makeExecutorService());
         final PackageInfo activePkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG,
                 ApexManager.MATCH_ACTIVE_PACKAGE);
 
@@ -90,6 +95,8 @@
     @Test
     public void testGetPackageInfo_setFlagsMatchFactoryPackage() throws RemoteException {
         when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true));
+        mApexManager.scanApexPackagesTraced(mPackageParser2,
+                ParallelPackageParser.makeExecutorService());
         PackageInfo factoryPkgPi = mApexManager.getPackageInfo(TEST_APEX_PKG,
                 ApexManager.MATCH_FACTORY_PACKAGE);
 
@@ -105,6 +112,8 @@
     @Test
     public void testGetPackageInfo_setFlagsNone() throws RemoteException {
         when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true));
+        mApexManager.scanApexPackagesTraced(mPackageParser2,
+                ParallelPackageParser.makeExecutorService());
 
         assertThat(mApexManager.getPackageInfo(TEST_APEX_PKG, 0)).isNull();
     }
@@ -112,6 +121,8 @@
     @Test
     public void testGetActivePackages() throws RemoteException {
         when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, true));
+        mApexManager.scanApexPackagesTraced(mPackageParser2,
+                ParallelPackageParser.makeExecutorService());
 
         assertThat(mApexManager.getActivePackages()).isNotEmpty();
     }
@@ -119,6 +130,8 @@
     @Test
     public void testGetActivePackages_noneActivePackages() throws RemoteException {
         when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true));
+        mApexManager.scanApexPackagesTraced(mPackageParser2,
+                ParallelPackageParser.makeExecutorService());
 
         assertThat(mApexManager.getActivePackages()).isEmpty();
     }
@@ -126,6 +139,8 @@
     @Test
     public void testGetFactoryPackages() throws RemoteException {
         when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true));
+        mApexManager.scanApexPackagesTraced(mPackageParser2,
+                ParallelPackageParser.makeExecutorService());
 
         assertThat(mApexManager.getFactoryPackages()).isNotEmpty();
     }
@@ -133,6 +148,8 @@
     @Test
     public void testGetFactoryPackages_noneFactoryPackages() throws RemoteException {
         when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false));
+        mApexManager.scanApexPackagesTraced(mPackageParser2,
+                ParallelPackageParser.makeExecutorService());
 
         assertThat(mApexManager.getFactoryPackages()).isEmpty();
     }
@@ -140,6 +157,8 @@
     @Test
     public void testGetInactivePackages() throws RemoteException {
         when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true));
+        mApexManager.scanApexPackagesTraced(mPackageParser2,
+                ParallelPackageParser.makeExecutorService());
 
         assertThat(mApexManager.getInactivePackages()).isNotEmpty();
     }
@@ -147,6 +166,8 @@
     @Test
     public void testGetInactivePackages_noneInactivePackages() throws RemoteException {
         when(mApexService.getAllPackages()).thenReturn(createApexInfo(true, false));
+        mApexManager.scanApexPackagesTraced(mPackageParser2,
+                ParallelPackageParser.makeExecutorService());
 
         assertThat(mApexManager.getInactivePackages()).isEmpty();
     }
@@ -154,6 +175,8 @@
     @Test
     public void testIsApexPackage() throws RemoteException {
         when(mApexService.getAllPackages()).thenReturn(createApexInfo(false, true));
+        mApexManager.scanApexPackagesTraced(mPackageParser2,
+                ParallelPackageParser.makeExecutorService());
 
         assertThat(mApexManager.isApexPackage(TEST_APEX_PKG)).isTrue();
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index a19d919..d760629 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -17,11 +17,15 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+
 import android.annotation.NonNull;
+import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
@@ -30,7 +34,6 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageUserState;
-import android.content.pm.ProviderInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.Signature;
 import android.content.pm.parsing.ParsingPackage;
@@ -49,6 +52,7 @@
 import android.util.DisplayMetrics;
 
 import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -69,9 +73,11 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.lang.reflect.Array;
 import java.lang.reflect.Field;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
@@ -90,6 +96,9 @@
 
     private File mTmpDir;
     private static final File FRAMEWORK = new File("/system/framework/framework-res.apk");
+    private static final String TEST_APP1_APK = "PackageParserTestApp1.apk";
+    private static final String TEST_APP2_APK = "PackageParserTestApp2.apk";
+    private static final String TEST_APP3_APK = "PackageParserTestApp3.apk";
 
     @Before
     public void setUp() throws IOException {
@@ -209,6 +218,61 @@
         assertSame(deserialized.getSharedUserId(), deserialized2.getSharedUserId());
     }
 
+    private static PackageParser2 makeParser() {
+        return new PackageParser2(null, false, null, null, null);
+    }
+
+    private File extractFile(String filename) throws Exception {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final File tmpFile = File.createTempFile(filename, ".apk");
+        try (InputStream inputStream = context.getAssets().openNonAsset(filename)) {
+            Files.copy(inputStream, tmpFile.toPath(), REPLACE_EXISTING);
+        }
+        return tmpFile;
+    }
+
+    /**
+     * Tests AndroidManifest.xml with no android:isolatedSplits attribute.
+     */
+    @Test
+    public void testParseIsolatedSplitsDefault() throws Exception {
+        final File testFile = extractFile(TEST_APP1_APK);
+        try {
+            final ParsedPackage pkg = makeParser().parsePackage(testFile, 0, false);
+            assertFalse("isolatedSplits", pkg.isIsolatedSplitLoading());
+        } finally {
+            testFile.delete();
+        }
+    }
+
+    /**
+     * Tests AndroidManifest.xml with an android:isolatedSplits attribute set to a constant.
+     */
+    @Test
+    public void testParseIsolatedSplitsConstant() throws Exception {
+        final File testFile = extractFile(TEST_APP2_APK);
+        try {
+            final ParsedPackage pkg = makeParser().parsePackage(testFile, 0, false);
+            assertTrue("isolatedSplits", pkg.isIsolatedSplitLoading());
+        } finally {
+            testFile.delete();
+        }
+    }
+
+    /**
+     * Tests AndroidManifest.xml with an android:isolatedSplits attribute set to a resource.
+     */
+    @Test
+    public void testParseIsolatedSplitsResource() throws Exception {
+        final File testFile = extractFile(TEST_APP3_APK);
+        try {
+            final ParsedPackage pkg = makeParser().parsePackage(testFile, 0, false);
+            assertTrue("isolatedSplits", pkg.isIsolatedSplitLoading());
+        } finally {
+            testFile.delete();
+        }
+    }
+
     /**
      * A trivial subclass of package parser that only caches the package name, and throws away
      * all other information.
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index 155c6dd..fcbd507 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -34,6 +34,7 @@
 import android.media.tv.tunerresourcemanager.TunerFrontendRequest;
 import android.media.tv.tunerresourcemanager.TunerResourceManager;
 import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -54,6 +55,7 @@
  * Tests for {@link TunerResourceManagerService} class.
  */
 @SmallTest
+@Presubmit
 @RunWith(JUnit4.class)
 public class TunerResourceManagerServiceTest {
     private static final String TAG = "TunerResourceManagerServiceTest";
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java
index ab5665b..2ff178e 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/UseCasePriorityHintsTest.java
@@ -18,6 +18,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.media.tv.TvInputService;
+import android.platform.test.annotations.Presubmit;
 import android.util.Slog;
 
 import androidx.test.filters.SmallTest;
@@ -36,6 +37,7 @@
  * Tests for {@link UseCasePriorityHints} class.
  */
 @SmallTest
+@Presubmit
 @RunWith(JUnit4.class)
 public class UseCasePriorityHintsTest {
     private static final String TAG = "UseCasePriorityHintsTest";
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
new file mode 100644
index 0000000..c409438
--- /dev/null
+++ b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
@@ -0,0 +1,53 @@
+// Copyright (C) 2020 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.
+
+android_test_helper_app {
+    name: "PackageParserTestApp1",
+    sdk_version: "current",
+    srcs: ["**/*.java"],
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+    manifest: "AndroidManifestApp1.xml",
+}
+
+android_test_helper_app {
+    name: "PackageParserTestApp2",
+    sdk_version: "current",
+    srcs: ["**/*.java"],
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+    manifest: "AndroidManifestApp2.xml",
+}
+
+android_test_helper_app {
+    name: "PackageParserTestApp3",
+    sdk_version: "current",
+    srcs: ["**/*.java"],
+    dex_preopt: {
+        enabled: false,
+    },
+    optimize: {
+        enabled: false,
+    },
+    resource_dirs: ["res"],
+    manifest: "AndroidManifestApp3.xml",
+}
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp1.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp1.xml
new file mode 100644
index 0000000..01d335d
--- /dev/null
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp1.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.servicestests.apps.packageparserapp" >
+
+    <application>
+        <activity android:name=".TestActivity"
+                  android:exported="true" />
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp2.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp2.xml
new file mode 100644
index 0000000..567946c
--- /dev/null
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp2.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.servicestests.apps.packageparserapp"
+        android:isolatedSplits="true" >
+
+    <application>
+        <activity android:name=".TestActivity"
+                  android:exported="true" />
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp3.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp3.xml
new file mode 100644
index 0000000..77285a8
--- /dev/null
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp3.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.servicestests.apps.packageparserapp"
+        android:isolatedSplits="@bool/config_isIsolated" >
+
+    <application>
+        <activity android:name=".TestActivity"
+                  android:exported="true" />
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml b/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml
new file mode 100644
index 0000000..6a4cc65
--- /dev/null
+++ b/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <bool name="config_isIsolated">true</bool>
+</resources>
\ No newline at end of file
diff --git a/core/java/android/content/pm/NamedParcelFileDescriptor.aidl b/services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/TestActivity.java
similarity index 61%
rename from core/java/android/content/pm/NamedParcelFileDescriptor.aidl
rename to services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/TestActivity.java
index 68dd5f5..2eacb96 100644
--- a/core/java/android/content/pm/NamedParcelFileDescriptor.aidl
+++ b/services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/TestActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-package android.content.pm;
+package com.android.servicestests.apps.packageparserapp;
 
-import android.os.ParcelFileDescriptor;
+import android.app.Activity;
+import android.os.Bundle;
 
-/**
- * A named ParcelFileDescriptor.
- * @hide
- */
-parcelable NamedParcelFileDescriptor {
-    @utf8InCpp String name;
-    ParcelFileDescriptor fd;
+public class TestActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        finish();
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
index d7fc97c..2578ca8 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleCheckerTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
@@ -33,6 +34,7 @@
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -110,8 +112,10 @@
 
     void setUpShortcutBubble(boolean isValid) {
         when(mBubbleMetadata.getShortcutId()).thenReturn(SHORTCUT_ID);
-        when(mShortcutHelper.hasValidShortcutInfo(SHORTCUT_ID, PKG, mUserHandle))
-                .thenReturn(isValid);
+        ShortcutInfo info = mock(ShortcutInfo.class);
+        when(info.getId()).thenReturn(SHORTCUT_ID);
+        when(mShortcutHelper.getValidShortcutInfo(SHORTCUT_ID, PKG, mUserHandle))
+                .thenReturn(isValid ? info : null);
         when(mBubbleMetadata.getIntent()).thenReturn(null);
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
index c7cef05..0dbbbaa 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BubbleExtractorTest.java
@@ -47,6 +47,7 @@
 public class BubbleExtractorTest extends UiServiceTestCase {
 
     @Mock RankingConfig mConfig;
+    @Mock BubbleExtractor.BubbleChecker mBubbleChecker;
     BubbleExtractor mBubbleExtractor;
 
     private String mPkg = "com.android.server.notification";
@@ -141,4 +142,33 @@
 
         assertFalse(r.canBubble());
     }
+
+    @Test
+    public void testFlagBubble_true() {
+        when(mConfig.bubblesEnabled()).thenReturn(true);
+        when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);
+        NotificationRecord r = getNotificationRecord(true, IMPORTANCE_UNSPECIFIED);
+
+        mBubbleExtractor.setBubbleChecker(mBubbleChecker);
+        when(mBubbleChecker.isNotificationAppropriateToBubble(r)).thenReturn(true);
+        mBubbleExtractor.process(r);
+
+        assertTrue(r.canBubble());
+        assertTrue(r.getNotification().isBubbleNotification());
+    }
+
+    @Test
+    public void testFlagBubble_noFlag_previouslyRemoved() {
+        when(mConfig.bubblesEnabled()).thenReturn(true);
+        when(mConfig.areBubblesAllowed(mPkg, mUid)).thenReturn(true);
+        NotificationRecord r = getNotificationRecord(true, IMPORTANCE_UNSPECIFIED);
+        r.setFlagBubbleRemoved(true);
+
+        mBubbleExtractor.setBubbleChecker(mBubbleChecker);
+        when(mBubbleChecker.isNotificationAppropriateToBubble(r)).thenReturn(true);
+        mBubbleExtractor.process(r);
+
+        assertTrue(r.canBubble());
+        assertFalse(r.getNotification().isBubbleNotification());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 3c29445..7026420 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -78,6 +78,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.AutomaticZenRule;
 import android.app.IActivityManager;
@@ -154,6 +155,7 @@
 import com.android.internal.logging.InstanceIdSequenceFake;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.UiServiceTestCase;
@@ -380,12 +382,20 @@
 
         MockitoAnnotations.initMocks(this);
 
+        DeviceIdleInternal deviceIdleInternal = mock(DeviceIdleInternal.class);
+        when(deviceIdleInternal.getNotificationWhitelistDuration()).thenReturn(3000L);
+        ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class);
+
         LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
         LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
         LocalServices.removeServiceForTest(WindowManagerInternal.class);
         LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
         LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
         LocalServices.addService(StatusBarManagerInternal.class, mStatusBar);
+        LocalServices.removeServiceForTest(DeviceIdleInternal.class);
+        LocalServices.addService(DeviceIdleInternal.class, deviceIdleInternal);
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+        LocalServices.addService(ActivityManagerInternal.class, activityManagerInternal);
 
         doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
 
@@ -5592,6 +5602,67 @@
     }
 
     @Test
+    public void testNotificationBubbleIsFlagRemoved_resetOnUpdate() throws Exception {
+        // Bubbles are allowed!
+        setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+        // Notif with bubble metadata
+        NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+                "testNotificationBubbleIsFlagRemoved_resetOnUpdate");
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+        waitForIdle();
+        // Flag shouldn't be modified
+        NotificationRecord recordToCheck = mService.getNotificationRecord(nr.getSbn().getKey());
+        assertFalse(recordToCheck.isFlagBubbleRemoved());
+
+        // Notify we're not a bubble
+        mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), false);
+        waitForIdle();
+        // Flag should be modified
+        recordToCheck = mService.getNotificationRecord(nr.getSbn().getKey());
+        assertTrue(recordToCheck.isFlagBubbleRemoved());
+
+
+        // Update the notif
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+        waitForIdle();
+        // And the flag is reset
+        recordToCheck = mService.getNotificationRecord(nr.getSbn().getKey());
+        assertFalse(recordToCheck.isFlagBubbleRemoved());
+    }
+
+    @Test
+    public void testNotificationBubbleIsFlagRemoved_resetOnBubbleChangedTrue() throws Exception {
+        // Bubbles are allowed!
+        setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
+        // Notif with bubble metadata
+        NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+                "testNotificationBubbleIsFlagRemoved_trueOnBubbleChangedTrue");
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+        waitForIdle();
+        // Flag shouldn't be modified
+        NotificationRecord recordToCheck = mService.getNotificationRecord(nr.getSbn().getKey());
+        assertFalse(recordToCheck.isFlagBubbleRemoved());
+
+        // Notify we're not a bubble
+        mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), false);
+        waitForIdle();
+        // Flag should be modified
+        recordToCheck = mService.getNotificationRecord(nr.getSbn().getKey());
+        assertTrue(recordToCheck.isFlagBubbleRemoved());
+
+        // Notify we are a bubble
+        mService.mNotificationDelegate.onNotificationBubbleChanged(nr.getKey(), true);
+        waitForIdle();
+        // And the flag is reset
+        assertFalse(recordToCheck.isFlagBubbleRemoved());
+    }
+
+    @Test
     public void testOnBubbleNotificationSuppressionChanged() throws Exception {
         // Bubbles are allowed!
         setUpPrefsForBubbles(PKG, mUid, true /* global */, true /* app */, true /* channel */);
@@ -6372,13 +6443,41 @@
 
         ShortcutInfo si = mock(ShortcutInfo.class);
         when(si.getShortLabel()).thenReturn("Hello");
+        when(si.isLongLived()).thenReturn(true);
         when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si));
 
         List<ConversationChannelWrapper> conversations =
                 mBinderService.getConversationsForPackage(PKG_P, mUid).getList();
         assertEquals(si, conversations.get(0).getShortcutInfo());
         assertEquals(si, conversations.get(1).getShortcutInfo());
+    }
 
+    @Test
+    public void testGetConversationsForPackage_shortcut_notLongLived() throws Exception {
+        mService.setPreferencesHelper(mPreferencesHelper);
+        ArrayList<ConversationChannelWrapper> convos = new ArrayList<>();
+        ConversationChannelWrapper convo1 = new ConversationChannelWrapper();
+        NotificationChannel channel1 = new NotificationChannel("a", "a", 1);
+        channel1.setConversationId("parent1", "convo 1");
+        convo1.setNotificationChannel(channel1);
+        convos.add(convo1);
+
+        ConversationChannelWrapper convo2 = new ConversationChannelWrapper();
+        NotificationChannel channel2 = new NotificationChannel("b", "b", 1);
+        channel2.setConversationId("parent1", "convo 2");
+        convo2.setNotificationChannel(channel2);
+        convos.add(convo2);
+        when(mPreferencesHelper.getConversations(anyString(), anyInt())).thenReturn(convos);
+
+        ShortcutInfo si = mock(ShortcutInfo.class);
+        when(si.getShortLabel()).thenReturn("Hello");
+        when(si.isLongLived()).thenReturn(false);
+        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si));
+
+        List<ConversationChannelWrapper> conversations =
+                mBinderService.getConversationsForPackage(PKG_P, mUid).getList();
+        assertNull(conversations.get(0).getShortcutInfo());
+        assertNull(conversations.get(1).getShortcutInfo());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 52fc3de..12934ee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -27,6 +27,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -56,7 +57,6 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
 
 import android.app.ActivityManager;
 import android.app.IApplicationThread;
@@ -1157,6 +1157,34 @@
     }
 
     @Test
+    public void testCheckBehindFullscreenActivity() {
+        final ActivityRecord bottomActivity =
+                new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build();
+        final ActivityRecord topActivity =
+                new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build();
+        doReturn(true).when(mStack).shouldBeVisible(any());
+        assertTrue(mStack.checkBehindFullscreenActivity(bottomActivity,
+                null /* handleBehindFullscreenActivity */));
+        assertFalse(mStack.checkBehindFullscreenActivity(topActivity,
+                null /* handleBehindFullscreenActivity */));
+
+        doReturn(false).when(topActivity).occludesParent();
+        assertFalse(mStack.checkBehindFullscreenActivity(bottomActivity,
+                null /* handleBehindFullscreenActivity */));
+        assertFalse(mStack.checkBehindFullscreenActivity(topActivity,
+                null /* handleBehindFullscreenActivity */));
+
+        final ActivityRecord finishingActivity =
+                new ActivityBuilder(mService).setStack(mStack).setTask(mTask).build();
+        finishingActivity.finishing = true;
+        doCallRealMethod().when(finishingActivity).occludesParent();
+        assertFalse(mStack.checkBehindFullscreenActivity(bottomActivity,
+                null /* handleBehindFullscreenActivity */));
+        assertFalse(mStack.checkBehindFullscreenActivity(topActivity,
+                null /* handleBehindFullscreenActivity */));
+    }
+
+    @Test
     public void testClearUnknownAppVisibilityBehindFullscreenActivity() {
         final UnknownAppVisibilityController unknownAppVisibilityController =
                 mDefaultDisplay.mDisplayContent.mUnknownAppVisibilityController;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 6ec0f91..ed400ea 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -82,9 +82,8 @@
 import android.platform.test.annotations.Presubmit;
 import android.service.voice.IVoiceInteractionSession;
 import android.view.Gravity;
-import android.window.IWindowContainer;
-import android.view.SurfaceControl;
 import android.window.ITaskOrganizer;
+import android.window.IWindowContainer;
 
 import androidx.test.filters.SmallTest;
 
@@ -975,7 +974,7 @@
 
         // Move activity to split-screen-primary stack and make sure it has the focus.
         TestSplitOrganizer splitOrg = new TestSplitOrganizer(mService, top.getDisplayId());
-        splitOrg.mPrimary.addChild(top.getRootTask(), 0 /* index */);
+        top.getRootTask().reparent(splitOrg.mPrimary, POSITION_BOTTOM);
         top.getRootTask().moveToFront("testWindowingModeOptionsLaunchAdjacent");
 
         // Activity must landed on split-screen-secondary when launch adjacent.
@@ -1001,8 +1000,8 @@
 
     static class TestSplitOrganizer extends ITaskOrganizer.Stub {
         final ActivityTaskManagerService mService;
-        TaskTile mPrimary;
-        TaskTile mSecondary;
+        Task mPrimary;
+        Task mSecondary;
         boolean mInSplit = false;
         int mDisplayId;
         TestSplitOrganizer(ActivityTaskManagerService service, int displayId) {
@@ -1014,10 +1013,10 @@
                     WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
             IWindowContainer primary = mService.mTaskOrganizerController.createRootTask(
                     displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).token;
-            mPrimary = TaskTile.forToken(primary.asBinder());
+            mPrimary = WindowContainer.fromBinder(primary.asBinder()).asTask();
             IWindowContainer secondary = mService.mTaskOrganizerController.createRootTask(
                     displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token;
-            mSecondary = TaskTile.forToken(secondary.asBinder());
+            mSecondary = WindowContainer.fromBinder(secondary.asBinder()).asTask();
         }
         @Override
         public void onTaskAppeared(ActivityManager.RunningTaskInfo info) {
@@ -1042,7 +1041,7 @@
                     for (int i = dc.getStackCount() - 1; i >= 0; --i) {
                         if (!WindowConfiguration.isSplitScreenWindowingMode(
                                 dc.getStackAt(i).getWindowingMode())) {
-                            mSecondary.addChild(dc.getStackAt(i), 0);
+                            dc.getStackAt(i).reparent(mSecondary, POSITION_BOTTOM);
                         }
                     }
                 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 4634e2d..716369d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -464,7 +464,7 @@
         ActivityStack build() {
             final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
             final ActivityStack stack = mDisplay.createStackUnchecked(mWindowingMode,
-                    mActivityType, stackId, mOnTop, mInfo, mIntent);
+                    mActivityType, stackId, mOnTop, mInfo, mIntent, false /* createdByOrganizer */);
             final ActivityStackSupervisor supervisor = mRootWindowContainer.mStackSupervisor;
 
             if (mCreateActivity) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index c71d819..08e492a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -28,7 +28,6 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 
@@ -38,7 +37,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 
-import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
@@ -70,7 +68,7 @@
 
     @Before
     public void setUp() throws Exception {
-        doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean());
+        doNothing().when(mWm.mRoot).performSurfacePlacement();
         mDc = mWm.getDefaultDisplayContentLocked();
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 6cc57f4..cf3cfec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -174,7 +174,7 @@
                 null /* freezeThisOneIfNeeded */, false /* forceUpdate */);
         // In this test, DC will not get config update. Set the waiting flag to false.
         mDisplayContent.mWaitingForConfig = false;
-        mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
+        mWm.mRoot.performSurfacePlacement();
         assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, mDisplayContent.getLastOrientation());
         assertTrue(appWindow.mResizeReported);
         appWindow.removeImmediately();
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index bfb126f..db7bce4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -19,10 +19,12 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
@@ -147,6 +149,61 @@
     }
 
     @Test
+    public void testStripForDispatch_belowIme() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+
+        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+
+        assertNotNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+    }
+
+    @Test
+    public void testStripForDispatch_aboveIme() {
+        final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+
+        assertNull(getController().getInsetsForDispatch(app).peekSource(ITYPE_IME));
+    }
+
+    @Test
+    public void testStripForDispatch_childWindow_altFocusable() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+        final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
+        child.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
+
+        final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+
+        // IME cannot be the IME target.
+        ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+
+        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+
+        assertNull(getController().getInsetsForDispatch(child).peekSource(ITYPE_IME));
+    }
+
+    @Test
+    public void testStripForDispatch_childWindow_splitScreen() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+        final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
+        child.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+        child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+
+        final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+
+        // IME cannot be the IME target.
+        ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+
+        getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+
+        assertNull(getController().getInsetsForDispatch(child).peekSource(ITYPE_IME));
+    }
+
+    @Test
     public void testImeForDispatch() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 8f3ff52..ae467c0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -120,7 +120,7 @@
                 .build();
         mTestTask.mUserId = TEST_USER_ID;
         mTestTask.mLastNonFullscreenBounds = TEST_BOUNDS;
-        mTestTask.hasBeenVisible = true;
+        mTestTask.setHasBeenVisible(true);
 
         mTaskWithDifferentComponent = new TaskBuilder(mSupervisor)
                 .setComponent(ALTERNATIVE_COMPONENT).build();
@@ -346,7 +346,7 @@
                 .build();
         anotherTaskOfTheSameUser.setWindowingMode(WINDOWING_MODE_FREEFORM);
         anotherTaskOfTheSameUser.setBounds(200, 300, 400, 500);
-        anotherTaskOfTheSameUser.hasBeenVisible = true;
+        anotherTaskOfTheSameUser.setHasBeenVisible(true);
         mTarget.saveTask(anotherTaskOfTheSameUser);
 
         stack = mTestDisplay.createStack(TEST_WINDOWING_MODE,
@@ -358,7 +358,7 @@
                 .build();
         anotherTaskOfDifferentUser.setWindowingMode(WINDOWING_MODE_FREEFORM);
         anotherTaskOfDifferentUser.setBounds(300, 400, 500, 600);
-        anotherTaskOfDifferentUser.hasBeenVisible = true;
+        anotherTaskOfDifferentUser.setHasBeenVisible(true);
         mTarget.saveTask(anotherTaskOfDifferentUser);
 
         mTarget.onCleanupUser(TEST_USER_ID);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 66566bc..8846fb8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -21,6 +21,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
@@ -28,6 +29,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.TYPE_VIRTUAL;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -54,6 +56,7 @@
 
 import android.app.ActivityManager.RecentTaskInfo;
 import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.WindowConfiguration;
 import android.content.ComponentName;
@@ -681,24 +684,19 @@
      * Tests that tasks on singleTaskDisplay are not visible and not trimmed/removed.
      */
     @Test
-    public void testVisibleTasks_singleTaskDisplay() {
+    public void testVisibleTasks_alwaysOnTop() {
         mRecentTasks.setOnlyTestVisibleRange();
         mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */);
 
-        final DisplayContent singleTaskDisplay =
-                addNewDisplayContentAt(DisplayContent.POSITION_TOP);
-        singleTaskDisplay.setDisplayToSingleTaskInstance();
-        ActivityStack singleTaskStack = singleTaskDisplay.createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
+        final Task alwaysOnTopTask = display.createStack(WINDOWING_MODE_MULTI_WINDOW,
+                ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        alwaysOnTopTask.setAlwaysOnTop(true);
 
-        Task excludedTask1 = createTaskBuilder(".ExcludedTask1")
-                .setStack(singleTaskStack)
-                .build();
+        assertFalse("Always on top tasks should not be visible recents",
+                mRecentTasks.isVisibleRecentTask(alwaysOnTopTask));
 
-        assertFalse("Tasks on singleTaskDisplay should not be visible recents",
-                mRecentTasks.isVisibleRecentTask(excludedTask1));
-
-        mRecentTasks.add(excludedTask1);
+        mRecentTasks.add(alwaysOnTopTask);
 
         // Add N+1 visible tasks.
         mRecentTasks.add(mTasks.get(0));
@@ -1115,7 +1113,6 @@
                 () -> mService.moveTaskToStack(0, INVALID_STACK_ID, true));
         assertSecurityException(expectCallable,
                 () -> mService.setTaskWindowingModeSplitScreenPrimary(0, true));
-        assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
         assertSecurityException(expectCallable,
                 () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
         assertSecurityException(expectCallable, () -> mService.getAllStackInfos());
@@ -1366,12 +1363,12 @@
         public boolean mLastAllowed;
 
         @Override
-        void getTasks(int maxNum, List<RunningTaskInfo> list, int ignoreActivityType,
-                int ignoreWindowingMode, RootWindowContainer root,
-                int callingUid, boolean allowed, boolean crossUser, ArraySet<Integer> profileIds) {
+        void getTasks(int maxNum, List<RunningTaskInfo> list, boolean filterOnlyVisibleRecents,
+                RootWindowContainer root, int callingUid, boolean allowed, boolean crossUser,
+                ArraySet<Integer> profileIds) {
             mLastAllowed = allowed;
-            super.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, root,
-                    callingUid, allowed, crossUser, profileIds);
+            super.getTasks(maxNum, list, filterOnlyVisibleRecents, root, callingUid, allowed,
+                    crossUser, profileIds);
         }
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 406affc..da07bac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -100,7 +100,7 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean());
+        doNothing().when(mWm.mRoot).performSurfacePlacement();
         when(mMockRunner.asBinder()).thenReturn(new Binder());
         mDefaultDisplay = mWm.mRoot.getDefaultDisplay();
         mController = spy(new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks,
@@ -403,11 +403,11 @@
                 wallpapers.get(0).getConfiguration().orientation);
 
         // Wallpaper's transform state is controlled by home, so the invocation should be no-op.
-        wallpaperWindowToken.clearFixedRotationTransform();
+        wallpaperWindowToken.finishFixedRotationTransform();
         assertTrue(wallpaperWindowToken.hasFixedRotationTransform());
 
         // Wallpaper's transform state should be cleared with home.
-        homeActivity.clearFixedRotationTransform();
+        homeActivity.finishFixedRotationTransform();
         assertFalse(wallpaperWindowToken.hasFixedRotationTransform());
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 0ef2582..e841e43 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -788,6 +788,22 @@
     }
 
     @Test
+    public void testGetValidLaunchStackOnDisplayWithCandidateRootTask() {
+        // Create a root task with an activity on secondary display.
+        final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mService, 300,
+                600).build();
+        final Task task = new ActivityTestsBase.StackBuilder(mRootWindowContainer).setDisplay(
+                secondaryDisplay).build();
+        final ActivityRecord activity = new ActivityTestsBase.ActivityBuilder(mService)
+                .setTask(task).build();
+
+        // Make sure the root task is valid and can be reused on default display.
+        final ActivityStack stack = mRootWindowContainer.getValidLaunchStackOnDisplay(
+                DEFAULT_DISPLAY, activity, task, null, null);
+        assertEquals(task, stack);
+    }
+
+    @Test
     public void testSwitchUser_missingHomeRootTask() {
         doReturn(mFullscreenStack).when(mRootWindowContainer).getTopDisplayFocusedStack();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index 0d55654..d6a67ab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -16,9 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
@@ -82,9 +79,8 @@
         // collected from all tasks across all the stacks
         final int numFetchTasks = 5;
         ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
-        mRunningTasks.getTasks(5, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED,
-                mRootWindowContainer, -1 /* callingUid */, true /* allowed */,
-                true /*crossUser */, PROFILE_IDS);
+        mRunningTasks.getTasks(5, tasks, false /* filterOnlyVisibleRecents */, mRootWindowContainer,
+                -1 /* callingUid */, true /* allowed */, true /*crossUser */, PROFILE_IDS);
         assertThat(tasks).hasSize(numFetchTasks);
         for (int i = 0; i < numFetchTasks; i++) {
             assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -93,9 +89,9 @@
         // Ensure that requesting more than the total number of tasks only returns the subset
         // and does not crash
         tasks.clear();
-        mRunningTasks.getTasks(100, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED,
-                mRootWindowContainer, -1 /* callingUid */, true /* allowed */,
-                true /* crossUser */, PROFILE_IDS);
+        mRunningTasks.getTasks(100, tasks, false /* filterOnlyVisibleRecents */,
+                mRootWindowContainer, -1 /* callingUid */, true /* allowed */, true /* crossUser */,
+                PROFILE_IDS);
         assertThat(tasks).hasSize(numTasks);
         for (int i = 0; i < numTasks; i++) {
             assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -119,9 +115,9 @@
 
         final int numFetchTasks = 5;
         final ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
-        mRunningTasks.getTasks(numFetchTasks, tasks, ACTIVITY_TYPE_UNDEFINED,
-                WINDOWING_MODE_UNDEFINED, mRootWindowContainer, -1 /* callingUid */,
-                true /* allowed */, true /*crossUser */, PROFILE_IDS);
+        mRunningTasks.getTasks(numFetchTasks, tasks, false /* filterOnlyVisibleRecents */,
+                mRootWindowContainer, -1 /* callingUid */, true /* allowed */, true /*crossUser */,
+                PROFILE_IDS);
         assertThat(tasks).hasSize(numFetchTasks);
         for (int i = 0; i < tasks.size(); i++) {
             final Bundle extras = tasks.get(i).baseIntent.getExtras();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index edf81ea..893a145 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -481,7 +481,7 @@
         // The letterbox needs a main window to layout.
         addWindowToActivity(mActivity);
         // Compute the frames of the window and invoke {@link ActivityRecord#layoutLetterbox}.
-        mActivity.mRootWindowContainer.performSurfacePlacement(false /* recoveringMemory */);
+        mActivity.mRootWindowContainer.performSurfacePlacement();
         // The letterbox insets should be [350, 0 - 350, 0].
         assertEquals(new Rect(mActivity.getBounds().left, 0, dh - mActivity.getBounds().right, 0),
                 mActivity.getLetterboxInsets());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index a96f401..4cc84a6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -37,6 +37,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -95,10 +96,28 @@
         return registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
     }
 
+    Task createTask(ActivityStack stack, boolean fakeDraw) {
+        final Task task = createTaskInStack(stack, 0);
+
+        if (fakeDraw) {
+            task.setHasBeenVisible(true);
+        }
+        return task;
+    }
+
+    Task createTask(ActivityStack stack) {
+        // Fake draw notifications for most of our tests.
+        return createTask(stack, true);
+    }
+
+    ActivityStack createStack() {
+        return createTaskStackOnDisplay(mDisplayContent);
+    }
+
     @Test
     public void testAppearVanish() throws RemoteException {
-        final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
+        final ActivityStack stack = createStack();
+        final Task task = createTask(stack);
         final ITaskOrganizer organizer = registerMockOrganizer();
 
         task.setTaskOrganizer(organizer);
@@ -109,9 +128,42 @@
     }
 
     @Test
+    public void testAppearWaitsForVisibility() throws RemoteException {
+        final ActivityStack stack = createStack();
+        final Task task = createTask(stack, false);
+        final ITaskOrganizer organizer = registerMockOrganizer();
+
+        task.setTaskOrganizer(organizer);
+
+        verify(organizer, never()).onTaskAppeared(any());
+        task.setHasBeenVisible(true);
+        assertTrue(stack.getHasBeenVisible());
+
+        verify(organizer).onTaskAppeared(any());
+
+        task.removeImmediately();
+        verify(organizer).onTaskVanished(any());
+    }
+
+    @Test
+    public void testNoVanishedIfNoAppear() throws RemoteException {
+        final ActivityStack stack = createStack();
+        final Task task = createTask(stack, false /* hasBeenVisible */);
+        final ITaskOrganizer organizer = registerMockOrganizer();
+
+        // In this test we skip making the Task visible, and verify
+        // that even though a TaskOrganizer is set remove doesn't emit
+        // a vanish callback, because we never emitted appear.
+        task.setTaskOrganizer(organizer);
+        verify(organizer, never()).onTaskAppeared(any());
+        task.removeImmediately();
+        verify(organizer, never()).onTaskVanished(any());
+    }
+
+    @Test
     public void testSwapOrganizer() throws RemoteException {
-        final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
+        final ActivityStack stack = createStack();
+        final Task task = createTask(stack);
         final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
         final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
 
@@ -124,8 +176,8 @@
 
     @Test
     public void testSwapWindowingModes() throws RemoteException {
-        final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
+        final ActivityStack stack = createStack();
+        final Task task = createTask(stack);
         final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
         final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
 
@@ -138,8 +190,8 @@
 
     @Test
     public void testClearOrganizer() throws RemoteException {
-        final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
+        final ActivityStack stack = createStack();
+        final Task task = createTask(stack);
         final ITaskOrganizer organizer = registerMockOrganizer();
 
         stack.setTaskOrganizer(organizer);
@@ -153,8 +205,8 @@
 
     @Test
     public void testUnregisterOrganizer() throws RemoteException {
-        final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
+        final ActivityStack stack = createStack();
+        final Task task = createTask(stack);
         final ITaskOrganizer organizer = registerMockOrganizer();
 
         stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -168,12 +220,12 @@
 
     @Test
     public void testUnregisterOrganizerReturnsRegistrationToPrevious() throws RemoteException {
-        final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
-        final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent);
-        final Task task2 = createTaskInStack(stack2, 0 /* userId */);
-        final ActivityStack stack3 = createTaskStackOnDisplay(mDisplayContent);
-        final Task task3 = createTaskInStack(stack3, 0 /* userId */);
+        final ActivityStack stack = createStack();
+        final Task task = createTask(stack);
+        final ActivityStack stack2 = createStack();
+        final Task task2 = createTask(stack2);
+        final ActivityStack stack3 = createStack();
+        final Task task3 = createTask(stack3);
         final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
 
         // First organizer is registered, verify a task appears when changing windowing mode
@@ -201,9 +253,9 @@
     public void testRegisterTaskOrganizerStackWindowingModeChanges() throws RemoteException {
         final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED);
 
-        final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
-        final Task task2 = createTaskInStack(stack, 0 /* userId */);
+        final ActivityStack stack = createStack();
+        final Task task = createTask(stack);
+        final Task task2 = createTask(stack);
         stack.setWindowingMode(WINDOWING_MODE_PINNED);
         verify(organizer, times(1)).onTaskAppeared(any());
 
@@ -213,8 +265,8 @@
 
     @Test
     public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException {
-        final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stack, 0 /* userId */);
+        final ActivityStack stack = createStack();
+        final Task task = createTask(stack);
         stack.setWindowingMode(WINDOWING_MODE_PINNED);
 
         final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED);
@@ -347,45 +399,62 @@
         assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType);
 
         DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
-        List<TaskTile> infos = getTaskTiles(dc);
+        List<Task> infos = getTasksCreatedByOrganizer(dc);
         assertEquals(2, infos.size());
 
         assertTrue(mWm.mAtmService.mTaskOrganizerController.deleteRootTask(info1.token));
-        infos = getTaskTiles(dc);
+        infos = getTasksCreatedByOrganizer(dc);
         assertEquals(1, infos.size());
         assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, infos.get(0).getWindowingMode());
     }
 
     @Test
     public void testTileAddRemoveChild() {
+        ITaskOrganizer listener = new ITaskOrganizer.Stub() {
+            @Override
+            public void onTaskAppeared(RunningTaskInfo taskInfo) { }
+
+            @Override
+            public void onTaskVanished(RunningTaskInfo container) { }
+
+            @Override
+            public void onTaskInfoChanged(RunningTaskInfo info) throws RemoteException {
+            }
+        };
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
         RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
                 mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
 
         final ActivityStack stack = createTaskStackOnDisplay(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
         assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
-        TaskTile tile1 = TaskTile.forToken(info1.token.asBinder());
-        tile1.addChild(stack, 0 /* index */);
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertEquals(info1.configuration.windowConfiguration.getWindowingMode(),
                 stack.getWindowingMode());
 
         // Info should reflect new membership
-        List<TaskTile> tiles = getTaskTiles(mDisplayContent);
-        info1 = tiles.get(0).getTaskInfo();
+        List<Task> infos = getTasksCreatedByOrganizer(mDisplayContent);
+        info1 = infos.get(0).getTaskInfo();
         assertEquals(ACTIVITY_TYPE_STANDARD, info1.topActivityType);
 
         // Children inherit configuration
         Rect newSize = new Rect(10, 10, 300, 300);
-        Configuration c = new Configuration(tile1.getRequestedOverrideConfiguration());
+        Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
+        Configuration c = new Configuration(task1.getRequestedOverrideConfiguration());
         c.windowConfiguration.setBounds(newSize);
         doNothing().when(stack).adjustForMinimalTaskDimensions(any(), any());
-        tile1.onRequestedOverrideConfigurationChanged(c);
+        task1.onRequestedOverrideConfigurationChanged(c);
         assertEquals(newSize, stack.getBounds());
 
-        tile1.removeChild(stack);
+        wct = new WindowContainerTransaction();
+        wct.reparent(stack.mRemoteToken, null, true /* onTop */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
-        tiles = getTaskTiles(mDisplayContent);
-        info1 = tiles.get(0).getTaskInfo();
+        infos = getTasksCreatedByOrganizer(mDisplayContent);
+        info1 = infos.get(0).getTaskInfo();
         assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
     }
 
@@ -415,8 +484,10 @@
 
         final ActivityStack stack = createTaskStackOnDisplay(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
-        TaskTile tile1 = TaskTile.forToken(info1.token.asBinder());
-        tile1.addChild(stack, 0 /* index */);
+        Task task1 = WindowContainer.fromBinder(info1.token.asBinder()).asTask();
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertTrue(called[0]);
         assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
 
@@ -424,19 +495,24 @@
         called[0] = false;
         final ActivityStack stack2 = createTaskStackOnDisplay(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent);
-        tile1.addChild(stack2, 0 /* index */);
+        wct = new WindowContainerTransaction();
+        wct.reparent(stack2.mRemoteToken, info1.token, true /* onTop */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertTrue(called[0]);
         assertEquals(ACTIVITY_TYPE_HOME, lastReportedTiles.get(0).topActivityType);
 
         lastReportedTiles.clear();
         called[0] = false;
-        mDisplayContent.positionStackAtTop(stack, false /* includingParents */);
+        task1.positionChildAt(POSITION_TOP, stack, false /* includingParents */);
         assertTrue(called[0]);
         assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
 
         lastReportedTiles.clear();
         called[0] = false;
-        tile1.removeAllChildren();
+        wct = new WindowContainerTransaction();
+        wct.reparent(stack.mRemoteToken, null, true /* onTop */);
+        wct.reparent(stack2.mRemoteToken, null, true /* onTop */);
+        mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
         assertTrue(called[0]);
         assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType);
     }
@@ -457,9 +533,11 @@
             }
         };
         mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
+                listener, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
                 listener, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
         RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
-                mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
         RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
                 mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
 
@@ -522,21 +600,19 @@
                 lastReportedTiles.get(info1.token.asBinder()).topActivityType);
     }
 
-    private List<TaskTile> getTaskTiles(DisplayContent dc) {
-        ArrayList<TaskTile> out = new ArrayList<>();
+    private List<Task> getTasksCreatedByOrganizer(DisplayContent dc) {
+        ArrayList<Task> out = new ArrayList<>();
         for (int i = dc.getStackCount() - 1; i >= 0; --i) {
-            final TaskTile t = dc.getStackAt(i).asTile();
-            if (t != null) {
-                out.add(t);
-            }
+            final Task t = dc.getStackAt(i);
+            if (t.mCreatedByOrganizer) out.add(t);
         }
         return out;
     }
 
     @Test
     public void testTrivialBLASTCallback() throws RemoteException {
-        final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stackController1, 0 /* userId */);
+        final ActivityStack stackController1 = createStack();
+        final Task task = createTask(stackController1);
         final ITaskOrganizer organizer = registerMockOrganizer();
 
         spyOn(task);
@@ -557,8 +633,8 @@
 
     @Test
     public void testOverlappingBLASTCallback() throws RemoteException {
-        final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stackController1, 0 /* userId */);
+        final ActivityStack stackController1 = createStack();
+        final Task task = createTask(stackController1);
         final ITaskOrganizer organizer = registerMockOrganizer();
 
         spyOn(task);
@@ -586,8 +662,8 @@
 
     @Test
     public void testBLASTCallbackWithWindow() {
-        final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stackController1, 0 /* userId */);
+        final ActivityStack stackController1 = createStack();
+        final Task task = createTask(stackController1);
         final ITaskOrganizer organizer = registerMockOrganizer();
         final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
         makeWindowVisible(w);
@@ -610,8 +686,8 @@
 
     @Test
     public void testBLASTCallbackWithInvisibleWindow() {
-        final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stackController1, 0 /* userId */);
+        final ActivityStack stackController1 = createStack();
+        final Task task = createTask(stackController1);
         final ITaskOrganizer organizer = registerMockOrganizer();
         final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
 
@@ -632,8 +708,8 @@
 
     @Test
     public void testBLASTCallbackWithChildWindow() {
-        final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
-        final Task task = createTaskInStack(stackController1, 0 /* userId */);
+        final ActivityStack stackController1 = createStack();
+        final Task task = createTask(stackController1);
         final ITaskOrganizer organizer = registerMockOrganizer();
         final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
         final WindowState child = createWindow(w, TYPE_APPLICATION, "Other Window");
@@ -683,6 +759,8 @@
         record.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
         spyOn(record);
         doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean());
+
+        record.getRootTask().setHasBeenVisible(true);
         return record;
     }
 
@@ -730,4 +808,23 @@
         assertEquals(3, ratio.getNumerator());
         assertEquals(4, ratio.getDenominator());
     }
+
+    @Test
+    public void testPreventDuplicateAppear() throws RemoteException {
+        final ActivityStack stack = createStack();
+        final Task task = createTask(stack);
+        final ITaskOrganizer organizer = registerMockOrganizer();
+
+        task.setTaskOrganizer(organizer);
+        // setHasBeenVisible was already called once by the set-up code.
+        task.setHasBeenVisible(true);
+        verify(organizer, times(1)).onTaskAppeared(any());
+
+        task.taskOrganizerUnregistered();
+        task.setTaskOrganizer(organizer);
+        verify(organizer, times(2)).onTaskAppeared(any());
+
+        task.removeImmediately();
+        verify(organizer).onTaskVanished(any());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index a25acae..1ad4079 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -27,6 +27,7 @@
 import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.util.DisplayMetrics.DENSITY_DEFAULT;
 import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED;
@@ -151,7 +152,7 @@
             assertFalse(factory.mCreated);
 
             Task.create(mService, 0 /*taskId*/, 0 /*activityType*/,
-                    new ActivityInfo(), new Intent());
+                    new ActivityInfo(), new Intent(), false /* createdByOrganizer */);
 
             assertTrue(factory.mCreated);
         } finally {
@@ -873,11 +874,11 @@
         spyOn(persister);
 
         final Task task = getTestTask();
-        task.hasBeenVisible = false;
+        task.setHasBeenVisible(false);
         task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
         task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
 
-        task.hasBeenVisible = true;
+        task.setHasBeenVisible(true);
         task.onConfigurationChanged(task.getParent().getConfiguration());
 
         verify(persister).saveTask(task, task.getDisplayContent());
@@ -889,7 +890,7 @@
         spyOn(persister);
 
         final Task task = getTestTask();
-        task.hasBeenVisible = false;
+        task.setHasBeenVisible(false);
         task.getDisplayContent().setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
         task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         final DisplayContent oldDisplay = task.getDisplayContent();
@@ -899,7 +900,7 @@
         persister.getLaunchParams(task, null, params);
         assertEquals(WINDOWING_MODE_UNDEFINED, params.mWindowingMode);
 
-        task.hasBeenVisible = true;
+        task.setHasBeenVisible(true);
         task.removeImmediately();
 
         verify(persister).saveTask(task, oldDisplay);
@@ -914,10 +915,10 @@
         spyOn(persister);
 
         final Task task = getTestTask();
-        task.hasBeenVisible = false;
+        task.setHasBeenVisible(false);
         task.getStack().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
 
-        task.hasBeenVisible = true;
+        task.setHasBeenVisible(true);
         task.onConfigurationChanged(task.getParent().getConfiguration());
 
         verify(persister, never()).saveTask(same(task), any());
@@ -929,16 +930,30 @@
         spyOn(persister);
 
         final Task task = getTestTask();
-        task.hasBeenVisible = false;
+        task.setHasBeenVisible(false);
         task.getDisplayContent().setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
         task.getStack().setWindowingMode(WINDOWING_MODE_PINNED);
 
-        task.hasBeenVisible = true;
+        task.setHasBeenVisible(true);
         task.onConfigurationChanged(task.getParent().getConfiguration());
 
         verify(persister, never()).saveTask(same(task), any());
     }
 
+    @Test
+    public void testNotSpecifyOrientationByFloatingTask() {
+        final Task task = getTestTask();
+        final ActivityRecord activity = task.getTopMostActivity();
+        final WindowContainer<?> taskContainer = task.getParent();
+        activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+        assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskContainer.getOrientation());
+
+        task.setWindowingMode(WINDOWING_MODE_PINNED);
+
+        assertEquals(SCREEN_ORIENTATION_UNSET, taskContainer.getOrientation());
+    }
+
     private Task getTestTask() {
         final ActivityStack stack = new StackBuilder(mRootWindowContainer).build();
         return stack.getBottomMostTask();
@@ -1000,7 +1015,7 @@
 
         @Override
         Task create(ActivityTaskManagerService service, int taskId, int activityType,
-                ActivityInfo info, Intent intent) {
+                ActivityInfo info, Intent intent, boolean createdByOrganizer) {
             mCreated = true;
             return null;
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 85e4a16..e95ccab 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -251,11 +251,9 @@
 
         // b/145812508: special legacy use-case for transparent/translucent windows.
         appWindow.mAttrs.format = PixelFormat.TRANSPARENT;
-        appWindow.mAttrs.alpha = 0;
         assertTrue(appWindow.canBeImeTarget());
 
         appWindow.mAttrs.format = PixelFormat.OPAQUE;
-        appWindow.mAttrs.alpha = 1;
         appWindow.mAttrs.flags &= ~FLAG_ALT_FOCUSABLE_IM;
         assertFalse(appWindow.canBeImeTarget());
         appWindow.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerDbHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerDbHelper.java
index 9906585..71da8dd 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerDbHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerDbHelper.java
@@ -86,10 +86,11 @@
         synchronized(this) {
             SQLiteDatabase db = getWritableDatabase();
             ContentValues values = new ContentValues();
-            values.put(GenericSoundModelContract.KEY_MODEL_UUID, soundModel.uuid.toString());
-            values.put(GenericSoundModelContract.KEY_VENDOR_UUID, soundModel.vendorUuid.toString());
-            values.put(GenericSoundModelContract.KEY_DATA, soundModel.data);
-            values.put(GenericSoundModelContract.KEY_MODEL_VERSION, soundModel.version);
+            values.put(GenericSoundModelContract.KEY_MODEL_UUID, soundModel.getUuid().toString());
+            values.put(GenericSoundModelContract.KEY_VENDOR_UUID,
+                    soundModel.getVendorUuid().toString());
+            values.put(GenericSoundModelContract.KEY_DATA, soundModel.getData());
+            values.put(GenericSoundModelContract.KEY_MODEL_VERSION, soundModel.getVersion());
 
             try {
                 return db.insertWithOnConflict(GenericSoundModelContract.TABLE, null, values,
@@ -140,7 +141,7 @@
             // Delete all sound models for the given keyphrase and specified user.
             SQLiteDatabase db = getWritableDatabase();
             String soundModelClause = GenericSoundModelContract.KEY_MODEL_UUID
-                    + "='" + soundModel.uuid.toString() + "'";
+                    + "='" + soundModel.getUuid().toString() + "'";
             try {
                 return db.delete(GenericSoundModelContract.TABLE, soundModelClause, null) != 0;
             } finally {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 7099c09..6c0f2b0 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -195,7 +195,7 @@
             }
 
             // Process existing model first.
-            if (model != null && !model.getModelId().equals(soundModel.uuid)) {
+            if (model != null && !model.getModelId().equals(soundModel.getUuid())) {
                 // The existing model has a different UUID, should be replaced.
                 int status = cleanUpExistingKeyphraseModelLocked(model);
                 if (status != STATUS_OK) {
@@ -208,7 +208,7 @@
             // We need to create a new one: either no previous models existed for given keyphrase id
             // or the existing model had a different UUID and was cleaned up.
             if (model == null) {
-                model = createKeyphraseModelDataLocked(soundModel.uuid, keyphraseId);
+                model = createKeyphraseModelDataLocked(soundModel.getUuid(), keyphraseId);
             }
 
             return startRecognition(soundModel, model, callback, recognitionConfig,
@@ -249,7 +249,7 @@
                 return STATUS_ERROR;
             }
             if (mModule == null) {
-                mModule = SoundTrigger.attachModule(mModuleProperties.id, this, null);
+                mModule = SoundTrigger.attachModule(mModuleProperties.getId(), this, null);
                 if (mModule == null) {
                     Slog.w(TAG, "startRecognition cannot attach to sound trigger module");
                     return STATUS_ERROR;
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 3196758..170bee8 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -207,7 +207,13 @@
 
     @Override
     public void onBootPhase(int phase) {
-        if (PHASE_SYSTEM_SERVICES_READY == phase) {
+        Slog.d(TAG, "onBootPhase: " + phase + " : " + isSafeMode());
+        if (PHASE_DEVICE_SPECIFIC_SERVICES_READY == phase) {
+            if (isSafeMode()) {
+                Slog.w(TAG, "not enabling SoundTriggerService in safe mode");
+                return;
+            }
+
             initSoundTriggerHelper();
             mLocalSoundTriggerService.setSoundTriggerHelper(mSoundTriggerHelper);
         } else if (PHASE_THIRD_PARTY_APPS_CAN_START == phase) {
@@ -352,7 +358,7 @@
         public int loadGenericSoundModel(GenericSoundModel soundModel) {
             enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
             if (!isInitialized()) return STATUS_ERROR;
-            if (soundModel == null || soundModel.uuid == null) {
+            if (soundModel == null || soundModel.getUuid() == null) {
                 Slog.e(TAG, "Invalid sound model");
 
                 sEventLogger.log(new SoundTriggerLogger.StringEvent(
@@ -361,24 +367,24 @@
                 return STATUS_ERROR;
             }
             if (DEBUG) {
-                Slog.i(TAG, "loadGenericSoundModel(): id = " + soundModel.uuid);
+                Slog.i(TAG, "loadGenericSoundModel(): id = " + soundModel.getUuid());
             }
 
             sEventLogger.log(new SoundTriggerLogger.StringEvent("loadGenericSoundModel(): id = "
-                    + soundModel.uuid));
+                    + soundModel.getUuid()));
 
             synchronized (mLock) {
-                SoundModel oldModel = mLoadedModels.get(soundModel.uuid);
+                SoundModel oldModel = mLoadedModels.get(soundModel.getUuid());
                 // If the model we're loading is actually different than what we had loaded, we
                 // should unload that other model now. We don't care about return codes since we
                 // don't know if the other model is loaded.
                 if (oldModel != null && !oldModel.equals(soundModel)) {
-                    mSoundTriggerHelper.unloadGenericSoundModel(soundModel.uuid);
+                    mSoundTriggerHelper.unloadGenericSoundModel(soundModel.getUuid());
                     synchronized (mCallbacksLock) {
-                        mCallbacks.remove(soundModel.uuid);
+                        mCallbacks.remove(soundModel.getUuid());
                     }
                 }
-                mLoadedModels.put(soundModel.uuid, soundModel);
+                mLoadedModels.put(soundModel.getUuid(), soundModel);
             }
             return STATUS_OK;
         }
@@ -387,7 +393,7 @@
         public int loadKeyphraseSoundModel(KeyphraseSoundModel soundModel) {
             enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
             if (!isInitialized()) return STATUS_ERROR;
-            if (soundModel == null || soundModel.uuid == null) {
+            if (soundModel == null || soundModel.getUuid() == null) {
                 Slog.e(TAG, "Invalid sound model");
 
                 sEventLogger.log(new SoundTriggerLogger.StringEvent(
@@ -395,7 +401,7 @@
 
                 return STATUS_ERROR;
             }
-            if (soundModel.keyphrases == null || soundModel.keyphrases.length != 1) {
+            if (soundModel.getKeyphrases() == null || soundModel.getKeyphrases().length != 1) {
                 Slog.e(TAG, "Only one keyphrase per model is currently supported.");
 
                 sEventLogger.log(new SoundTriggerLogger.StringEvent(
@@ -405,24 +411,25 @@
                 return STATUS_ERROR;
             }
             if (DEBUG) {
-                Slog.i(TAG, "loadKeyphraseSoundModel(): id = " + soundModel.uuid);
+                Slog.i(TAG, "loadKeyphraseSoundModel(): id = " + soundModel.getUuid());
             }
 
             sEventLogger.log(new SoundTriggerLogger.StringEvent("loadKeyphraseSoundModel(): id = "
-                    + soundModel.uuid));
+                    + soundModel.getUuid()));
 
             synchronized (mLock) {
-                SoundModel oldModel = mLoadedModels.get(soundModel.uuid);
+                SoundModel oldModel = mLoadedModels.get(soundModel.getUuid());
                 // If the model we're loading is actually different than what we had loaded, we
                 // should unload that other model now. We don't care about return codes since we
                 // don't know if the other model is loaded.
                 if (oldModel != null && !oldModel.equals(soundModel)) {
-                    mSoundTriggerHelper.unloadKeyphraseSoundModel(soundModel.keyphrases[0].id);
+                    mSoundTriggerHelper.unloadKeyphraseSoundModel(
+                            soundModel.getKeyphrases()[0].getId());
                     synchronized (mCallbacksLock) {
-                        mCallbacks.remove(soundModel.uuid);
+                        mCallbacks.remove(soundModel.getUuid());
                     }
                 }
-                mLoadedModels.put(soundModel.uuid, soundModel);
+                mLoadedModels.put(soundModel.getUuid(), soundModel);
             }
             return STATUS_OK;
         }
@@ -472,9 +479,9 @@
                     return STATUS_ERROR;
                 }
                 int ret;
-                switch (soundModel.type) {
+                switch (soundModel.getType()) {
                     case SoundModel.TYPE_GENERIC_SOUND:
-                        ret = mSoundTriggerHelper.startGenericRecognition(soundModel.uuid,
+                        ret = mSoundTriggerHelper.startGenericRecognition(soundModel.getUuid(),
                             (GenericSoundModel) soundModel, callback, config);
                         break;
                     default:
@@ -539,9 +546,10 @@
                     return STATUS_ERROR;
                 }
                 int ret;
-                switch (soundModel.type) {
+                switch (soundModel.getType()) {
                     case SoundModel.TYPE_GENERIC_SOUND:
-                        ret = mSoundTriggerHelper.stopGenericRecognition(soundModel.uuid, callback);
+                        ret = mSoundTriggerHelper.stopGenericRecognition(
+                                soundModel.getUuid(), callback);
                         break;
                     default:
                         Slog.e(TAG, "Unknown model type");
@@ -591,13 +599,13 @@
                     return STATUS_ERROR;
                 }
                 int ret;
-                switch (soundModel.type) {
+                switch (soundModel.getType()) {
                     case SoundModel.TYPE_KEYPHRASE:
                         ret = mSoundTriggerHelper.unloadKeyphraseSoundModel(
-                                ((KeyphraseSoundModel)soundModel).keyphrases[0].id);
+                                ((KeyphraseSoundModel) soundModel).getKeyphrases()[0].getId());
                         break;
                     case SoundModel.TYPE_GENERIC_SOUND:
-                        ret = mSoundTriggerHelper.unloadGenericSoundModel(soundModel.uuid);
+                        ret = mSoundTriggerHelper.unloadGenericSoundModel(soundModel.getUuid());
                         break;
                     default:
                         Slog.e(TAG, "Unknown model type");
@@ -655,16 +663,16 @@
 
                     return ret;
                 }
-                switch (soundModel.type) {
+                switch (soundModel.getType()) {
                     case SoundModel.TYPE_GENERIC_SOUND:
-                        ret = mSoundTriggerHelper.getGenericModelState(soundModel.uuid);
+                        ret = mSoundTriggerHelper.getGenericModelState(soundModel.getUuid());
                         break;
                     default:
                         // SoundModel.TYPE_KEYPHRASE is not supported to increase privacy.
-                        Slog.e(TAG, "Unsupported model type, " + soundModel.type);
+                        Slog.e(TAG, "Unsupported model type, " + soundModel.getType());
                         sEventLogger.log(new SoundTriggerLogger.StringEvent(
                                 "getModelState(): Unsupported model type, "
-                                + soundModel.type));
+                                + soundModel.getType()));
                         break;
                 }
 
@@ -717,7 +725,7 @@
                     return STATUS_BAD_VALUE;
                 }
 
-                return mSoundTriggerHelper.setParameter(soundModel.uuid, modelParam, value);
+                return mSoundTriggerHelper.setParameter(soundModel.getUuid(), modelParam, value);
             }
         }
 
@@ -749,7 +757,7 @@
                     throw new IllegalArgumentException("sound model is not loaded");
                 }
 
-                return mSoundTriggerHelper.getParameter(soundModel.uuid, modelParam);
+                return mSoundTriggerHelper.getParameter(soundModel.getUuid(), modelParam);
             }
         }
 
@@ -780,7 +788,7 @@
                     return null;
                 }
 
-                return mSoundTriggerHelper.queryParameter(soundModel.uuid, modelParam);
+                return mSoundTriggerHelper.queryParameter(soundModel.getUuid(), modelParam);
             }
         }
     }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index be0987d..aaf7a9e 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -162,23 +162,26 @@
         synchronized(this) {
             SQLiteDatabase db = getWritableDatabase();
             ContentValues values = new ContentValues();
-            values.put(SoundModelContract.KEY_MODEL_UUID, soundModel.uuid.toString());
-            if (soundModel.vendorUuid != null) {
-                values.put(SoundModelContract.KEY_VENDOR_UUID, soundModel.vendorUuid.toString());
+            values.put(SoundModelContract.KEY_MODEL_UUID, soundModel.getUuid().toString());
+            if (soundModel.getVendorUuid() != null) {
+                values.put(SoundModelContract.KEY_VENDOR_UUID,
+                        soundModel.getVendorUuid().toString());
             }
             values.put(SoundModelContract.KEY_TYPE, SoundTrigger.SoundModel.TYPE_KEYPHRASE);
-            values.put(SoundModelContract.KEY_DATA, soundModel.data);
-            values.put(SoundModelContract.KEY_MODEL_VERSION, soundModel.version);
+            values.put(SoundModelContract.KEY_DATA, soundModel.getData());
+            values.put(SoundModelContract.KEY_MODEL_VERSION, soundModel.getVersion());
 
-            if (soundModel.keyphrases != null && soundModel.keyphrases.length == 1) {
-                values.put(SoundModelContract.KEY_KEYPHRASE_ID, soundModel.keyphrases[0].id);
+            if (soundModel.getKeyphrases() != null && soundModel.getKeyphrases().length == 1) {
+                values.put(SoundModelContract.KEY_KEYPHRASE_ID,
+                        soundModel.getKeyphrases()[0].getId());
                 values.put(SoundModelContract.KEY_RECOGNITION_MODES,
-                        soundModel.keyphrases[0].recognitionModes);
+                        soundModel.getKeyphrases()[0].getRecognitionModes());
                 values.put(SoundModelContract.KEY_USERS,
-                        getCommaSeparatedString(soundModel.keyphrases[0].users));
+                        getCommaSeparatedString(soundModel.getKeyphrases()[0].getUsers()));
                 values.put(SoundModelContract.KEY_LOCALE,
-                        soundModel.keyphrases[0].locale.toLanguageTag());
-                values.put(SoundModelContract.KEY_HINT_TEXT, soundModel.keyphrases[0].text);
+                        soundModel.getKeyphrases()[0].getLocale().toLanguageTag());
+                values.put(SoundModelContract.KEY_HINT_TEXT,
+                        soundModel.getKeyphrases()[0].getText());
                 try {
                     return db.insertWithOnConflict(SoundModelContract.TABLE, null, values,
                             SQLiteDatabase.CONFLICT_REPLACE) != -1;
@@ -206,7 +209,7 @@
             // Delete all sound models for the given keyphrase and specified user.
             SQLiteDatabase db = getWritableDatabase();
             String soundModelClause = SoundModelContract.KEY_MODEL_UUID
-                    + "='" + soundModel.uuid.toString() + "'";
+                    + "='" + soundModel.getUuid().toString() + "'";
             try {
                 return db.delete(SoundModelContract.TABLE, soundModelClause, null) != 0;
             } finally {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 18d5819..ef282ba 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -1045,12 +1045,12 @@
                     return null;
                 }
 
-                for (SoundTrigger.Keyphrase phrase : model.keyphrases) {
-                    if (keyphrase.equals(phrase.text)) {
+                for (SoundTrigger.Keyphrase phrase : model.getKeyphrases()) {
+                    if (keyphrase.equals(phrase.getText())) {
                         ArraySet<Locale> locales = new ArraySet<>();
-                        locales.add(phrase.locale);
-                        return new KeyphraseMetadata(phrase.id, phrase.text, locales,
-                                phrase.recognitionModes);
+                        locales.add(phrase.getLocale());
+                        return new KeyphraseMetadata(phrase.getId(), phrase.getText(), locales,
+                                phrase.getRecognitionModes());
                     }
                 }
             } finally {
@@ -1093,8 +1093,8 @@
                 KeyphraseSoundModel soundModel =
                         mDbHelper.getKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale);
                 if (soundModel == null
-                        || soundModel.uuid == null
-                        || soundModel.keyphrases == null) {
+                        || soundModel.getUuid() == null
+                        || soundModel.getKeyphrases() == null) {
                     Slog.w(TAG, "No matching sound model found in startRecognition");
                     return SoundTriggerInternal.STATUS_ERROR;
                 } else {
diff --git a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
index 488ee78..47bf148 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
@@ -23,6 +23,9 @@
 
 import com.android.server.wm.ActivityMetricsLaunchObserver;
 
+import java.io.StringWriter;
+import java.io.PrintWriter;
+
 /**
  * A validator to check the correctness of event sequence during app startup.
  *
@@ -100,7 +103,8 @@
   @Override
   public void onIntentStarted(@NonNull Intent intent, long timestampNs) {
     if (state == State.UNKNOWN) {
-      Log.wtf(TAG, "IntentStarted during UNKNOWN." + intent);
+      logWarningWithStackTrace(
+          String.format("IntentStarted during UNKNOWN. " + intent));
       incAccIntentStartedEvents();
       return;
     }
@@ -110,7 +114,7 @@
         state != State.ACTIVITY_CANCELLED &&
         state != State.ACTIVITY_FINISHED &&
         state != State.REPORT_FULLY_DRAWN) {
-      Log.wtf(TAG,
+      logWarningWithStackTrace(
           String.format("Cannot transition from %s to %s", state, State.INTENT_STARTED));
       incAccIntentStartedEvents();
       incAccIntentStartedEvents();
@@ -124,12 +128,12 @@
   @Override
   public void onIntentFailed() {
     if (state == State.UNKNOWN) {
-      Log.wtf(TAG, "IntentFailed during UNKNOWN.");
+      logWarningWithStackTrace(String.format("onIntentFailed during UNKNOWN."));
       decAccIntentStartedEvents();
       return;
     }
     if (state != State.INTENT_STARTED) {
-      Log.wtf(TAG,
+      logWarningWithStackTrace(
           String.format("Cannot transition from %s to %s", state, State.INTENT_FAILED));
       incAccIntentStartedEvents();
       return;
@@ -143,11 +147,12 @@
   public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
       @Temperature int temperature) {
     if (state == State.UNKNOWN) {
-      Log.wtf(TAG, "onActivityLaunched during UNKNOWN.");
+      logWarningWithStackTrace(
+          String.format("onActivityLaunched during UNKNOWN."));
       return;
     }
     if (state != State.INTENT_STARTED) {
-      Log.wtf(TAG,
+      logWarningWithStackTrace(
           String.format("Cannot transition from %s to %s", state, State.ACTIVITY_LAUNCHED));
       incAccIntentStartedEvents();
       return;
@@ -160,12 +165,13 @@
   @Override
   public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) {
     if (state == State.UNKNOWN) {
-      Log.wtf(TAG, "onActivityLaunchCancelled during UNKNOWN.");
+      logWarningWithStackTrace(
+          String.format("onActivityLaunchCancelled during UNKNOWN."));
       decAccIntentStartedEvents();
       return;
     }
     if (state != State.ACTIVITY_LAUNCHED) {
-      Log.wtf(TAG,
+      logWarningWithStackTrace(
           String.format("Cannot transition from %s to %s", state, State.ACTIVITY_CANCELLED));
       incAccIntentStartedEvents();
       return;
@@ -179,13 +185,14 @@
   public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity,
       long timestampNs) {
     if (state == State.UNKNOWN) {
-      Log.wtf(TAG, "onActivityLaunchFinished during UNKNOWN.");
+      logWarningWithStackTrace(
+          String.format("onActivityLaunchFinished during UNKNOWN."));
       decAccIntentStartedEvents();
       return;
     }
 
     if (state != State.ACTIVITY_LAUNCHED) {
-      Log.wtf(TAG,
+      logWarningWithStackTrace(
           String.format("Cannot transition from %s to %s", state, State.ACTIVITY_FINISHED));
       incAccIntentStartedEvents();
       return;
@@ -199,7 +206,8 @@
   public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
       long timestampNs) {
     if (state == State.UNKNOWN) {
-      Log.wtf(TAG, "onReportFullyDrawn during UNKNOWN.");
+      logWarningWithStackTrace(
+          String.format("onReportFullyDrawn during UNKNOWN."));
       return;
     }
     if (state == State.INIT) {
@@ -207,7 +215,7 @@
     }
 
     if (state != State.ACTIVITY_FINISHED) {
-      Log.wtf(TAG,
+      logWarningWithStackTrace(
           String.format("Cannot transition from %s to %s", state, State.REPORT_FULLY_DRAWN));
       return;
     }
@@ -252,4 +260,11 @@
     Log.i(TAG,
         String.format("dec AccIntentStartedEvents to %d", accIntentStartedEvents));
   }
+
+  private void logWarningWithStackTrace(String log) {
+    StringWriter sw = new StringWriter();
+    PrintWriter pw = new PrintWriter(sw);
+    new Throwable("EventSequenceValidator#getStackTrace").printStackTrace(pw);
+    Log.w(TAG, String.format("%s\n%s", log, sw));
+  }
 }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 0dca006..ffd25c0 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -2633,6 +2633,7 @@
      * @param request Details about the incoming call.
      * @return The {@code Connection} object to satisfy this call, or {@code null} to
      *         not handle the call.
+     * @hide
      */
     public @Nullable Conference onCreateIncomingConference(
             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
@@ -2717,6 +2718,7 @@
      * @param connectionManagerPhoneAccount See description at
      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
      * @param request The incoming connection request.
+     * @hide
      */
     public void onCreateIncomingConferenceFailed(
             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
@@ -2737,6 +2739,7 @@
      * @param connectionManagerPhoneAccount See description at
      *         {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
      * @param request The outgoing connection request.
+     * @hide
      */
     public void onCreateOutgoingConferenceFailed(
             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
@@ -2805,6 +2808,7 @@
      * @param request Details about the outgoing call.
      * @return The {@code Conference} object to satisfy this call, or the result of an invocation
      *         of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
+     * @hide
      */
     public @Nullable Conference onCreateOutgoingConference(
             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 0b33174..1a38a42 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -361,7 +361,8 @@
             TelephonyCommonStatsLog.write(TelephonyCommonStatsLog.DEVICE_IDENTIFIER_ACCESS_DENIED,
                     callingPackage, message, /* isPreinstalled= */ false, false);
         }
-        Log.w(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message);
+        Log.w(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message + ":"
+                + subId);
         // if the target SDK is pre-Q then check if the calling package would have previously
         // had access to device identifiers.
         if (callingPackageInfo != null && (
@@ -442,16 +443,40 @@
         // NOTE(b/73308711): If an app has one of the following AppOps bits explicitly revoked, they
         // will be denied access, even if they have another permission and AppOps bit if needed.
 
-        // First, check if we can read the phone state and the SDK version is below R.
+        // First, check if the SDK version is below R
+        boolean preR = false;
         try {
             ApplicationInfo info = context.getPackageManager().getApplicationInfoAsUser(
                     callingPackage, 0, UserHandle.getUserHandleForUid(Binder.getCallingUid()));
-            if (info.targetSdkVersion <= Build.VERSION_CODES.Q) {
+            preR = info.targetSdkVersion <= Build.VERSION_CODES.Q;
+        } catch (PackageManager.NameNotFoundException nameNotFoundException) {
+        }
+        if (preR) {
+            // SDK < R allows READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or carrier privilege
+            try {
                 return checkReadPhoneState(
                         context, subId, pid, uid, callingPackage, callingFeatureId, message);
+            } catch (SecurityException readPhoneStateException) {
             }
-        } catch (SecurityException | PackageManager.NameNotFoundException e) {
+        } else {
+            // SDK >= R allows READ_PRIVILEGED_PHONE_STATE or carrier privilege
+            try {
+                context.enforcePermission(
+                        android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message);
+                // Skip checking for runtime permission since caller has privileged permission
+                return true;
+            } catch (SecurityException readPrivilegedPhoneStateException) {
+                if (SubscriptionManager.isValidSubscriptionId(subId)) {
+                    try {
+                        enforceCarrierPrivilege(context, subId, uid, message);
+                        // Skip checking for runtime permission since caller has carrier privilege
+                        return true;
+                    } catch (SecurityException carrierPrivilegeException) {
+                    }
+                }
+            }
         }
+
         // Can be read with READ_SMS too.
         try {
             context.enforcePermission(android.Manifest.permission.READ_SMS, pid, uid, message);
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index 558f4cd..39a7543 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -19,9 +19,9 @@
 import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.hardware.radio.V1_1.EutranBands;
 import android.hardware.radio.V1_1.GeranBands;
 import android.hardware.radio.V1_5.AccessNetwork;
+import android.hardware.radio.V1_5.EutranBands;
 import android.hardware.radio.V1_5.UtranBands;
 
 import java.lang.annotation.Retention;
@@ -212,7 +212,8 @@
 
     /**
      * Frequency bands for EUTRAN.
-     * http://www.etsi.org/deliver/etsi_ts/136100_136199/136101/14.03.00_60/ts_136101v140p.pdf
+     * 3GPP TS 36.101, Version 16.4.0, Table 5.5: Operating bands
+     * https://www.etsi.org/deliver/etsi_ts/136100_136199/136101/15.09.00_60/ts_136101v150900p.pdf
      */
     public static final class EutranBand {
         public static final int BAND_1 = EutranBands.BAND_1;
@@ -259,10 +260,22 @@
         public static final int BAND_46 = EutranBands.BAND_46;
         public static final int BAND_47 = EutranBands.BAND_47;
         public static final int BAND_48 = EutranBands.BAND_48;
+        public static final int BAND_49 = EutranBands.BAND_49;
+        public static final int BAND_50 = EutranBands.BAND_50;
+        public static final int BAND_51 = EutranBands.BAND_51;
+        public static final int BAND_52 = EutranBands.BAND_52;
+        public static final int BAND_53 = EutranBands.BAND_53;
         public static final int BAND_65 = EutranBands.BAND_65;
         public static final int BAND_66 = EutranBands.BAND_66;
         public static final int BAND_68 = EutranBands.BAND_68;
         public static final int BAND_70 = EutranBands.BAND_70;
+        public static final int BAND_71 = EutranBands.BAND_71;
+        public static final int BAND_72 = EutranBands.BAND_72;
+        public static final int BAND_73 = EutranBands.BAND_73;
+        public static final int BAND_74 = EutranBands.BAND_74;
+        public static final int BAND_85 = EutranBands.BAND_85;
+        public static final int BAND_87 = EutranBands.BAND_87;
+        public static final int BAND_88 = EutranBands.BAND_88;
 
         /** @hide */
         private EutranBand() {};
@@ -305,9 +318,11 @@
 
     /**
      * Frequency bands for NGRAN
+     * https://www.etsi.org/deliver/etsi_ts/138100_138199/13810101/15.08.02_60/ts_13810101v150802p.pdf
+     * https://www.etsi.org/deliver/etsi_ts/138100_138199/13810102/15.08.00_60/ts_13810102v150800p.pdf
      */
     public static final class NgranBands {
-        /** FR1 bands */
+        /** 3GPP TS 38.101-1, Version 16.2.0, Table 5.2-1: FR1 bands */
         public static final int BAND_1 = android.hardware.radio.V1_5.NgranBands.BAND_1;
         public static final int BAND_2 = android.hardware.radio.V1_5.NgranBands.BAND_2;
         public static final int BAND_3 = android.hardware.radio.V1_5.NgranBands.BAND_3;
@@ -346,9 +361,15 @@
         public static final int BAND_83 = android.hardware.radio.V1_5.NgranBands.BAND_83;
         public static final int BAND_84 = android.hardware.radio.V1_5.NgranBands.BAND_84;
         public static final int BAND_86 = android.hardware.radio.V1_5.NgranBands.BAND_86;
+        public static final int BAND_89 = android.hardware.radio.V1_5.NgranBands.BAND_89;
         public static final int BAND_90 = android.hardware.radio.V1_5.NgranBands.BAND_90;
+        public static final int BAND_91 = android.hardware.radio.V1_5.NgranBands.BAND_91;
+        public static final int BAND_92 = android.hardware.radio.V1_5.NgranBands.BAND_92;
+        public static final int BAND_93 = android.hardware.radio.V1_5.NgranBands.BAND_93;
+        public static final int BAND_94 = android.hardware.radio.V1_5.NgranBands.BAND_94;
+        public static final int BAND_95 = android.hardware.radio.V1_5.NgranBands.BAND_95;
 
-        /** FR2 bands */
+        /** 3GPP TS 38.101-2, Version 16.2.0, Table 5.2-1: FR2 bands */
         public static final int BAND_257 = android.hardware.radio.V1_5.NgranBands.BAND_257;
         public static final int BAND_258 = android.hardware.radio.V1_5.NgranBands.BAND_258;
         public static final int BAND_260 = android.hardware.radio.V1_5.NgranBands.BAND_260;
@@ -398,7 +419,13 @@
                         BAND_83,
                         BAND_84,
                         BAND_86,
+                        BAND_89,
                         BAND_90,
+                        BAND_91,
+                        BAND_92,
+                        BAND_93,
+                        BAND_94,
+                        BAND_95,
                         BAND_257,
                         BAND_258,
                         BAND_260,
@@ -495,7 +522,13 @@
                 case BAND_83:
                 case BAND_84:
                 case BAND_86:
+                case BAND_89:
                 case BAND_90:
+                case BAND_91:
+                case BAND_92:
+                case BAND_93:
+                case BAND_94:
+                case BAND_95:
                     return FREQUENCY_RANGE_GROUP_1;
                 case BAND_257:
                 case BAND_258:
diff --git a/telephony/java/android/telephony/AccessNetworkUtils.java b/telephony/java/android/telephony/AccessNetworkUtils.java
index 5d2c225..981ed450 100644
--- a/telephony/java/android/telephony/AccessNetworkUtils.java
+++ b/telephony/java/android/telephony/AccessNetworkUtils.java
@@ -34,12 +34,10 @@
             return DUPLEX_MODE_UNKNOWN;
         }
 
-        if (band >= EutranBand.BAND_68) {
+        if (band > EutranBand.BAND_88) {
             return DUPLEX_MODE_UNKNOWN;
         } else if (band >= EutranBand.BAND_65) {
             return DUPLEX_MODE_FDD;
-        } else if (band >= EutranBand.BAND_47) {
-            return DUPLEX_MODE_UNKNOWN;
         } else if (band >= EutranBand.BAND_33) {
             return DUPLEX_MODE_TDD;
         } else if (band >= EutranBand.BAND_1) {
@@ -58,17 +56,53 @@
      * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists
      */
     public static int getOperatingBandForEarfcn(int earfcn) {
-        if (earfcn > 67535) {
+        if (earfcn > 70645) {
             return INVALID_BAND;
+        } else if (earfcn >= 70596) {
+            return EutranBand.BAND_88;
+        } else if (earfcn >= 70546) {
+            return EutranBand.BAND_87;
+        } else if (earfcn >= 70366) {
+            return EutranBand.BAND_85;
+        } else if (earfcn > 69465) {
+            return INVALID_BAND;
+        } else if (earfcn >= 69036) {
+            return EutranBand.BAND_74;
+        } else if (earfcn >= 68986) {
+            return EutranBand.BAND_73;
+        } else if (earfcn >= 68936) {
+            return EutranBand.BAND_72;
+        } else if (earfcn >= 68586) {
+            return EutranBand.BAND_71;
+        } else if (earfcn >= 68336) {
+            return EutranBand.BAND_70;
+        } else if (earfcn > 67835) {
+            return INVALID_BAND;
+        } else if (earfcn >= 67536) {
+            return EutranBand.BAND_68;
         } else if (earfcn >= 67366) {
             return INVALID_BAND; // band 67 only for CarrierAgg
         } else if (earfcn >= 66436) {
             return EutranBand.BAND_66;
         } else if (earfcn >= 65536) {
             return EutranBand.BAND_65;
-        } else if (earfcn > 54339) {
+        } else if (earfcn > 60254) {
             return INVALID_BAND;
-        } else if (earfcn >= 46790 /* inferred from the end range of BAND_45 */) {
+        } else if (earfcn >= 60140) {
+            return EutranBand.BAND_53;
+        } else if (earfcn >= 59140) {
+            return EutranBand.BAND_52;
+        } else if (earfcn >= 59090) {
+            return EutranBand.BAND_51;
+        } else if (earfcn >= 58240) {
+            return EutranBand.BAND_50;
+        } else if (earfcn >= 56740) {
+            return EutranBand.BAND_49;
+        } else if (earfcn >= 55240) {
+            return EutranBand.BAND_48;
+        } else if (earfcn >= 54540) {
+            return EutranBand.BAND_47;
+        } else if (earfcn >= 46790) {
             return EutranBand.BAND_46;
         } else if (earfcn >= 46590) {
             return EutranBand.BAND_45;
diff --git a/telephony/java/android/telephony/CellSignalStrengthCdma.java b/telephony/java/android/telephony/CellSignalStrengthCdma.java
index 1c92705b..d00049c 100644
--- a/telephony/java/android/telephony/CellSignalStrengthCdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthCdma.java
@@ -314,6 +314,8 @@
 
     /**
      * Get the signal strength as dBm
+     *
+     * @return min(CDMA RSSI, EVDO RSSI) of the measured cell.
      */
     @Override
     public int getDbm() {
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 76d2df9..9d55f10 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -145,6 +145,8 @@
 
     /**
      * Get the signal strength as dBm.
+     *
+     * @return the RSSI of the measured cell.
      */
     @Override
     public int getDbm() {
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index c144746..991375c 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2520,7 +2520,6 @@
      * @param sentIntent if not NULL this <code>PendingIntent</code> is
      *  broadcast when the message is successfully sent, or failed
      * @throws IllegalArgumentException if contentUri is empty
-     * @deprecated use {@link MmsManager#sendMultimediaMessage} instead.
      */
     public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl,
             Bundle configOverrides, PendingIntent sentIntent) {
@@ -2555,7 +2554,6 @@
      * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
      *  broadcast when the message is downloaded, or the download is failed
      * @throws IllegalArgumentException if locationUrl or contentUri is empty
-     * @deprecated use {@link MmsManager#downloadMultimediaMessage} instead.
      */
     public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri,
             Bundle configOverrides, PendingIntent downloadedIntent) {
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 5d3cc44..2facd5a 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -944,6 +944,18 @@
             if (DBG) log("onSubscriptionsChanged: NOT OVERRIDDEN");
         }
 
+        /**
+         * Callback invoked when {@link SubscriptionManager#addOnSubscriptionsChangedListener(
+         * Executor, OnSubscriptionsChangedListener)} or
+         * {@link SubscriptionManager#addOnSubscriptionsChangedListener(
+         * OnSubscriptionsChangedListener)} fails to complete due to the
+         * {@link Context#TELEPHONY_REGISTRY_SERVICE} being unavailable.
+         * @hide
+         */
+        public void onAddListenerFailed() {
+            Rlog.w(LOG_TAG, "onAddListenerFailed not overridden");
+        }
+
         private void log(String s) {
             Rlog.d(LOG_TAG, s);
         }
@@ -1012,6 +1024,12 @@
         if (telephonyRegistryManager != null) {
             telephonyRegistryManager.addOnSubscriptionsChangedListener(listener,
                     executor);
+        } else {
+            // If the telephony registry isn't available, we will inform the caller on their
+            // listener that it failed so they can try to re-register.
+            loge("addOnSubscriptionsChangedListener: pkgname=" + pkgName + " failed to be added "
+                    + " due to TELEPHONY_REGISTRY_SERVICE being unavailable.");
+            executor.execute(() -> listener.onAddListenerFailed());
         }
     }
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 5a7b852..211f872 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -12268,6 +12268,17 @@
             "android.telephony.extra.NETWORK_COUNTRY";
 
     /**
+     * The extra used with an {@link #ACTION_NETWORK_COUNTRY_CHANGED} to specify the
+     * last known the country code in ISO-3166-1 alpha-2 format.
+     * <p class="note">
+     * Retrieve with {@link android.content.Intent#getStringExtra(String)}.
+     *
+     * @hide
+     */
+    public static final String EXTRA_LAST_KNOWN_NETWORK_COUNTRY =
+            "android.telephony.extra.LAST_KNOWN_NETWORK_COUNTRY";
+
+    /**
      * Indicate if the user is allowed to use multiple SIM cards at the same time to register
      * on the network (e.g. Dual Standby or Dual Active) when the device supports it, or if the
      * usage is restricted. This API is used to prevent usage of multiple SIM card, based on
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 3e1d72c..2b1d9e5 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -74,7 +74,6 @@
     public static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER;
     public static final int EVENT_DATA_SETUP_COMPLETE = BASE + 0;
     public static final int EVENT_RADIO_AVAILABLE = BASE + 1;
-    public static final int EVENT_RECORDS_LOADED = BASE + 2;
     public static final int EVENT_TRY_SETUP_DATA = BASE + 3;
     public static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = BASE + 6;
     public static final int EVENT_VOICE_CALL_STARTED = BASE + 7;
@@ -94,7 +93,6 @@
     public static final int EVENT_CLEAN_UP_CONNECTION = BASE + 24;
     public static final int EVENT_RESTART_RADIO = BASE + 26;
     public static final int EVENT_CLEAN_UP_ALL_CONNECTIONS = BASE + 29;
-    public static final int EVENT_ICC_CHANGED = BASE + 33;
     public static final int EVENT_DATA_SETUP_COMPLETE_ERROR = BASE + 35;
     public static final int CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA = BASE + 36;
     public static final int CMD_ENABLE_MOBILE_PROVISIONING = BASE + 37;
@@ -114,7 +112,7 @@
     public static final int EVENT_SERVICE_STATE_CHANGED = BASE + 52;
     public static final int EVENT_5G_TIMER_HYSTERESIS = BASE + 53;
     public static final int EVENT_5G_TIMER_WATCHDOG = BASE + 54;
-    public static final int EVENT_UPDATE_CARRIER_CONFIGS = BASE + 55;
+    public static final int EVENT_CARRIER_CONFIG_CHANGED = BASE + 55;
 
     /***** Constants *****/
 
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index 784ee85..cf3b03c 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -928,4 +928,10 @@
     public Handler getMainThreadHandler() {
         throw new UnsupportedOperationException();
     }
+
+    /** {@hide} */
+    @Override
+    public boolean isUiContext() {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/test-runner/src/android/test/TouchUtils.java b/test-runner/src/android/test/TouchUtils.java
index bb4e00b..f2f0be7 100644
--- a/test-runner/src/android/test/TouchUtils.java
+++ b/test-runner/src/android/test/TouchUtils.java
@@ -223,7 +223,7 @@
     public static void dragViewToBottom(InstrumentationTestCase test, Activity activity, View v,
             int stepCount) {
         int screenHeight =
-                activity.getWindowManager().getCurrentWindowMetrics().getSize().getHeight();
+                activity.getWindowManager().getCurrentWindowMetrics().getBounds().height();
 
         int[] xy = new int[2];
         v.getLocationOnScreen(xy);
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 2f9a1c8..0ad3039 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -46,6 +46,7 @@
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
@@ -72,6 +73,7 @@
     private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts";
     private static final String KEY_APPS = "apps";
     private static final String KEY_IORAP_TRIAL_LAUNCH = "iorap_trial_launch";
+    private static final String KEY_IORAP_COMPILER_FILTERS = "iorap_compiler_filters";
     private static final String KEY_TRIAL_LAUNCH = "trial_launch";
     private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations";
     private static final String KEY_LAUNCH_ORDER = "launch_order";
@@ -153,6 +155,7 @@
     private BufferedWriter mBufferedWriter = null;
     private boolean mSimplePerfAppOnly = false;
     private String[] mCompilerFilters = null;
+    private List<String> mIorapCompilerFilters = null;
     private String mLastAppName = "";
     private boolean mCycleCleanUp = false;
     private boolean mTraceAll = false;
@@ -509,16 +512,68 @@
         for (int i = 0; i < IORAP_COMPILE_CMD_TIMEOUT; ++i) {
             IorapCompilationStatus status = waitForIorapCompiled(appPkgName);
             if (status == IorapCompilationStatus.COMPLETE) {
+                Log.v(TAG, "compileAppForIorap: success");
+                logDumpsysIorapd(appPkgName);
                 return true;
             } else if (status == IorapCompilationStatus.INSUFFICIENT_TRACES) {
+                Log.e(TAG, "compileAppForIorap: failed due to insufficient traces");
+                logDumpsysIorapd(appPkgName);
                 return false;
             } // else INCOMPLETE. keep asking iorapd if it's done yet.
             sleep(1000);
         }
 
+        Log.e(TAG, "compileAppForIorap: failed due to timeout");
+        logDumpsysIorapd(appPkgName);
         return false;
     }
 
+    /** Save the contents of $(adb shell dumpsys iorapd) to the launch_logs directory. */
+    private void logDumpsysIorapd(String packageName) throws IOException {
+        InstrumentationTestRunner instrumentation =
+                (InstrumentationTestRunner)getInstrumentation();
+        Bundle args = instrumentation.getArguments();
+
+        String launchDirectory = args.getString(KEY_LAUNCH_DIRECTORY);
+
+        // Root directory for applaunch file to log the app launch output
+        // Will be useful in case of simpleperf command is used
+        File launchRootDir = null;
+        if (null != launchDirectory && !launchDirectory.isEmpty()) {
+            launchRootDir = new File(launchDirectory);
+            if (!launchRootDir.exists() && !launchRootDir.mkdirs()) {
+                throw new IOException("Unable to create the destination directory "
+                    + launchRootDir + ". Try disabling selinux.");
+            }
+        } else {
+            Log.w(TAG, "logDumpsysIorapd: Missing launch-directory arg");
+            return;
+        }
+
+        File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
+
+        if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
+            throw new IOException("Unable to create the lauch file sub directory "
+                + launchSubDir + ". Try disabling selinux.");
+        }
+        String path = "iorapd_dumpsys_" + packageName + "_" + System.nanoTime() + ".txt";
+        File file = new File(launchSubDir, path);
+        try (FileOutputStream outputStream = new FileOutputStream(file);
+                BufferedWriter writer = new BufferedWriter(
+                        new OutputStreamWriter(outputStream));
+                ParcelFileDescriptor result = getInstrumentation().getUiAutomation().
+                        executeShellCommand(IORAP_DUMPSYS_CMD);
+                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
+                        new FileInputStream(result.getFileDescriptor())))) {
+            String line;
+            while ((line = bufferedReader.readLine()) != null) {
+                writer.write(line + "\n");
+            }
+        }
+
+        Log.v(TAG, "logDumpsysIorapd: Saved to file: " + path);
+    }
+
     enum IorapCompilationStatus {
         INCOMPLETE,
         COMPLETE,
@@ -566,6 +621,24 @@
         return reason;
     }
 
+    private boolean shouldIncludeIorap(String compilerFilter) {
+        if (!mIorapTrialLaunch) {
+            return false;
+        }
+
+        // No iorap compiler filters specified: treat all compiler filters as ok.
+        if (mIorapCompilerFilters == null) {
+            return true;
+        }
+
+        // iorap compiler filters specified: the compilerFilter must be in the whitelist.
+        if (mIorapCompilerFilters.indexOf(compilerFilter) != -1) {
+            return true;
+        }
+
+        return false;
+    }
+
     /**
      * If launch order is "cyclic" then apps will be launched one after the
      * other for each iteration count.
@@ -580,7 +653,7 @@
                         mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH, /*iorapEnabled*/false));
                     }
                 }
-                if (mIorapTrialLaunch) {
+                if (shouldIncludeIorap(compilerFilter)) {
                     for (int launchCount = 0; launchCount < IORAP_TRIAL_LAUNCH_ITERATIONS; ++launchCount) {
                         for (String app : mNameToResultKey.keySet()) {
                             String reason = makeReasonForIorapTrialLaunch(launchCount);
@@ -594,14 +667,16 @@
                 for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
                     for (String app : mNameToResultKey.keySet()) {
                         mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
-                                  String.format(LAUNCH_ITERATION, launchCount), mIorapTrialLaunch));
+                                  String.format(LAUNCH_ITERATION, launchCount),
+                                        shouldIncludeIorap(compilerFilter)));
                     }
                 }
                 if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
                     for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
                         for (String app : mNameToResultKey.keySet()) {
                             mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
-                                      String.format(TRACE_ITERATION, traceCount), mIorapTrialLaunch));
+                                      String.format(TRACE_ITERATION, traceCount),
+                                            shouldIncludeIorap(compilerFilter)));
                         }
                     }
                 }
@@ -612,7 +687,7 @@
                     if (mTrialLaunch) {
                         mLaunchOrderList.add(new LaunchOrder(app, compilerFilter, TRIAL_LAUNCH, /*iorapEnabled*/false));
                     }
-                    if (mIorapTrialLaunch) {
+                    if (shouldIncludeIorap(compilerFilter)) {
                         for (int launchCount = 0; launchCount < IORAP_TRIAL_LAUNCH_ITERATIONS; ++launchCount) {
                             String reason = makeReasonForIorapTrialLaunch(launchCount);
                             mLaunchOrderList.add(
@@ -623,12 +698,14 @@
                     }
                     for (int launchCount = 0; launchCount < mLaunchIterations; launchCount++) {
                         mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
-                                String.format(LAUNCH_ITERATION, launchCount), mIorapTrialLaunch));
+                                String.format(LAUNCH_ITERATION, launchCount),
+                                        shouldIncludeIorap(compilerFilter)));
                     }
                     if (mTraceDirectoryStr != null && !mTraceDirectoryStr.isEmpty()) {
                         for (int traceCount = 0; traceCount < mTraceLaunchCount; traceCount++) {
                             mLaunchOrderList.add(new LaunchOrder(app, compilerFilter,
-                                    String.format(TRACE_ITERATION, traceCount), mIorapTrialLaunch));
+                                    String.format(TRACE_ITERATION, traceCount),
+                                            shouldIncludeIorap(compilerFilter)));
                         }
                     }
                 }
@@ -715,7 +792,7 @@
                 .executeShellCommand(String.format("setprop iorapd.readahead.enable %b", enable));
         getInstrumentation().getUiAutomation()
                 .executeShellCommand("start iorapd");
-        sleep(2000);  // give enough time for iorapd to start back up.
+        sleep(3000);  // give enough time for iorapd to start back up.
 
         if (enable) {
             mIorapStatus = IorapStatus.ENABLED;
@@ -770,6 +847,13 @@
             mCompilerFilters = new String[1];
         }
 
+        String iorapCompilerFilterList = args.getString(KEY_IORAP_COMPILER_FILTERS);
+        if (iorapCompilerFilterList != null) {
+            // Passing in iorap compiler filters implies an iorap trial launch.
+            mIorapTrialLaunch = true;
+            mIorapCompilerFilters = Arrays.asList(iorapCompilerFilterList.split("\\|"));
+        }
+
         // Pre-populate the results map to avoid null checks.
         for (String app : mNameToLaunchTime.keySet()) {
             HashMap<String, List<AppLaunchResult>> map = new HashMap<>();
diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
index 42908be..35a6c26 100644
--- a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
+++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java
@@ -167,7 +167,7 @@
 
         final byte[] actualBytes = new byte[lengthBytes];
         try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
-                session.openWrite(0L, 0L))) {
+                session.openRead())) {
             read(in, actualBytes, offsetBytes, lengthBytes);
         }
 
@@ -190,7 +190,7 @@
             long offsetBytes, long lengthBytes) throws Exception {
         final byte[] actualDigest;
         try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
-                session.openWrite(0L, 0L))) {
+                session.openRead())) {
             actualDigest = createSha256Digest(in, offsetBytes, lengthBytes);
         }
 
diff --git a/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java b/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java
index e255ce2..31532a2 100644
--- a/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java
+++ b/tests/MirrorSurfaceTest/src/com/google/android/test/mirrorsurface/MirrorSurfaceActivity.java
@@ -27,7 +27,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
-import android.util.Size;
 import android.view.Gravity;
 import android.view.IWindowManager;
 import android.view.MotionEvent;
@@ -90,8 +89,8 @@
                 .getSystemService(WindowManager.class);
         mIWm = WindowManagerGlobal.getWindowManagerService();
 
-        Size windowSize = mWm.getCurrentWindowMetrics().getSize();
-        mWindowBounds.set(0, 0, windowSize.getWidth(), windowSize.getHeight());
+        Rect windowBounds = mWm.getCurrentWindowMetrics().getBounds();
+        mWindowBounds.set(0, 0, windowBounds.width(), windowBounds.height());
 
         mScaleText = findViewById(R.id.scale);
         mDisplayFrameText = findViewById(R.id.displayFrame);
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 2957192..d011dbb 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -1123,6 +1123,28 @@
         assertThat(testController.getSyncRequests()).isEqualTo(expectedSyncRequests);
     }
 
+    /**
+     * Ensure that the failure history of a package is preserved when making duplicate calls to
+     * observe the package.
+     */
+    @Test
+    public void testFailureHistoryIsPreserved() {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver observer = new TestObserver(OBSERVER_NAME_1);
+        watchdog.startObservingHealth(observer, List.of(APP_A), SHORT_DURATION);
+        for (int i = 0; i < PackageWatchdog.DEFAULT_TRIGGER_FAILURE_COUNT - 1; i++) {
+            watchdog.onPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)),
+                    PackageWatchdog.FAILURE_REASON_UNKNOWN);
+        }
+        mTestLooper.dispatchAll();
+        assertThat(observer.mMitigatedPackages).isEmpty();
+        watchdog.startObservingHealth(observer, List.of(APP_A), LONG_DURATION);
+        watchdog.onPackageFailure(List.of(new VersionedPackage(APP_A, VERSION_CODE)),
+                PackageWatchdog.FAILURE_REASON_UNKNOWN);
+        mTestLooper.dispatchAll();
+        assertThat(observer.mMitigatedPackages).isEqualTo(List.of(APP_A));
+    }
+
     private void adoptShellPermissions(String... permissions) {
         InstrumentationRegistry
                 .getInstrumentation()
diff --git a/tests/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml
index 269cec1..7b85cc8 100644
--- a/tests/RollbackTest/RollbackTest.xml
+++ b/tests/RollbackTest/RollbackTest.xml
@@ -23,6 +23,10 @@
         <option name="run-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\\*' --esa flags &quot;ModuleConfig__versioned_immediate_commit_packages&quot; --esa types &quot;bytes&quot; --esa values &quot;Cm5vdGFwYWNrYWdlOgA=&quot; com.google.android.gms" />
         <option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\*' --esa flag &quot;ModuleConfig__immediate_commit_packages&quot; com.google.android.gms" />
         <option name="teardown-command" value="am broadcast -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' --es action delete --es package &quot;com.google.android.gms.platformconfigurator&quot; --es user '\*' --esa flag &quot;ModuleConfig__versioned_immediate_commit_packages&quot; com.google.android.gms" />
+        <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+        <option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
+        <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
+        <option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.tests.rollback" />
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 5a92d68..cab8b42 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -75,6 +75,12 @@
     private static final String PROPERTY_ENABLE_ROLLBACK_TIMEOUT_MILLIS =
             "enable_rollback_timeout";
 
+    private static boolean hasRollbackInclude(List<RollbackInfo> rollbacks, String packageName) {
+        return rollbacks.stream().anyMatch(
+                ri -> ri.getPackages().stream().anyMatch(
+                        pri -> packageName.equals(pri.getPackageName())));
+    }
+
     /**
      * Test basic rollbacks.
      */
@@ -113,18 +119,14 @@
             // Uninstall TestApp.A
             Uninstall.packages(TestApp.A);
             assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(-1);
-            // TODO: There is currently a race condition between when the app is
-            // uninstalled and when rollback manager deletes the rollback. Fix it
-            // so that's not the case!
             for (int i = 0; i < 5; ++i) {
-                RollbackInfo rollback = getUniqueRollbackInfoForPackage(
-                        rm.getRecentlyCommittedRollbacks(), TestApp.A);
-                if (rollback != null) {
+                if (hasRollbackInclude(rm.getRecentlyCommittedRollbacks(), TestApp.A)) {
                     Log.i(TAG, "Sleeping 1 second to wait for uninstall to take effect.");
                     Thread.sleep(1000);
                 }
             }
 
+            assertThat(hasRollbackInclude(rm.getRecentlyCommittedRollbacks(), TestApp.A)).isFalse();
             // The app should not be available for rollback.
             waitForUnavailableRollback(TestApp.A);
 
diff --git a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
index b185a26..9324ba0 100644
--- a/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
+++ b/tests/SoundTriggerTestApp/src/com/android/test/soundtrigger/SoundTriggerTestService.java
@@ -250,10 +250,12 @@
 
         boolean status = mSoundTriggerUtil.addOrUpdateSoundModel(soundModel);
         if (status) {
-            postToast("Successfully loaded " + modelInfo.name + ", UUID=" + soundModel.uuid);
+            postToast("Successfully loaded " + modelInfo.name + ", UUID="
+                    + soundModel.getUuid());
             setModelState(modelInfo, "Loaded");
         } else {
-            postErrorToast("Failed to load " + modelInfo.name + ", UUID=" + soundModel.uuid + "!");
+            postErrorToast("Failed to load " + modelInfo.name + ", UUID="
+                    + soundModel.getUuid() + "!");
             setModelState(modelInfo, "Failed to load");
         }
     }
@@ -275,11 +277,12 @@
         modelInfo.detector = null;
         boolean status = mSoundTriggerUtil.deleteSoundModel(modelUuid);
         if (status) {
-            postToast("Successfully unloaded " + modelInfo.name + ", UUID=" + soundModel.uuid);
+            postToast("Successfully unloaded " + modelInfo.name + ", UUID="
+                    + soundModel.getUuid());
             setModelState(modelInfo, "Unloaded");
         } else {
             postErrorToast("Failed to unload " +
-                    modelInfo.name + ", UUID=" + soundModel.uuid + "!");
+                    modelInfo.name + ", UUID=" + soundModel.getUuid() + "!");
             setModelState(modelInfo, "Failed to unload");
         }
     }
@@ -299,7 +302,8 @@
         GenericSoundModel updated = createNewSoundModel(modelInfo);
         boolean status = mSoundTriggerUtil.addOrUpdateSoundModel(updated);
         if (status) {
-            postToast("Successfully reloaded " + modelInfo.name + ", UUID=" + modelInfo.modelUuid);
+            postToast("Successfully reloaded " + modelInfo.name + ", UUID="
+                    + modelInfo.modelUuid);
             setModelState(modelInfo, "Reloaded");
         } else {
             postErrorToast("Failed to reload "
@@ -321,7 +325,8 @@
                     modelUuid, new DetectorCallback(modelInfo));
         }
 
-        postMessage("Starting recognition for " + modelInfo.name + ", UUID=" + modelInfo.modelUuid);
+        postMessage("Starting recognition for " + modelInfo.name + ", UUID="
+                + modelInfo.modelUuid);
         if (modelInfo.detector.startRecognition(modelInfo.captureAudio ?
                 SoundTriggerDetector.RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO :
                 SoundTriggerDetector.RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS)) {
@@ -495,7 +500,8 @@
 
             if (properties.containsKey("dataFile")) {
                 File modelDataFile = new File(
-                        getFilesDir().getPath() + "/" + properties.getProperty("dataFile"));
+                        getFilesDir().getPath() + "/"
+                                + properties.getProperty("dataFile"));
                 modelInfo.modelData = new byte[(int) modelDataFile.length()];
                 FileInputStream input = new FileInputStream(modelDataFile);
                 input.read(modelInfo.modelData, 0, modelInfo.modelData.length);
@@ -602,12 +608,14 @@
                 FileOutputStream fos  = null;
                 try {
                     fos = new FileOutputStream( new File(
-                            getFilesDir() + File.separator + mModelInfo.name.replace(' ', '_') +
-                                    "_capture_" + format.getChannelCount() + "ch_" +
-                                    format.getSampleRate() + "hz_" + encoding + ".pcm"));
+                            getFilesDir() + File.separator
+                                    + mModelInfo.name.replace(' ', '_')
+                                    + "_capture_" + format.getChannelCount() + "ch_"
+                                    + format.getSampleRate() + "hz_" + encoding + ".pcm"));
                 } catch (IOException e) {
                     Log.e(TAG, "Failed to open output for saving PCM data", e);
-                    postErrorToast("Failed to open output for saving PCM data: " + e.getMessage());
+                    postErrorToast("Failed to open output for saving PCM data: "
+                            + e.getMessage());
                 }
 
                 // Inform the user we're recording.
@@ -690,7 +698,8 @@
         AudioFormat format =  event.getCaptureAudioFormat();
         result = result + "AudioFormat: " + ((format == null) ? "null" : format.toString());
         byte[] triggerAudio = event.getTriggerAudio();
-        result = result + ", TriggerAudio: " + (triggerAudio == null ? "null" : triggerAudio.length);
+        result = result + ", TriggerAudio: "
+                + (triggerAudio == null ? "null" : triggerAudio.length);
         byte[] data = event.getData();
         result = result + ", Data: " + (data == null ? "null" : data.length);
         if (data != null) {
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
index c900eae..e36f398 100644
--- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
+++ b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
@@ -16,7 +16,6 @@
 
 package android.hardware.soundtrigger;
 
-import android.hardware.soundtrigger.SoundTrigger;
 import android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
 import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
@@ -51,10 +50,10 @@
         Keyphrase unparceled = Keyphrase.CREATOR.createFromParcel(parcel);
 
         // Verify that they are the same
-        assertEquals(keyphrase.id, unparceled.id);
-        assertNull(unparceled.users);
-        assertEquals(keyphrase.locale, unparceled.locale);
-        assertEquals(keyphrase.text, unparceled.text);
+        assertEquals(keyphrase.getId(), unparceled.getId());
+        assertNull(unparceled.getUsers());
+        assertEquals(keyphrase.getLocale(), unparceled.getLocale());
+        assertEquals(keyphrase.getText(), unparceled.getText());
     }
 
     @SmallTest
@@ -71,10 +70,10 @@
         Keyphrase unparceled = Keyphrase.CREATOR.createFromParcel(parcel);
 
         // Verify that they are the same
-        assertEquals(keyphrase.id, unparceled.id);
-        assertTrue(Arrays.equals(keyphrase.users, unparceled.users));
-        assertEquals(keyphrase.locale, unparceled.locale);
-        assertEquals(keyphrase.text, unparceled.text);
+        assertEquals(keyphrase.getId(), unparceled.getId());
+        assertTrue(Arrays.equals(keyphrase.getUsers(), unparceled.getUsers()));
+        assertEquals(keyphrase.getLocale(), unparceled.getLocale());
+        assertEquals(keyphrase.getText(), unparceled.getText());
     }
 
     @SmallTest
@@ -91,10 +90,10 @@
         Keyphrase unparceled = Keyphrase.CREATOR.createFromParcel(parcel);
 
         // Verify that they are the same
-        assertEquals(keyphrase.id, unparceled.id);
-        assertTrue(Arrays.equals(keyphrase.users, unparceled.users));
-        assertEquals(keyphrase.locale, unparceled.locale);
-        assertEquals(keyphrase.text, unparceled.text);
+        assertEquals(keyphrase.getId(), unparceled.getId());
+        assertTrue(Arrays.equals(keyphrase.getUsers(), unparceled.getUsers()));
+        assertEquals(keyphrase.getLocale(), unparceled.getLocale());
+        assertEquals(keyphrase.getText(), unparceled.getText());
     }
 
     @SmallTest
@@ -116,10 +115,10 @@
         KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
 
         // Verify that they are the same
-        assertEquals(ksm.uuid, unparceled.uuid);
-        assertNull(unparceled.data);
-        assertEquals(ksm.type, unparceled.type);
-        assertTrue(Arrays.equals(keyphrases, unparceled.keyphrases));
+        assertEquals(ksm.getUuid(), unparceled.getUuid());
+        assertNull(unparceled.getData());
+        assertEquals(ksm.getType(), unparceled.getType());
+        assertTrue(Arrays.equals(keyphrases, unparceled.getKeyphrases()));
     }
 
     @SmallTest
@@ -141,10 +140,10 @@
         KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
 
         // Verify that they are the same
-        assertEquals(ksm.uuid, unparceled.uuid);
-        assertEquals(ksm.type, unparceled.type);
-        assertTrue(Arrays.equals(ksm.keyphrases, unparceled.keyphrases));
-        assertTrue(Arrays.equals(ksm.data, unparceled.data));
+        assertEquals(ksm.getUuid(), unparceled.getUuid());
+        assertEquals(ksm.getType(), unparceled.getType());
+        assertTrue(Arrays.equals(ksm.getKeyphrases(), unparceled.getKeyphrases()));
+        assertTrue(Arrays.equals(ksm.getData(), unparceled.getData()));
     }
 
     @SmallTest
@@ -163,10 +162,10 @@
         KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
 
         // Verify that they are the same
-        assertEquals(ksm.uuid, unparceled.uuid);
-        assertEquals(ksm.type, unparceled.type);
-        assertNull(unparceled.keyphrases);
-        assertTrue(Arrays.equals(ksm.data, unparceled.data));
+        assertEquals(ksm.getUuid(), unparceled.getUuid());
+        assertEquals(ksm.getType(), unparceled.getType());
+        assertNull(unparceled.getKeyphrases());
+        assertTrue(Arrays.equals(ksm.getData(), unparceled.getData()));
     }
 
     @SmallTest
@@ -185,10 +184,10 @@
         KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
 
         // Verify that they are the same
-        assertEquals(ksm.uuid, unparceled.uuid);
-        assertEquals(ksm.type, unparceled.type);
-        assertTrue(Arrays.equals(ksm.keyphrases, unparceled.keyphrases));
-        assertTrue(Arrays.equals(ksm.data, unparceled.data));
+        assertEquals(ksm.getUuid(), unparceled.getUuid());
+        assertEquals(ksm.getType(), unparceled.getType());
+        assertTrue(Arrays.equals(ksm.getKeyphrases(), unparceled.getKeyphrases()));
+        assertTrue(Arrays.equals(ksm.getData(), unparceled.getData()));
     }
 
     @LargeTest
@@ -212,10 +211,10 @@
         KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
 
         // Verify that they are the same
-        assertEquals(ksm.uuid, unparceled.uuid);
-        assertEquals(ksm.type, unparceled.type);
-        assertTrue(Arrays.equals(ksm.data, unparceled.data));
-        assertTrue(Arrays.equals(ksm.keyphrases, unparceled.keyphrases));
+        assertEquals(ksm.getUuid(), unparceled.getUuid());
+        assertEquals(ksm.getType(), unparceled.getType());
+        assertTrue(Arrays.equals(ksm.getData(), unparceled.getData()));
+        assertTrue(Arrays.equals(ksm.getKeyphrases(), unparceled.getKeyphrases()));
     }
 
     @SmallTest
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
index c0583ce..2c3592c 100644
--- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
+++ b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
@@ -17,7 +17,6 @@
 package android.hardware.soundtrigger;
 
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.timeout;
@@ -37,6 +36,8 @@
 
 import com.android.internal.app.ISoundTriggerService;
 
+import org.mockito.MockitoAnnotations;
+
 import java.io.DataOutputStream;
 import java.net.InetAddress;
 import java.net.Socket;
@@ -45,8 +46,6 @@
 import java.util.Random;
 import java.util.UUID;
 
-import org.mockito.MockitoAnnotations;
-
 public class GenericSoundModelTest extends AndroidTestCase {
     static final int MSG_DETECTION_ERROR = -1;
     static final int MSG_DETECTION_RESUME = 0;
@@ -96,11 +95,11 @@
 
         // Update sound model
         soundTriggerService.updateSoundModel(model);
-        loadedModelUuids.add(model.uuid);
+        loadedModelUuids.add(model.getUuid());
 
         // Confirm it was updated
         GenericSoundModel returnedModel =
-                soundTriggerService.getSoundModel(new ParcelUuid(model.uuid));
+                soundTriggerService.getSoundModel(new ParcelUuid(model.getUuid()));
         assertEquals(model, returnedModel);
     }
 
@@ -110,15 +109,15 @@
 
         // Update sound model
         soundTriggerService.updateSoundModel(model);
-        loadedModelUuids.add(model.uuid);
+        loadedModelUuids.add(model.getUuid());
 
         // Delete sound model
-        soundTriggerService.deleteSoundModel(new ParcelUuid(model.uuid));
-        loadedModelUuids.remove(model.uuid);
+        soundTriggerService.deleteSoundModel(new ParcelUuid(model.getUuid()));
+        loadedModelUuids.remove(model.getUuid());
 
         // Confirm it was deleted
         GenericSoundModel returnedModel =
-                soundTriggerService.getSoundModel(new ParcelUuid(model.uuid));
+                soundTriggerService.getSoundModel(new ParcelUuid(model.getUuid()));
         assertEquals(null, returnedModel);
     }
 
@@ -134,14 +133,14 @@
 
         // Update and start sound model recognition
         soundTriggerService.updateSoundModel(model);
-        loadedModelUuids.add(model.uuid);
-        int r = soundTriggerService.startRecognition(new ParcelUuid(model.uuid), spyCallback,
+        loadedModelUuids.add(model.getUuid());
+        int r = soundTriggerService.startRecognition(new ParcelUuid(model.getUuid()), spyCallback,
                 config);
         assertEquals("Could Not Start Recognition with code: " + r,
                 android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
 
         // Stop recognition
-        r = soundTriggerService.stopRecognition(new ParcelUuid(model.uuid), spyCallback);
+        r = soundTriggerService.stopRecognition(new ParcelUuid(model.getUuid()), spyCallback);
         assertEquals("Could Not Stop Recognition with code: " + r,
                 android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
     }
@@ -158,13 +157,13 @@
 
         // Update and start sound model
         soundTriggerService.updateSoundModel(model);
-        loadedModelUuids.add(model.uuid);
-        soundTriggerService.startRecognition(new ParcelUuid(model.uuid), spyCallback, config);
+        loadedModelUuids.add(model.getUuid());
+        soundTriggerService.startRecognition(new ParcelUuid(model.getUuid()), spyCallback, config);
 
         // Send trigger to stub HAL
         Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
         DataOutputStream out = new DataOutputStream(socket.getOutputStream());
-        out.writeBytes("trig " + model.uuid.toString() + "\r\n");
+        out.writeBytes("trig " + model.getUuid().toString() + "\r\n");
         out.flush();
         socket.close();
 
@@ -227,11 +226,12 @@
             if (operation == 0 && modelInfo.status == STATUS_UNLOADED) {
                 // Update and start sound model
                 soundTriggerService.updateSoundModel(modelInfo.model);
-                loadedModelUuids.add(modelInfo.model.uuid);
+                loadedModelUuids.add(modelInfo.model.getUuid());
                 modelInfo.status = STATUS_LOADED;
             } else if (operation == 1 && modelInfo.status == STATUS_LOADED) {
                 // Start the sound model
-                int r = soundTriggerService.startRecognition(new ParcelUuid(modelInfo.model.uuid),
+                int r = soundTriggerService.startRecognition(new ParcelUuid(
+                                modelInfo.model.getUuid()),
                         spyCallback, config);
                 assertEquals("Could Not Start Recognition with code: " + r,
                         android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
@@ -240,7 +240,7 @@
                 // Send trigger to stub HAL
                 Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
                 DataOutputStream out = new DataOutputStream(socket.getOutputStream());
-                out.writeBytes("trig " + modelInfo.model.uuid + "\r\n");
+                out.writeBytes("trig " + modelInfo.model.getUuid() + "\r\n");
                 out.flush();
                 socket.close();
 
@@ -249,19 +249,20 @@
                 reset(spyCallback);
             } else if (operation == 3 && modelInfo.status == STATUS_STARTED) {
                 // Stop recognition
-                int r = soundTriggerService.stopRecognition(new ParcelUuid(modelInfo.model.uuid),
+                int r = soundTriggerService.stopRecognition(new ParcelUuid(
+                                modelInfo.model.getUuid()),
                         spyCallback);
                 assertEquals("Could Not Stop Recognition with code: " + r,
                         android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
                 modelInfo.status = STATUS_LOADED;
             } else if (operation == 4 && modelInfo.status != STATUS_UNLOADED) {
                 // Delete sound model
-                soundTriggerService.deleteSoundModel(new ParcelUuid(modelInfo.model.uuid));
-                loadedModelUuids.remove(modelInfo.model.uuid);
+                soundTriggerService.deleteSoundModel(new ParcelUuid(modelInfo.model.getUuid()));
+                loadedModelUuids.remove(modelInfo.model.getUuid());
 
                 // Confirm it was deleted
-                GenericSoundModel returnedModel =
-                        soundTriggerService.getSoundModel(new ParcelUuid(modelInfo.model.uuid));
+                GenericSoundModel returnedModel = soundTriggerService.getSoundModel(
+                        new ParcelUuid(modelInfo.model.getUuid()));
                 assertEquals(null, returnedModel);
                 modelInfo.status = STATUS_UNLOADED;
             }
diff --git a/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java
index 7927ac4..287364f 100644
--- a/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java
+++ b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java
@@ -159,36 +159,36 @@
             Log.e(TAG, "KeyphraseSoundModel must be non-null");
             return false;
         }
-        if (soundModel.uuid == null) {
+        if (soundModel.getUuid() == null) {
             Log.e(TAG, "KeyphraseSoundModel must have a UUID");
             return false;
         }
-        if (soundModel.data == null) {
+        if (soundModel.getData() == null) {
             Log.e(TAG, "KeyphraseSoundModel must have data");
             return false;
         }
-        if (soundModel.keyphrases == null || soundModel.keyphrases.length != 1) {
+        if (soundModel.getKeyphrases() == null || soundModel.getKeyphrases().length != 1) {
             Log.e(TAG, "Keyphrase must be exactly 1");
             return false;
         }
-        Keyphrase keyphrase = soundModel.keyphrases[0];
-        if (keyphrase.id <= 0) {
+        Keyphrase keyphrase = soundModel.getKeyphrases()[0];
+        if (keyphrase.getId() <= 0) {
             Log.e(TAG, "Keyphrase must have a valid ID");
             return false;
         }
-        if (keyphrase.recognitionModes < 0) {
+        if (keyphrase.getRecognitionModes() < 0) {
             Log.e(TAG, "Recognition modes must be valid");
             return false;
         }
-        if (keyphrase.locale == null) {
+        if (keyphrase.getLocale() == null) {
             Log.e(TAG, "Locale must not be null");
             return false;
         }
-        if (keyphrase.text == null) {
+        if (keyphrase.getText() == null) {
             Log.e(TAG, "Text must not be null");
             return false;
         }
-        if (keyphrase.users == null || keyphrase.users.length == 0) {
+        if (keyphrase.getUsers() == null || keyphrase.getUsers().length == 0) {
             Log.e(TAG, "Keyphrase must have valid user(s)");
             return false;
         }
diff --git a/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
index b357ad0..e4880fd 100644
--- a/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
+++ b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java
@@ -91,7 +91,7 @@
         }
         boolean status = mEnrollmentUtil.deleteSoundModel(KEYPHRASE_ID, BCP47_LOCALE);
         if (status) {
-            Toast.makeText(this, "Successfully un-enrolled, model UUID=" + soundModel.uuid,
+            Toast.makeText(this, "Successfully un-enrolled, model UUID=" + soundModel.getUuid(),
                     Toast.LENGTH_SHORT)
                     .show();
         } else {
@@ -112,11 +112,11 @@
         // Generate a fake model to push.
         byte[] data = new byte[2048];
         mRandom.nextBytes(data);
-        KeyphraseSoundModel updated = new KeyphraseSoundModel(soundModel.uuid,
-                soundModel.vendorUuid, data, soundModel.keyphrases);
+        KeyphraseSoundModel updated = new KeyphraseSoundModel(soundModel.getUuid(),
+                soundModel.getVendorUuid(), data, soundModel.getKeyphrases());
         boolean status = mEnrollmentUtil.addOrUpdateSoundModel(updated);
         if (status) {
-            Toast.makeText(this, "Successfully re-enrolled, model UUID=" + updated.uuid,
+            Toast.makeText(this, "Successfully re-enrolled, model UUID=" + updated.getUuid(),
                     Toast.LENGTH_SHORT)
                     .show();
         } else {
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
index 548af0c..498cb7c 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
@@ -18,7 +18,6 @@
 
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
-
 import static java.lang.Math.max;
 import static java.lang.Math.min;
 
@@ -31,6 +30,7 @@
 import android.graphics.Insets;
 import android.os.Bundle;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -44,11 +44,11 @@
 import android.view.animation.LinearInterpolator;
 import android.widget.LinearLayout;
 
-import androidx.appcompat.app.AppCompatActivity;
-
 import java.util.ArrayList;
 import java.util.List;
 
+import androidx.appcompat.app.AppCompatActivity;
+
 public class WindowInsetsActivity extends AppCompatActivity {
 
     private View mRoot;
@@ -191,6 +191,40 @@
                 mTransitions.forEach(it -> it.onFinish(animation));
             }
         });
+
+        findViewById(R.id.floating_action_button).setOnClickListener(
+                v -> v.getWindowInsetsController().controlWindowInsetsAnimation(ime(), -1,
+                new LinearInterpolator(), null /* cancellationSignal */,
+                new WindowInsetsAnimationControlListener() {
+                    @Override
+                    public void onReady(
+                            WindowInsetsAnimationController controller,
+                            int types) {
+                        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+                        anim.setDuration(1500);
+                        anim.addUpdateListener(animation
+                                -> controller.setInsetsAndAlpha(
+                                controller.getShownStateInsets(),
+                                (float) animation.getAnimatedValue(),
+                                anim.getAnimatedFraction()));
+                        anim.addListener(new AnimatorListenerAdapter() {
+                            @Override
+                            public void onAnimationEnd(Animator animation) {
+                                super.onAnimationEnd(animation);
+                                controller.finish(true);
+                            }
+                        });
+                        anim.start();
+                    }
+
+                    @Override
+                    public void onCancelled(WindowInsetsAnimationController controller) {
+                    }
+
+                    @Override
+                    public void onFinished(WindowInsetsAnimationController controller) {
+                    }
+                }));
     }
 
     @Override
@@ -200,57 +234,6 @@
         getWindow().getDecorView().post(() -> getWindow().setDecorFitsSystemWindows(false));
     }
 
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        getWindow().getInsetsController().addOnControllableInsetsChangedListener(
-                new OnControllableInsetsChangedListener() {
-
-                    boolean hasControl = false;
-                    @Override
-                    public void onControllableInsetsChanged(WindowInsetsController controller,
-                            int types) {
-                        if ((types & ime()) != 0 && !hasControl) {
-                            hasControl = true;
-                            controller.controlWindowInsetsAnimation(ime(), -1,
-                                    new LinearInterpolator(), null /* cancellationSignal */,
-                                    new WindowInsetsAnimationControlListener() {
-                                        @Override
-                                        public void onReady(
-                                                WindowInsetsAnimationController controller,
-                                                int types) {
-                                            ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
-                                            anim.setDuration(1500);
-                                            anim.addUpdateListener(animation
-                                                    -> controller.setInsetsAndAlpha(
-                                                    controller.getShownStateInsets(),
-                                                    (float) animation.getAnimatedValue(),
-                                                    anim.getAnimatedFraction()));
-                                            anim.addListener(new AnimatorListenerAdapter() {
-                                                @Override
-                                                public void onAnimationEnd(Animator animation) {
-                                                    super.onAnimationEnd(animation);
-                                                    controller.finish(true);
-                                                }
-                                            });
-                                            anim.start();
-                                        }
-
-                                        @Override
-                                        public void onFinished(
-                                                WindowInsetsAnimationController controller) {
-                                        }
-
-                                        @Override
-                                        public void onCancelled(
-                                                WindowInsetsAnimationController controller) {
-                                        }
-                                    });
-                        }
-                    }
-                });
-    }
-
     static class Transition {
         private int mEndBottom;
         private int mStartBottom;
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index b2e8c37..916c339 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -463,7 +463,9 @@
 
         nc1.setSSID(TEST_SSID);
         nc2.combineCapabilities(nc1);
-        assertTrue(TEST_SSID.equals(nc2.getSsid()));
+        if (isAtLeastR()) {
+            assertTrue(TEST_SSID.equals(nc2.getSsid()));
+        }
 
         // Because they now have the same SSID, the following call should not throw
         nc2.combineCapabilities(nc1);
@@ -601,12 +603,16 @@
         // from nc2.
         assertFalse(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING));
-        assertTrue(TEST_SSID.equals(nc2.getSsid()));
+        if (isAtLeastR()) {
+            assertTrue(TEST_SSID.equals(nc2.getSsid()));
+        }
 
         nc1.setSSID(DIFFERENT_TEST_SSID);
         nc2.set(nc1);
         assertEquals(nc1, nc2);
-        assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid()));
+        if (isAtLeastR()) {
+            assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSsid()));
+        }
 
         nc1.setUids(uidRange(10, 13));
         nc2.set(nc1);  // Overwrites, as opposed to combineCapabilities
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 4bfb51b..83399b8 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -307,6 +307,8 @@
 
     private static final long TIMESTAMP = 1234L;
 
+    private static final int NET_ID = 110;
+
     private static final String CLAT_PREFIX = "v4-";
     private static final String MOBILE_IFNAME = "test_rmnet_data0";
     private static final String WIFI_IFNAME = "test_wlan0";
@@ -1015,6 +1017,7 @@
         private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
 
         private VpnInfo mVpnInfo;
+        private Network[] mUnderlyingNetworks;
 
         public MockVpn(int userId) {
             super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
@@ -1104,9 +1107,21 @@
             return super.getVpnInfo();
         }
 
-        private void setVpnInfo(VpnInfo vpnInfo) {
+        private synchronized void setVpnInfo(VpnInfo vpnInfo) {
             mVpnInfo = vpnInfo;
         }
+
+        @Override
+        public synchronized Network[] getUnderlyingNetworks() {
+            if (mUnderlyingNetworks != null) return mUnderlyingNetworks;
+
+            return super.getUnderlyingNetworks();
+        }
+
+        /** Don't override behavior for {@link Vpn#setUnderlyingNetworks}. */
+        private synchronized void overrideUnderlyingNetworks(Network[] underlyingNetworks) {
+            mUnderlyingNetworks = underlyingNetworks;
+        }
     }
 
     private void mockVpn(int uid) {
@@ -6795,15 +6810,11 @@
 
         mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
 
-        try {
-            assertFalse(
-                    "Mismatched uid/package name should not pass the location permission check",
-                    mService.checkConnectivityDiagnosticsPermissions(
-                            Process.myPid() + 1, Process.myUid() + 1, naiWithoutUid,
-                            mContext.getOpPackageName()));
-        } catch (SecurityException e) {
-            fail("checkConnectivityDiagnosticsPermissions shouldn't surface a SecurityException");
-        }
+        assertFalse(
+                "Mismatched uid/package name should not pass the location permission check",
+                mService.checkConnectivityDiagnosticsPermissions(
+                        Process.myPid() + 1, Process.myUid() + 1, naiWithoutUid,
+                        mContext.getOpPackageName()));
     }
 
     @Test
@@ -6824,9 +6835,10 @@
 
     @Test
     public void testCheckConnectivityDiagnosticsPermissionsActiveVpn() throws Exception {
+        final Network network = new Network(NET_ID);
         final NetworkAgentInfo naiWithoutUid =
                 new NetworkAgentInfo(
-                        null, null, null, null, null, new NetworkCapabilities(), 0,
+                        null, null, network, null, null, new NetworkCapabilities(), 0,
                         mServiceContext, null, null, mService, null, null, null, 0);
 
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
@@ -6839,11 +6851,19 @@
         info.ownerUid = Process.myUid();
         info.vpnIface = "interface";
         mMockVpn.setVpnInfo(info);
+        mMockVpn.overrideUnderlyingNetworks(new Network[] {network});
         assertTrue(
                 "Active VPN permission not applied",
                 mService.checkConnectivityDiagnosticsPermissions(
                         Process.myPid(), Process.myUid(), naiWithoutUid,
                         mContext.getOpPackageName()));
+
+        mMockVpn.overrideUnderlyingNetworks(null);
+        assertFalse(
+                "VPN shouldn't receive callback on non-underlying network",
+                mService.checkConnectivityDiagnosticsPermissions(
+                        Process.myPid(), Process.myUid(), naiWithoutUid,
+                        mContext.getOpPackageName()));
     }
 
     @Test
diff --git a/tools/stats_log_api_gen/.clang-format b/tools/stats_log_api_gen/.clang-format
new file mode 100644
index 0000000..cead3a0
--- /dev/null
+++ b/tools/stats_log_api_gen/.clang-format
@@ -0,0 +1,17 @@
+BasedOnStyle: Google
+AllowShortIfStatementsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: false
+AllowShortLoopsOnASingleLine: true
+BinPackArguments: true
+BinPackParameters: true
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ContinuationIndentWidth: 8
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+AccessModifierOffset: -4
+IncludeCategories:
+  - Regex:    '^"Log\.h"'
+    Priority:    -1
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index 47eb63e..bf39093 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -15,11 +15,13 @@
  */
 
 #include "Collation.h"
-#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
 
 #include <stdio.h>
+
 #include <map>
 
+#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
+
 namespace android {
 namespace stats_log_api_gen {
 
@@ -32,55 +34,47 @@
 
 const bool dbg = false;
 
-
 //
 // AtomDecl class
 //
 
-AtomDecl::AtomDecl()
-    :code(0),
-     name()
-{
+AtomDecl::AtomDecl() : code(0), name() {
 }
 
-AtomDecl::AtomDecl(const AtomDecl &that)
-      : code(that.code),
-        name(that.name),
-        message(that.message),
-        fields(that.fields),
-        fieldNumberToAnnotations(that.fieldNumberToAnnotations),
-        primaryFields(that.primaryFields),
-        exclusiveField(that.exclusiveField),
-        defaultState(that.defaultState),
-        resetState(that.resetState),
-        nested(that.nested),
-        uidField(that.uidField),
-        whitelisted(that.whitelisted) {}
-
-AtomDecl::AtomDecl(int c, const string& n, const string& m)
-    :code(c),
-     name(n),
-     message(m)
-{
+AtomDecl::AtomDecl(const AtomDecl& that)
+    : code(that.code),
+      name(that.name),
+      message(that.message),
+      fields(that.fields),
+      fieldNumberToAnnotations(that.fieldNumberToAnnotations),
+      primaryFields(that.primaryFields),
+      exclusiveField(that.exclusiveField),
+      defaultState(that.defaultState),
+      resetState(that.resetState),
+      nested(that.nested),
+      uidField(that.uidField),
+      whitelisted(that.whitelisted),
+      truncateTimestamp(that.truncateTimestamp) {
 }
 
-AtomDecl::~AtomDecl()
-{
+AtomDecl::AtomDecl(int c, const string& n, const string& m) : code(c), name(n), message(m) {
 }
 
+AtomDecl::~AtomDecl() {
+}
 
 /**
- * Print an error message for a FieldDescriptor, including the file name and line number.
+ * Print an error message for a FieldDescriptor, including the file name and
+ * line number.
  */
-static void
-print_error(const FieldDescriptor* field, const char* format, ...)
-{
+static void print_error(const FieldDescriptor* field, const char* format, ...) {
     const Descriptor* message = field->containing_type();
     const FileDescriptor* file = message->file();
 
     SourceLocation loc;
     if (field->GetSourceLocation(&loc)) {
-        // TODO: this will work if we can figure out how to pass --include_source_info to protoc
+        // TODO: this will work if we can figure out how to pass
+        // --include_source_info to protoc
         fprintf(stderr, "%s:%d: ", file->name().c_str(), loc.start_line);
     } else {
         fprintf(stderr, "%s: ", file->name().c_str());
@@ -88,15 +82,13 @@
     va_list args;
     va_start(args, format);
     vfprintf(stderr, format, args);
-    va_end (args);
+    va_end(args);
 }
 
 /**
  * Convert a protobuf type into a java type.
  */
-static java_type_t
-java_type(const FieldDescriptor* field)
-{
+static java_type_t java_type(const FieldDescriptor* field) {
     int protoType = field->type();
     switch (protoType) {
         case FieldDescriptor::TYPE_DOUBLE:
@@ -121,12 +113,10 @@
             return JAVA_TYPE_UNKNOWN;
         case FieldDescriptor::TYPE_MESSAGE:
             // TODO: not the final package name
-            if (field->message_type()->full_name() ==
-                "android.os.statsd.AttributionNode") {
-              return JAVA_TYPE_ATTRIBUTION_CHAIN;
-            } else if (field->message_type()->full_name() ==
-                       "android.os.statsd.KeyValuePair") {
-              return JAVA_TYPE_KEY_VALUE_PAIR;
+            if (field->message_type()->full_name() == "android.os.statsd.AttributionNode") {
+                return JAVA_TYPE_ATTRIBUTION_CHAIN;
+            } else if (field->message_type()->full_name() == "android.os.statsd.KeyValuePair") {
+                return JAVA_TYPE_KEY_VALUE_PAIR;
             } else if (field->options().GetExtension(os::statsd::log_mode) ==
                        os::statsd::LogMode::MODE_BYTES) {
                 return JAVA_TYPE_BYTE_ARRAY;
@@ -155,307 +145,298 @@
 /**
  * Gather the enums info.
  */
-void collate_enums(const EnumDescriptor &enumDescriptor, AtomField *atomField) {
+void collate_enums(const EnumDescriptor& enumDescriptor, AtomField* atomField) {
     for (int i = 0; i < enumDescriptor.value_count(); i++) {
         atomField->enumValues[enumDescriptor.value(i)->number()] =
-            enumDescriptor.value(i)->name().c_str();
+                enumDescriptor.value(i)->name().c_str();
     }
 }
 
 static void addAnnotationToAtomDecl(AtomDecl* atomDecl, const int fieldNumber,
-        const int annotationId, const AnnotationType annotationType,
-        const AnnotationValue annotationValue) {
+                                    const int annotationId, const AnnotationType annotationType,
+                                    const AnnotationValue annotationValue) {
     if (dbg) {
-        printf("   Adding annotation to %s: [%d] = {id: %d, type: %d}\n",
-                atomDecl->name.c_str(), fieldNumber, annotationId, annotationType);
+        printf("   Adding annotation to %s: [%d] = {id: %d, type: %d}\n", atomDecl->name.c_str(),
+               fieldNumber, annotationId, annotationType);
     }
-    atomDecl->fieldNumberToAnnotations[fieldNumber].insert(make_shared<Annotation>(
-                annotationId, atomDecl->code, annotationType, annotationValue));
+    atomDecl->fieldNumberToAnnotations[fieldNumber].insert(
+            make_shared<Annotation>(annotationId, atomDecl->code, annotationType, annotationValue));
 }
 
-/**
- * Gather the info about an atom proto.
- */
-int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
-                 vector<java_type_t> *signature) {
-
-  int errorCount = 0;
-
-  // Build a sorted list of the fields. Descriptor has them in source file
-  // order.
-  map<int, const FieldDescriptor *> fields;
-  for (int j = 0; j < atom->field_count(); j++) {
-    const FieldDescriptor *field = atom->field(j);
-    fields[field->number()] = field;
-  }
-
-  // Check that the parameters start at 1 and go up sequentially.
-  int expectedNumber = 1;
-  for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
-       it != fields.end(); it++) {
-    const int number = it->first;
-    const FieldDescriptor *field = it->second;
-    if (number != expectedNumber) {
-      print_error(field,
-                  "Fields must be numbered consecutively starting at 1:"
-                  " '%s' is %d but should be %d\n",
-                  field->name().c_str(), number, expectedNumber);
-      errorCount++;
-      expectedNumber = number;
-      continue;
-    }
-    expectedNumber++;
-  }
-
-  // Check that only allowed types are present. Remove any invalid ones.
-  for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
-       it != fields.end(); it++) {
-    const FieldDescriptor *field = it->second;
-    bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
-                         os::statsd::LogMode::MODE_BYTES;
-
-    java_type_t javaType = java_type(field);
-
-    if (javaType == JAVA_TYPE_UNKNOWN) {
-      print_error(field, "Unkown type for field: %s\n", field->name().c_str());
-      errorCount++;
-      continue;
-    } else if (javaType == JAVA_TYPE_OBJECT &&
-               atomDecl->code < PULL_ATOM_START_ID) {
-        // Allow attribution chain, but only at position 1.
-        print_error(field,
-                    "Message type not allowed for field in pushed atoms: %s\n",
-                    field->name().c_str());
-        errorCount++;
-        continue;
-    } else if (javaType == JAVA_TYPE_BYTE_ARRAY && !isBinaryField) {
-        print_error(field, "Raw bytes type not allowed for field: %s\n",
-                    field->name().c_str());
-        errorCount++;
-        continue;
-    }
-
-    if (isBinaryField && javaType != JAVA_TYPE_BYTE_ARRAY) {
-        print_error(field, "Cannot mark field %s as bytes.\n",
-                    field->name().c_str());
-        errorCount++;
-        continue;
-    }
-
-    // Doubles are not supported yet.
-    if (javaType == JAVA_TYPE_DOUBLE) {
-        print_error(field, "Doubles are not supported in atoms. Please change field %s to float\n",
-                    field->name().c_str());
-        errorCount++;
-        continue;
-    }
-
-    if (field->is_repeated() &&
-        !(javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || javaType == JAVA_TYPE_KEY_VALUE_PAIR)) {
-        print_error(field,
-                    "Repeated fields are not supported in atoms. Please make field %s not "
-                    "repeated.\n",
-                    field->name().c_str());
-        errorCount++;
-        continue;
-    }
-  }
-
-  // Check that if there's an attribution chain, it's at position 1.
-  for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
-       it != fields.end(); it++) {
-    int number = it->first;
-    if (number != 1) {
-      const FieldDescriptor *field = it->second;
-      java_type_t javaType = java_type(field);
-      if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-        print_error(
-            field,
-            "AttributionChain fields must have field id 1, in message: '%s'\n",
-            atom->name().c_str());
-        errorCount++;
-      }
-    }
-  }
-
-  // Build the type signature and the atom data.
-  for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
-       it != fields.end(); it++) {
-    const FieldDescriptor *field = it->second;
-    java_type_t javaType = java_type(field);
-    bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
-                         os::statsd::LogMode::MODE_BYTES;
-
-    AtomField atField(field->name(), javaType);
-    // Generate signature for pushed atoms
-    if (atomDecl->code < PULL_ATOM_START_ID) {
-      if (javaType == JAVA_TYPE_ENUM) {
-        // All enums are treated as ints when it comes to function signatures.
-        signature->push_back(JAVA_TYPE_INT);
-      } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
-          signature->push_back(JAVA_TYPE_BYTE_ARRAY);
-      } else {
-          signature->push_back(javaType);
-      }
-    }
-    if (javaType == JAVA_TYPE_ENUM) {
-      // All enums are treated as ints when it comes to function signatures.
-      collate_enums(*field->enum_type(), &atField);
-    }
-    atomDecl->fields.push_back(atField);
+static int collate_field_annotations(AtomDecl* atomDecl, const FieldDescriptor* field,
+                                     const int fieldNumber, const java_type_t& javaType) {
+    int errorCount = 0;
 
     if (field->options().HasExtension(os::statsd::state_field_option)) {
         const int option = field->options().GetExtension(os::statsd::state_field_option).option();
         if (option != STATE_OPTION_UNSET) {
-            addAnnotationToAtomDecl(atomDecl, signature->size(), ANNOTATION_ID_STATE_OPTION,
-                    ANNOTATION_TYPE_INT, AnnotationValue(option));
+            addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_STATE_OPTION,
+                                    ANNOTATION_TYPE_INT, AnnotationValue(option));
         }
 
         if (option == STATE_OPTION_PRIMARY) {
-            if (javaType == JAVA_TYPE_UNKNOWN ||
-                    javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
-                    javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
-                print_error(
-                    field,
-                    "Invalid primary state field: '%s'\n",
-                    atom->name().c_str());
+            if (javaType == JAVA_TYPE_UNKNOWN || javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
+                javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
+                print_error(field, "Invalid primary state field: '%s'\n",
+                            atomDecl->message.c_str());
                 errorCount++;
-                continue;
             }
-            atomDecl->primaryFields.push_back(it->first);
-
+            atomDecl->primaryFields.push_back(fieldNumber);
         }
 
         if (option == STATE_OPTION_PRIMARY_FIELD_FIRST_UID) {
             if (javaType != JAVA_TYPE_ATTRIBUTION_CHAIN) {
-                print_error(
-                    field,
-                    "PRIMARY_FIELD_FIRST_UID annotation is only for AttributionChains: '%s'\n",
-                    atom->name().c_str());
+                print_error(field,
+                            "PRIMARY_FIELD_FIRST_UID annotation is only for AttributionChains: "
+                            "'%s'\n",
+                            atomDecl->message.c_str());
                 errorCount++;
-                continue;
             } else {
                 atomDecl->primaryFields.push_back(FIRST_UID_IN_CHAIN_ID);
             }
         }
 
         if (option == STATE_OPTION_EXCLUSIVE) {
-            if (javaType == JAVA_TYPE_UNKNOWN ||
-                    javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
-                    javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
-                print_error(
-                    field,
-                    "Invalid exclusive state field: '%s'\n",
-                    atom->name().c_str());
+            if (javaType == JAVA_TYPE_UNKNOWN || javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
+                javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
+                print_error(field, "Invalid exclusive state field: '%s'\n",
+                            atomDecl->message.c_str());
                 errorCount++;
-                continue;
             }
 
             if (atomDecl->exclusiveField == 0) {
-                atomDecl->exclusiveField = it->first;
+                atomDecl->exclusiveField = fieldNumber;
             } else {
-                print_error(
-                    field,
-                    "Cannot have more than one exclusive state field in an atom: '%s'\n",
-                    atom->name().c_str());
+                print_error(field,
+                            "Cannot have more than one exclusive state field in an "
+                            "atom: '%s'\n",
+                            atomDecl->message.c_str());
                 errorCount++;
-                continue;
             }
 
             if (field->options()
                         .GetExtension(os::statsd::state_field_option)
                         .has_default_state_value()) {
-                const int defaultState =
-                        field->options().GetExtension(os::statsd::state_field_option)
-                        .default_state_value();
+                const int defaultState = field->options()
+                                                 .GetExtension(os::statsd::state_field_option)
+                                                 .default_state_value();
                 atomDecl->defaultState = defaultState;
 
-                addAnnotationToAtomDecl(atomDecl, signature->size(), ANNOTATION_ID_DEFAULT_STATE,
-                        ANNOTATION_TYPE_INT, AnnotationValue(defaultState));
+                addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_DEFAULT_STATE,
+                                        ANNOTATION_TYPE_INT, AnnotationValue(defaultState));
             }
 
-            if (field->options().GetExtension(os::statsd::state_field_option)
-                    .has_reset_state_value()) {
-                const int resetState = field->options()
+            if (field->options()
                         .GetExtension(os::statsd::state_field_option)
-                        .reset_state_value();
+                        .has_reset_state_value()) {
+                const int resetState = field->options()
+                                               .GetExtension(os::statsd::state_field_option)
+                                               .reset_state_value();
 
                 atomDecl->resetState = resetState;
-                addAnnotationToAtomDecl(atomDecl, signature->size(), ANNOTATION_ID_RESET_STATE,
-                        ANNOTATION_TYPE_INT, AnnotationValue(resetState));
+                addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_RESET_STATE,
+                                        ANNOTATION_TYPE_INT, AnnotationValue(resetState));
             }
 
-            if (field->options().GetExtension(os::statsd::state_field_option)
-                    .has_nested()) {
+            if (field->options().GetExtension(os::statsd::state_field_option).has_nested()) {
                 const bool nested =
                         field->options().GetExtension(os::statsd::state_field_option).nested();
                 atomDecl->nested = nested;
 
-                addAnnotationToAtomDecl(atomDecl, signature->size(), ANNOTATION_ID_STATE_NESTED,
-                        ANNOTATION_TYPE_BOOL, AnnotationValue(nested));
+                addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_STATE_NESTED,
+                                        ANNOTATION_TYPE_BOOL, AnnotationValue(nested));
             }
         }
-
     }
+
     if (field->options().GetExtension(os::statsd::is_uid) == true) {
         if (javaType != JAVA_TYPE_INT) {
-            print_error(
-                field,
-                "is_uid annotation can only be applied to int32 fields: '%s'\n",
-                atom->name().c_str());
+            print_error(field, "is_uid annotation can only be applied to int32 fields: '%s'\n",
+                        atomDecl->message.c_str());
             errorCount++;
-            continue;
         }
 
         if (atomDecl->uidField == 0) {
-            atomDecl->uidField = it->first;
+            atomDecl->uidField = fieldNumber;
 
-            addAnnotationToAtomDecl(atomDecl, signature->size(), ANNOTATION_ID_IS_UID,
-                    ANNOTATION_TYPE_BOOL, AnnotationValue(true));
+            addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_IS_UID,
+                                    ANNOTATION_TYPE_BOOL, AnnotationValue(true));
         } else {
-            print_error(
-                field,
-                "Cannot have more than one field in an atom with is_uid annotation: '%s'\n",
-                atom->name().c_str());
+            print_error(field,
+                        "Cannot have more than one field in an atom with is_uid "
+                        "annotation: '%s'\n",
+                        atomDecl->message.c_str());
+            errorCount++;
+        }
+    }
+
+    return errorCount;
+}
+
+/**
+ * Gather the info about an atom proto.
+ */
+int collate_atom(const Descriptor* atom, AtomDecl* atomDecl, vector<java_type_t>* signature) {
+    int errorCount = 0;
+
+    // Build a sorted list of the fields. Descriptor has them in source file
+    // order.
+    map<int, const FieldDescriptor*> fields;
+    for (int j = 0; j < atom->field_count(); j++) {
+        const FieldDescriptor* field = atom->field(j);
+        fields[field->number()] = field;
+    }
+
+    // Check that the parameters start at 1 and go up sequentially.
+    int expectedNumber = 1;
+    for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
+         it++) {
+        const int number = it->first;
+        const FieldDescriptor* field = it->second;
+        if (number != expectedNumber) {
+            print_error(field,
+                        "Fields must be numbered consecutively starting at 1:"
+                        " '%s' is %d but should be %d\n",
+                        field->name().c_str(), number, expectedNumber);
+            errorCount++;
+            expectedNumber = number;
+            continue;
+        }
+        expectedNumber++;
+    }
+
+    // Check that only allowed types are present. Remove any invalid ones.
+    for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
+         it++) {
+        const FieldDescriptor* field = it->second;
+        bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
+                             os::statsd::LogMode::MODE_BYTES;
+
+        java_type_t javaType = java_type(field);
+
+        if (javaType == JAVA_TYPE_UNKNOWN) {
+            print_error(field, "Unknown type for field: %s\n", field->name().c_str());
+            errorCount++;
+            continue;
+        } else if (javaType == JAVA_TYPE_OBJECT && atomDecl->code < PULL_ATOM_START_ID) {
+            // Allow attribution chain, but only at position 1.
+            print_error(field, "Message type not allowed for field in pushed atoms: %s\n",
+                        field->name().c_str());
+            errorCount++;
+            continue;
+        } else if (javaType == JAVA_TYPE_BYTE_ARRAY && !isBinaryField) {
+            print_error(field, "Raw bytes type not allowed for field: %s\n", field->name().c_str());
+            errorCount++;
+            continue;
+        }
+
+        if (isBinaryField && javaType != JAVA_TYPE_BYTE_ARRAY) {
+            print_error(field, "Cannot mark field %s as bytes.\n", field->name().c_str());
+            errorCount++;
+            continue;
+        }
+
+        // Doubles are not supported yet.
+        if (javaType == JAVA_TYPE_DOUBLE) {
+            print_error(field,
+                        "Doubles are not supported in atoms. Please change field %s "
+                        "to float\n",
+                        field->name().c_str());
+            errorCount++;
+            continue;
+        }
+
+        if (field->is_repeated() &&
+            !(javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || javaType == JAVA_TYPE_KEY_VALUE_PAIR)) {
+            print_error(field,
+                        "Repeated fields are not supported in atoms. Please make "
+                        "field %s not "
+                        "repeated.\n",
+                        field->name().c_str());
             errorCount++;
             continue;
         }
     }
-  }
 
-  return errorCount;
+    // Check that if there's an attribution chain, it's at position 1.
+    for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
+         it++) {
+        int number = it->first;
+        if (number != 1) {
+            const FieldDescriptor* field = it->second;
+            java_type_t javaType = java_type(field);
+            if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                print_error(field,
+                            "AttributionChain fields must have field id 1, in message: '%s'\n",
+                            atom->name().c_str());
+                errorCount++;
+            }
+        }
+    }
+
+    // Build the type signature and the atom data.
+    for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
+         it++) {
+        const FieldDescriptor* field = it->second;
+        java_type_t javaType = java_type(field);
+        bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
+                             os::statsd::LogMode::MODE_BYTES;
+
+        AtomField atField(field->name(), javaType);
+
+        if (javaType == JAVA_TYPE_ENUM) {
+            // All enums are treated as ints when it comes to function signatures.
+            collate_enums(*field->enum_type(), &atField);
+        }
+
+        // Generate signature for pushed atoms
+        if (atomDecl->code < PULL_ATOM_START_ID) {
+            if (javaType == JAVA_TYPE_ENUM) {
+                // All enums are treated as ints when it comes to function signatures.
+                signature->push_back(JAVA_TYPE_INT);
+            } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
+                signature->push_back(JAVA_TYPE_BYTE_ARRAY);
+            } else {
+                signature->push_back(javaType);
+            }
+        }
+
+        atomDecl->fields.push_back(atField);
+
+        errorCount += collate_field_annotations(atomDecl, field, it->first, javaType);
+    }
+
+    return errorCount;
 }
 
-// This function flattens the fields of the AttributionNode proto in an Atom proto and generates
-// the corresponding atom decl and signature.
-bool get_non_chained_node(const Descriptor *atom, AtomDecl *atomDecl,
-                          vector<java_type_t> *signature) {
+// This function flattens the fields of the AttributionNode proto in an Atom
+// proto and generates the corresponding atom decl and signature.
+bool get_non_chained_node(const Descriptor* atom, AtomDecl* atomDecl,
+                          vector<java_type_t>* signature) {
     // Build a sorted list of the fields. Descriptor has them in source file
     // order.
-    map<int, const FieldDescriptor *> fields;
+    map<int, const FieldDescriptor*> fields;
     for (int j = 0; j < atom->field_count(); j++) {
-        const FieldDescriptor *field = atom->field(j);
+        const FieldDescriptor* field = atom->field(j);
         fields[field->number()] = field;
     }
 
     AtomDecl attributionDecl;
     vector<java_type_t> attributionSignature;
-    collate_atom(android::os::statsd::AttributionNode::descriptor(),
-                 &attributionDecl, &attributionSignature);
+    collate_atom(android::os::statsd::AttributionNode::descriptor(), &attributionDecl,
+                 &attributionSignature);
 
     // Build the type signature and the atom data.
     bool has_attribution_node = false;
-    for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
-        it != fields.end(); it++) {
-        const FieldDescriptor *field = it->second;
+    for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
+         it++) {
+        const FieldDescriptor* field = it->second;
         java_type_t javaType = java_type(field);
         if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-            atomDecl->fields.insert(
-                atomDecl->fields.end(),
-                attributionDecl.fields.begin(), attributionDecl.fields.end());
-            signature->insert(
-                signature->end(),
-                attributionSignature.begin(), attributionSignature.end());
+            atomDecl->fields.insert(atomDecl->fields.end(), attributionDecl.fields.begin(),
+                                    attributionDecl.fields.end());
+            signature->insert(signature->end(), attributionSignature.begin(),
+                              attributionSignature.end());
             has_attribution_node = true;
 
         } else {
@@ -473,118 +454,129 @@
     return has_attribution_node;
 }
 
-static void populateFieldNumberToAnnotations(
-        const AtomDecl& atomDecl,
-        FieldNumberToAnnotations* fieldNumberToAnnotations) {
+static void populateFieldNumberToAnnotations(const AtomDecl& atomDecl,
+                                             FieldNumberToAnnotations* fieldNumberToAnnotations) {
     for (FieldNumberToAnnotations::const_iterator it = atomDecl.fieldNumberToAnnotations.begin();
-            it != atomDecl.fieldNumberToAnnotations.end(); it++) {
+         it != atomDecl.fieldNumberToAnnotations.end(); it++) {
         const int fieldNumber = it->first;
         const set<shared_ptr<Annotation>>& insertAnnotationsSource = it->second;
         set<shared_ptr<Annotation>>& insertAnnotationsTarget =
                 (*fieldNumberToAnnotations)[fieldNumber];
-        insertAnnotationsTarget.insert(
-                insertAnnotationsSource.begin(),
-                insertAnnotationsSource.end());
+        insertAnnotationsTarget.insert(insertAnnotationsSource.begin(),
+                                       insertAnnotationsSource.end());
     }
 }
 
 /**
  * Gather the info about the atoms.
  */
-int collate_atoms(const Descriptor *descriptor, const string& moduleName, Atoms *atoms) {
-  int errorCount = 0;
+int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* atoms) {
+    int errorCount = 0;
 
-  int maxPushedAtomId = 2;
-  for (int i = 0; i < descriptor->field_count(); i++) {
-    const FieldDescriptor *atomField = descriptor->field(i);
+    int maxPushedAtomId = 2;
+    for (int i = 0; i < descriptor->field_count(); i++) {
+        const FieldDescriptor* atomField = descriptor->field(i);
 
-    if (moduleName != DEFAULT_MODULE_NAME) {
-        const int moduleCount = atomField->options().ExtensionSize(os::statsd::module);
-        int j;
-        for (j = 0; j < moduleCount; ++j) {
-            const string atomModuleName = atomField->options().GetExtension(os::statsd::module, j);
-            if (atomModuleName == moduleName) {
-                break;
+        if (moduleName != DEFAULT_MODULE_NAME) {
+            const int moduleCount = atomField->options().ExtensionSize(os::statsd::module);
+            int j;
+            for (j = 0; j < moduleCount; ++j) {
+                const string atomModuleName =
+                        atomField->options().GetExtension(os::statsd::module, j);
+                if (atomModuleName == moduleName) {
+                    break;
+                }
+            }
+
+            // This atom is not in the module we're interested in; skip it.
+            if (moduleCount == j) {
+                if (dbg) {
+                    printf("   Skipping %s (%d)\n", atomField->name().c_str(), atomField->number());
+                }
+                continue;
             }
         }
 
-        // This atom is not in the module we're interested in; skip it.
-        if (moduleCount == j) {
-            if (dbg) {
-              printf("   Skipping %s (%d)\n", atomField->name().c_str(), atomField->number());
-            }
+        if (dbg) {
+            printf("   %s (%d)\n", atomField->name().c_str(), atomField->number());
+        }
+
+        // StatsEvent only has one oneof, which contains only messages. Don't allow
+        // other types.
+        if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
+            print_error(atomField,
+                        "Bad type for atom. StatsEvent can only have message type "
+                        "fields: %s\n",
+                        atomField->name().c_str());
+            errorCount++;
             continue;
         }
+
+        const Descriptor* atom = atomField->message_type();
+        AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
+
+        if (atomField->options().GetExtension(os::statsd::allow_from_any_uid) == true) {
+            atomDecl.whitelisted = true;
+            if (dbg) {
+                printf("%s is whitelisted\n", atomField->name().c_str());
+            }
+        }
+
+        if (atomDecl.code < PULL_ATOM_START_ID &&
+            atomField->options().GetExtension(os::statsd::truncate_timestamp)) {
+            addAnnotationToAtomDecl(&atomDecl, ATOM_ID_FIELD_NUMBER,
+                                    ANNOTATION_ID_TRUNCATE_TIMESTAMP, ANNOTATION_TYPE_BOOL,
+                                    AnnotationValue(true));
+            if (dbg) {
+                printf("%s can have timestamp truncated\n", atomField->name().c_str());
+            }
+        }
+
+        vector<java_type_t> signature;
+        errorCount += collate_atom(atom, &atomDecl, &signature);
+        if (atomDecl.primaryFields.size() != 0 && atomDecl.exclusiveField == 0) {
+            print_error(atomField, "Cannot have a primary field without an exclusive field: %s\n",
+                        atomField->name().c_str());
+            errorCount++;
+            continue;
+        }
+
+        atoms->decls.insert(atomDecl);
+        FieldNumberToAnnotations& fieldNumberToAnnotations = atoms->signatureInfoMap[signature];
+        populateFieldNumberToAnnotations(atomDecl, &fieldNumberToAnnotations);
+
+        AtomDecl nonChainedAtomDecl(atomField->number(), atomField->name(), atom->name());
+        vector<java_type_t> nonChainedSignature;
+        if (get_non_chained_node(atom, &nonChainedAtomDecl, &nonChainedSignature)) {
+            atoms->non_chained_decls.insert(nonChainedAtomDecl);
+            FieldNumberToAnnotations& fieldNumberToAnnotations =
+                    atoms->nonChainedSignatureInfoMap[nonChainedSignature];
+            populateFieldNumberToAnnotations(atomDecl, &fieldNumberToAnnotations);
+        }
+
+        if (atomDecl.code < PULL_ATOM_START_ID && atomDecl.code > maxPushedAtomId) {
+            maxPushedAtomId = atomDecl.code;
+        }
     }
 
+    atoms->maxPushedAtomId = maxPushedAtomId;
+
     if (dbg) {
-      printf("   %s (%d)\n", atomField->name().c_str(), atomField->number());
+        printf("signatures = [\n");
+        for (map<vector<java_type_t>, FieldNumberToAnnotations>::const_iterator it =
+                     atoms->signatureInfoMap.begin();
+             it != atoms->signatureInfoMap.end(); it++) {
+            printf("   ");
+            for (vector<java_type_t>::const_iterator jt = it->first.begin(); jt != it->first.end();
+                 jt++) {
+                printf(" %d", (int)*jt);
+            }
+            printf("\n");
+        }
+        printf("]\n");
     }
 
-    // StatsEvent only has one oneof, which contains only messages. Don't allow
-    // other types.
-    if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
-      print_error(atomField,
-                  "Bad type for atom. StatsEvent can only have message type "
-                  "fields: %s\n",
-                  atomField->name().c_str());
-      errorCount++;
-      continue;
-    }
-
-    const Descriptor *atom = atomField->message_type();
-    AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
-
-    if (atomField->options().GetExtension(os::statsd::allow_from_any_uid) == true) {
-        atomDecl.whitelisted = true;
-    }
-
-    vector<java_type_t> signature;
-    errorCount += collate_atom(atom, &atomDecl, &signature);
-    if (atomDecl.primaryFields.size() != 0 && atomDecl.exclusiveField == 0) {
-        print_error(atomField,
-                  "Cannot have a primary field without an exclusive field: %s\n",
-                  atomField->name().c_str());
-        errorCount++;
-        continue;
-    }
-
-    atoms->decls.insert(atomDecl);
-    FieldNumberToAnnotations& fieldNumberToAnnotations = atoms->signatureInfoMap[signature];
-    populateFieldNumberToAnnotations(atomDecl, &fieldNumberToAnnotations);
-
-    AtomDecl nonChainedAtomDecl(atomField->number(), atomField->name(), atom->name());
-    vector<java_type_t> nonChainedSignature;
-    if (get_non_chained_node(atom, &nonChainedAtomDecl, &nonChainedSignature)) {
-        atoms->non_chained_decls.insert(nonChainedAtomDecl);
-        FieldNumberToAnnotations& fieldNumberToAnnotations =
-                atoms->nonChainedSignatureInfoMap[nonChainedSignature];
-        populateFieldNumberToAnnotations(atomDecl, &fieldNumberToAnnotations);
-    }
-
-    if (atomDecl.code < PULL_ATOM_START_ID && atomDecl.code > maxPushedAtomId) {
-        maxPushedAtomId = atomDecl.code;
-    }
-  }
-
-  atoms->maxPushedAtomId = maxPushedAtomId;
-
-  if (dbg) {
-    printf("signatures = [\n");
-    for (map<vector<java_type_t>, FieldNumberToAnnotations>::const_iterator it =
-             atoms->signatureInfoMap.begin();
-         it != atoms->signatureInfoMap.end(); it++) {
-      printf("   ");
-      for (vector<java_type_t>::const_iterator jt = it->first.begin();
-           jt != it->first.end(); jt++){
-        printf(" %d", (int)*jt);
-      }
-      printf("\n");
-    }
-    printf("]\n");
-  }
-
-  return errorCount;
+    return errorCount;
 }
 
 }  // namespace stats_log_api_gen
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index c6dad1d..2aedb21 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -17,24 +17,24 @@
 #ifndef ANDROID_STATS_LOG_API_GEN_COLLATION_H
 #define ANDROID_STATS_LOG_API_GEN_COLLATION_H
 
-
 #include <google/protobuf/descriptor.h>
-#include "frameworks/base/cmds/statsd/src/atom_field_options.pb.h"
 
+#include <map>
 #include <set>
 #include <vector>
-#include <map>
+
+#include "frameworks/base/cmds/statsd/src/atom_field_options.pb.h"
 
 namespace android {
 namespace stats_log_api_gen {
 
+using google::protobuf::Descriptor;
+using google::protobuf::FieldDescriptor;
 using std::map;
 using std::set;
 using std::shared_ptr;
 using std::string;
 using std::vector;
-using google::protobuf::Descriptor;
-using google::protobuf::FieldDescriptor;
 
 const int PULL_ATOM_START_ID = 10000;
 
@@ -52,26 +52,28 @@
 const int STATE_OPTION_PRIMARY_FIELD_FIRST_UID = os::statsd::StateField::PRIMARY_FIELD_FIRST_UID;
 const int STATE_OPTION_PRIMARY = os::statsd::StateField::PRIMARY_FIELD;
 
+const int ATOM_ID_FIELD_NUMBER = -1;
+
 const string DEFAULT_MODULE_NAME = "DEFAULT";
 
 /**
  * The types for atom parameters.
  */
 typedef enum {
-  JAVA_TYPE_UNKNOWN = 0,
+    JAVA_TYPE_UNKNOWN = 0,
 
-  JAVA_TYPE_ATTRIBUTION_CHAIN = 1,
-  JAVA_TYPE_BOOLEAN = 2,
-  JAVA_TYPE_INT = 3,
-  JAVA_TYPE_LONG = 4,
-  JAVA_TYPE_FLOAT = 5,
-  JAVA_TYPE_DOUBLE = 6,
-  JAVA_TYPE_STRING = 7,
-  JAVA_TYPE_ENUM = 8,
-  JAVA_TYPE_KEY_VALUE_PAIR = 9,
+    JAVA_TYPE_ATTRIBUTION_CHAIN = 1,
+    JAVA_TYPE_BOOLEAN = 2,
+    JAVA_TYPE_INT = 3,
+    JAVA_TYPE_LONG = 4,
+    JAVA_TYPE_FLOAT = 5,
+    JAVA_TYPE_DOUBLE = 6,
+    JAVA_TYPE_STRING = 7,
+    JAVA_TYPE_ENUM = 8,
+    JAVA_TYPE_KEY_VALUE_PAIR = 9,
 
-  JAVA_TYPE_OBJECT = -1,
-  JAVA_TYPE_BYTE_ARRAY = -2,
+    JAVA_TYPE_OBJECT = -1,
+    JAVA_TYPE_BYTE_ARRAY = -2,
 } java_type_t;
 
 enum AnnotationType {
@@ -84,8 +86,10 @@
     int intValue;
     bool boolValue;
 
-    AnnotationValue(const int value): intValue(value) {}
-    AnnotationValue(const bool value): boolValue(value) {}
+    AnnotationValue(const int value) : intValue(value) {
+    }
+    AnnotationValue(const bool value) : boolValue(value) {
+    }
 };
 
 struct Annotation {
@@ -95,16 +99,18 @@
     AnnotationValue value;
 
     inline Annotation(unsigned char annotationId, int atomId, AnnotationType type,
-            AnnotationValue value):
-            annotationId(annotationId), atomId(atomId), type(type), value(value) {}
-    inline ~Annotation() {}
+                      AnnotationValue value)
+        : annotationId(annotationId), atomId(atomId), type(type), value(value) {
+    }
+    inline ~Annotation() {
+    }
 
     inline bool operator<(const Annotation& that) const {
         return atomId == that.atomId ? annotationId < that.annotationId : atomId < that.atomId;
     }
 };
 
-using FieldNumberToAnnotations =  map<int, set<shared_ptr<Annotation>>>;
+using FieldNumberToAnnotations = map<int, set<shared_ptr<Annotation>>>;
 
 /**
  * The name and type for an atom field.
@@ -113,16 +119,20 @@
     string name;
     java_type_t javaType;
 
-    // If the field is of type enum, the following map contains the list of enum values.
+    // If the field is of type enum, the following map contains the list of enum
+    // values.
     map<int /* numeric value */, string /* value name */> enumValues;
 
-    inline AtomField() :name(), javaType(JAVA_TYPE_UNKNOWN) {}
-    inline AtomField(const AtomField& that) :name(that.name),
-                                             javaType(that.javaType),
-                                             enumValues(that.enumValues) {}
+    inline AtomField() : name(), javaType(JAVA_TYPE_UNKNOWN) {
+    }
+    inline AtomField(const AtomField& that)
+        : name(that.name), javaType(that.javaType), enumValues(that.enumValues) {
+    }
 
-    inline AtomField(string n, java_type_t jt) :name(n), javaType(jt) {}
-    inline ~AtomField() {}
+    inline AtomField(string n, java_type_t jt) : name(n), javaType(jt) {
+    }
+    inline ~AtomField() {
+    }
 };
 
 /**
@@ -147,6 +157,8 @@
 
     bool whitelisted = false;
 
+    bool truncateTimestamp = false;
+
     AtomDecl();
     AtomDecl(const AtomDecl& that);
     AtomDecl(int code, const string& name, const string& message);
@@ -169,10 +181,9 @@
  * Gather the information about the atoms.  Returns the number of errors.
  */
 int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms* atoms);
-int collate_atom(const Descriptor *atom, AtomDecl *atomDecl, vector<java_type_t> *signature);
+int collate_atom(const Descriptor* atom, AtomDecl* atomDecl, vector<java_type_t>* signature);
 
 }  // namespace stats_log_api_gen
 }  // namespace android
 
-
-#endif // ANDROID_STATS_LOG_API_GEN_COLLATION_H
+#endif  // ANDROID_STATS_LOG_API_GEN_COLLATION_H
diff --git a/tools/stats_log_api_gen/atoms_info_writer.cpp b/tools/stats_log_api_gen/atoms_info_writer.cpp
index 4f66f68..6944752 100644
--- a/tools/stats_log_api_gen/atoms_info_writer.cpp
+++ b/tools/stats_log_api_gen/atoms_info_writer.cpp
@@ -15,12 +15,13 @@
  */
 
 #include "atoms_info_writer.h"
-#include "utils.h"
 
 #include <map>
 #include <set>
 #include <vector>
 
+#include "utils.h"
+
 namespace android {
 namespace stats_log_api_gen {
 
@@ -42,32 +43,27 @@
             "  const static std::set<int> "
             "kTruncatingTimestampAtomBlackList;\n");
     fprintf(out, "  const static std::map<int, int> kAtomsWithUidField;\n");
-    fprintf(out,
-            "  const static std::set<int> kAtomsWithAttributionChain;\n");
+    fprintf(out, "  const static std::set<int> kAtomsWithAttributionChain;\n");
     fprintf(out,
             "  const static std::map<int, StateAtomFieldOptions> "
             "kStateAtomsFieldOptions;\n");
-    fprintf(out,
-            "  const static std::set<int> kWhitelistedAtoms;\n");
+    fprintf(out, "  const static std::set<int> kWhitelistedAtoms;\n");
     fprintf(out, "};\n");
     fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", atoms.maxPushedAtomId);
-
 }
 
 static void write_atoms_info_cpp_body(FILE* out, const Atoms& atoms) {
-    std::set<string> kTruncatingAtomNames = {
-            "mobile_radio_power_state_changed",
-            "audio_state_changed",
-            "call_state_changed",
-            "phone_signal_strength_changed",
-            "mobile_bytes_transfer_by_fg_bg",
-            "mobile_bytes_transfer"
-    };
+    std::set<string> kTruncatingAtomNames = {"mobile_radio_power_state_changed",
+                                             "audio_state_changed",
+                                             "call_state_changed",
+                                             "phone_signal_strength_changed",
+                                             "mobile_bytes_transfer_by_fg_bg",
+                                             "mobile_bytes_transfer"};
     fprintf(out,
             "const std::set<int> "
             "AtomsInfo::kTruncatingTimestampAtomBlackList = {\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-         atom != atoms.decls.end(); atom++) {
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         if (kTruncatingAtomNames.find(atom->name) != kTruncatingAtomNames.end()) {
             const string constant = make_constant_name(atom->name);
             fprintf(out, "    %d, // %s\n", atom->code, constant.c_str());
@@ -77,10 +73,9 @@
     fprintf(out, "};\n");
     fprintf(out, "\n");
 
-    fprintf(out,
-            "const std::set<int> AtomsInfo::kAtomsWithAttributionChain = {\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-         atom != atoms.decls.end(); atom++) {
+    fprintf(out, "const std::set<int> AtomsInfo::kAtomsWithAttributionChain = {\n");
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         for (vector<AtomField>::const_iterator field = atom->fields.begin();
              field != atom->fields.end(); field++) {
             if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
@@ -94,10 +89,9 @@
     fprintf(out, "};\n");
     fprintf(out, "\n");
 
-    fprintf(out,
-            "const std::set<int> AtomsInfo::kWhitelistedAtoms = {\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-         atom != atoms.decls.end(); atom++) {
+    fprintf(out, "const std::set<int> AtomsInfo::kWhitelistedAtoms = {\n");
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         if (atom->whitelisted) {
             const string constant = make_constant_name(atom->name);
             fprintf(out, "    %d, // %s\n", atom->code, constant.c_str());
@@ -109,8 +103,8 @@
 
     fprintf(out, "static std::map<int, int> getAtomUidField() {\n");
     fprintf(out, "    std::map<int, int> uidField;\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-         atom != atoms.decls.end(); atom++) {
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         if (atom->uidField == 0) {
             continue;
         }
@@ -118,8 +112,8 @@
                 "\n    // Adding uid field for atom "
                 "(%d)%s\n",
                 atom->code, atom->name.c_str());
-        fprintf(out, "    uidField[%d /* %s */] = %d;\n",
-                atom->code, make_constant_name(atom->name).c_str(), atom->uidField);
+        fprintf(out, "    uidField[%d /* %s */] = %d;\n", atom->code,
+                make_constant_name(atom->name).c_str(), atom->uidField);
     }
 
     fprintf(out, "    return uidField;\n");
@@ -134,8 +128,8 @@
             "getStateAtomFieldOptions() {\n");
     fprintf(out, "    std::map<int, StateAtomFieldOptions> options;\n");
     fprintf(out, "    StateAtomFieldOptions* opt;\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-         atom != atoms.decls.end(); atom++) {
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         if (atom->primaryFields.size() == 0 && atom->exclusiveField == 0) {
             continue;
         }
@@ -143,8 +137,8 @@
                 "\n    // Adding primary and exclusive fields for atom "
                 "(%d)%s\n",
                 atom->code, atom->name.c_str());
-        fprintf(out, "    opt = &(options[%d /* %s */]);\n",
-                atom->code, make_constant_name(atom->name).c_str());
+        fprintf(out, "    opt = &(options[%d /* %s */]);\n", atom->code,
+                make_constant_name(atom->name).c_str());
         fprintf(out, "    opt->primaryFields.reserve(%lu);\n", atom->primaryFields.size());
         for (const auto& field : atom->primaryFields) {
             fprintf(out, "    opt->primaryFields.push_back(%d);\n", field);
@@ -174,7 +168,7 @@
             "getStateAtomFieldOptions();\n");
 }
 
-int write_atoms_info_header(FILE* out, const Atoms &atoms, const string& namespaceStr) {
+int write_atoms_info_header(FILE* out, const Atoms& atoms, const string& namespaceStr) {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
@@ -195,8 +189,8 @@
     return 0;
 }
 
-int write_atoms_info_cpp(FILE *out, const Atoms &atoms, const string& namespaceStr,
-        const string& importHeader) {
+int write_atoms_info_cpp(FILE* out, const Atoms& atoms, const string& namespaceStr,
+                         const string& importHeader) {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
diff --git a/tools/stats_log_api_gen/atoms_info_writer.h b/tools/stats_log_api_gen/atoms_info_writer.h
index d04e65a..ffe9e43 100644
--- a/tools/stats_log_api_gen/atoms_info_writer.h
+++ b/tools/stats_log_api_gen/atoms_info_writer.h
@@ -16,18 +16,18 @@
 
 #pragma once
 
-#include "Collation.h"
-
 #include <stdio.h>
 #include <string.h>
 
+#include "Collation.h"
+
 namespace android {
 namespace stats_log_api_gen {
 
 using namespace std;
 
 int write_atoms_info_cpp(FILE* out, const Atoms& atoms, const string& namespaceStr,
-        const string& importHeader);
+                         const string& importHeader);
 
 int write_atoms_info_header(FILE* out, const Atoms& atoms, const string& namespaceStr);
 
diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp
index 18508d2..5a22b5c 100644
--- a/tools/stats_log_api_gen/java_writer.cpp
+++ b/tools/stats_log_api_gen/java_writer.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "java_writer.h"
+
 #include "java_writer_q.h"
 #include "utils.h"
 
@@ -22,9 +23,8 @@
 namespace stats_log_api_gen {
 
 static int write_java_q_logger_class(
-        FILE* out,
-        const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
-        const AtomDecl &attributionDecl) {
+        FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
+        const AtomDecl& attributionDecl) {
     fprintf(out, "\n");
     fprintf(out, "    // Write logging helper methods for statsd in Q and earlier.\n");
     fprintf(out, "    private static class QLogger {\n");
@@ -34,29 +34,27 @@
     // Print Q write methods.
     fprintf(out, "\n");
     fprintf(out, "        // Write methods.\n");
-    write_java_methods_q_schema(
-            out, signatureInfoMap, attributionDecl, "        ");
+    write_java_methods_q_schema(out, signatureInfoMap, attributionDecl, "        ");
 
     fprintf(out, "    }\n");
     return 0;
 }
 
-static void write_annotations(
-        FILE* out, int argIndex,
-        const FieldNumberToAnnotations& fieldNumberToAnnotations) {
+static void write_annotations(FILE* out, int argIndex,
+                              const FieldNumberToAnnotations& fieldNumberToAnnotations) {
     auto it = fieldNumberToAnnotations.find(argIndex);
     if (it == fieldNumberToAnnotations.end()) {
         return;
     }
     const set<shared_ptr<Annotation>>& annotations = it->second;
-    for (auto& annotation: annotations) {
+    for (auto& annotation : annotations) {
         // TODO(b/151744250): Group annotations for same atoms.
         // TODO(b/151786433): Write atom constant name instead of atom id literal.
         fprintf(out, "        if (code == %d) {\n", annotation->atomId);
-        switch(annotation->type) {
+        switch (annotation->type) {
             case ANNOTATION_TYPE_INT:
-                // TODO(b/151776731): Check for reset state annotation and only include reset state
-                // when field value == default state annotation value.
+                // TODO(b/151776731): Check for reset state annotation and only include
+                // reset state when field value == default state annotation value.
                 // TODO(b/151786433): Write annotation constant name instead of
                 // annotation id literal.
                 fprintf(out, "            builder.addIntAnnotation((byte) %d, %d);\n",
@@ -66,8 +64,7 @@
                 // TODO(b/151786433): Write annotation constant name instead of
                 // annotation id literal.
                 fprintf(out, "            builder.addBooleanAnnotation((byte) %d, %s);\n",
-                        annotation->annotationId,
-                        annotation->value.boolValue ? "true" : "false");
+                        annotation->annotationId, annotation->value.boolValue ? "true" : "false");
                 break;
             default:
                 break;
@@ -77,24 +74,21 @@
 }
 
 static int write_java_methods(
-        FILE* out,
-        const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
-        const AtomDecl &attributionDecl,
-        const bool supportQ
-        ) {
+        FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
+        const AtomDecl& attributionDecl, const bool supportQ) {
     for (auto signatureInfoMapIt = signatureInfoMap.begin();
-            signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
+         signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
         // Print method signature.
         fprintf(out, "    public static void write(int code");
         const vector<java_type_t>& signature = signatureInfoMapIt->first;
         const FieldNumberToAnnotations& fieldNumberToAnnotations = signatureInfoMapIt->second;
         int argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
                 for (auto chainField : attributionDecl.fields) {
-                    fprintf(out, ", %s[] %s",
-                        java_type_name(chainField.javaType), chainField.name.c_str());
+                    fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
+                            chainField.name.c_str());
                 }
             } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
                 fprintf(out, ", android.util.SparseArray<Object> valueMap");
@@ -108,134 +102,134 @@
         // Print method body.
         string indent("");
         if (supportQ) {
-            // TODO(b/146235828): Use just SDK_INT check once it is incremented from Q.
+            // TODO(b/146235828): Use just SDK_INT check once it is incremented from
+            // Q.
             fprintf(out, "        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q\n");
-            fprintf(out, "                || (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q\n");
+            fprintf(out,
+                    "                || (Build.VERSION.SDK_INT == "
+                    "Build.VERSION_CODES.Q\n");
             fprintf(out, "                    && Build.VERSION.PREVIEW_SDK_INT > 0)) {\n");
             indent = "    ";
         }
 
         // Start StatsEvent.Builder.
-        fprintf(out, "%s        final StatsEvent.Builder builder = StatsEvent.newBuilder();\n",
+        fprintf(out,
+                "%s        final StatsEvent.Builder builder = "
+                "StatsEvent.newBuilder();\n",
                 indent.c_str());
 
         // Write atom code.
         fprintf(out, "%s        builder.setAtomId(code);\n", indent.c_str());
+        write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAnnotations);
 
         // Write the args.
         argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             switch (*arg) {
-            case JAVA_TYPE_BOOLEAN:
-                fprintf(out, "%s        builder.writeBoolean(arg%d);\n", indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_INT:
-            case JAVA_TYPE_ENUM:
-                fprintf(out, "%s        builder.writeInt(arg%d);\n", indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_FLOAT:
-                fprintf(out, "%s        builder.writeFloat(arg%d);\n", indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_LONG:
-                fprintf(out, "%s        builder.writeLong(arg%d);\n", indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_STRING:
-                fprintf(out, "%s        builder.writeString(arg%d);\n", indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_BYTE_ARRAY:
-                fprintf(out, "%s        builder.writeByteArray(null == arg%d ? new byte[0] : arg%d);\n",
-                        indent.c_str(), argIndex, argIndex);
-                break;
-            case JAVA_TYPE_ATTRIBUTION_CHAIN:
-            {
-                const char* uidName = attributionDecl.fields.front().name.c_str();
-                const char* tagName = attributionDecl.fields.back().name.c_str();
+                case JAVA_TYPE_BOOLEAN:
+                    fprintf(out, "%s        builder.writeBoolean(arg%d);\n", indent.c_str(),
+                            argIndex);
+                    break;
+                case JAVA_TYPE_INT:
+                case JAVA_TYPE_ENUM:
+                    fprintf(out, "%s        builder.writeInt(arg%d);\n", indent.c_str(), argIndex);
+                    break;
+                case JAVA_TYPE_FLOAT:
+                    fprintf(out, "%s        builder.writeFloat(arg%d);\n", indent.c_str(),
+                            argIndex);
+                    break;
+                case JAVA_TYPE_LONG:
+                    fprintf(out, "%s        builder.writeLong(arg%d);\n", indent.c_str(), argIndex);
+                    break;
+                case JAVA_TYPE_STRING:
+                    fprintf(out, "%s        builder.writeString(arg%d);\n", indent.c_str(),
+                            argIndex);
+                    break;
+                case JAVA_TYPE_BYTE_ARRAY:
+                    fprintf(out,
+                            "%s        builder.writeByteArray(null == arg%d ? new byte[0] : "
+                            "arg%d);\n",
+                            indent.c_str(), argIndex, argIndex);
+                    break;
+                case JAVA_TYPE_ATTRIBUTION_CHAIN: {
+                    const char* uidName = attributionDecl.fields.front().name.c_str();
+                    const char* tagName = attributionDecl.fields.back().name.c_str();
 
-                fprintf(out, "%s        builder.writeAttributionChain(\n", indent.c_str());
-                fprintf(out, "%s                null == %s ? new int[0] : %s,\n",
-                        indent.c_str(), uidName, uidName);
-                fprintf(out, "%s                null == %s ? new String[0] : %s);\n",
-                        indent.c_str(), tagName, tagName);
-                break;
-            }
-            case JAVA_TYPE_KEY_VALUE_PAIR:
-                fprintf(out, "\n");
-                fprintf(out,
-                        "%s        // Write KeyValuePairs.\n", indent.c_str());
-                fprintf(out,
-                        "%s        final int count = valueMap.size();\n", indent.c_str());
-                fprintf(out,
-                        "%s        android.util.SparseIntArray intMap = null;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s        android.util.SparseLongArray longMap = null;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s        android.util.SparseArray<String> stringMap = null;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s        android.util.SparseArray<Float> floatMap = null;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s        for (int i = 0; i < count; i++) {\n", indent.c_str());
-                fprintf(out,
-                        "%s            final int key = valueMap.keyAt(i);\n", indent.c_str());
-                fprintf(out,
-                        "%s            final Object value = valueMap.valueAt(i);\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s            if (value instanceof Integer) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                if (null == intMap) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                    intMap = new android.util.SparseIntArray();\n", indent.c_str());
-                fprintf(out,
-                        "%s                }\n", indent.c_str());
-                fprintf(out,
-                        "%s                intMap.put(key, (Integer) value);\n", indent.c_str());
-                fprintf(out,
-                        "%s            } else if (value instanceof Long) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                if (null == longMap) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                    longMap = new android.util.SparseLongArray();\n", indent.c_str());
-                fprintf(out,
-                        "%s                }\n", indent.c_str());
-                fprintf(out,
-                        "%s                longMap.put(key, (Long) value);\n", indent.c_str());
-                fprintf(out,
-                        "%s            } else if (value instanceof String) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                if (null == stringMap) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                    stringMap = new android.util.SparseArray<>();\n", indent.c_str());
-                fprintf(out,
-                        "%s                }\n", indent.c_str());
-                fprintf(out,
-                        "%s                stringMap.put(key, (String) value);\n", indent.c_str());
-                fprintf(out,
-                        "%s            } else if (value instanceof Float) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                if (null == floatMap) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                    floatMap = new android.util.SparseArray<>();\n", indent.c_str());
-                fprintf(out,
-                        "%s                }\n", indent.c_str());
-                fprintf(out,
-                        "%s                floatMap.put(key, (Float) value);\n", indent.c_str());
-                fprintf(out,
-                        "%s            }\n", indent.c_str());
-                fprintf(out,
-                        "%s        }\n", indent.c_str());
-                fprintf(out,
-                        "%s        builder.writeKeyValuePairs("
-                        "intMap, longMap, stringMap, floatMap);\n", indent.c_str());
-                break;
-            default:
-                // Unsupported types: OBJECT, DOUBLE.
-                fprintf(stderr, "Encountered unsupported type.");
-                return 1;
+                    fprintf(out, "%s        builder.writeAttributionChain(\n", indent.c_str());
+                    fprintf(out, "%s                null == %s ? new int[0] : %s,\n",
+                            indent.c_str(), uidName, uidName);
+                    fprintf(out, "%s                null == %s ? new String[0] : %s);\n",
+                            indent.c_str(), tagName, tagName);
+                    break;
+                }
+                case JAVA_TYPE_KEY_VALUE_PAIR:
+                    fprintf(out, "\n");
+                    fprintf(out, "%s        // Write KeyValuePairs.\n", indent.c_str());
+                    fprintf(out, "%s        final int count = valueMap.size();\n", indent.c_str());
+                    fprintf(out, "%s        android.util.SparseIntArray intMap = null;\n",
+                            indent.c_str());
+                    fprintf(out, "%s        android.util.SparseLongArray longMap = null;\n",
+                            indent.c_str());
+                    fprintf(out, "%s        android.util.SparseArray<String> stringMap = null;\n",
+                            indent.c_str());
+                    fprintf(out, "%s        android.util.SparseArray<Float> floatMap = null;\n",
+                            indent.c_str());
+                    fprintf(out, "%s        for (int i = 0; i < count; i++) {\n", indent.c_str());
+                    fprintf(out, "%s            final int key = valueMap.keyAt(i);\n",
+                            indent.c_str());
+                    fprintf(out, "%s            final Object value = valueMap.valueAt(i);\n",
+                            indent.c_str());
+                    fprintf(out, "%s            if (value instanceof Integer) {\n", indent.c_str());
+                    fprintf(out, "%s                if (null == intMap) {\n", indent.c_str());
+                    fprintf(out,
+                            "%s                    intMap = new "
+                            "android.util.SparseIntArray();\n",
+                            indent.c_str());
+                    fprintf(out, "%s                }\n", indent.c_str());
+                    fprintf(out, "%s                intMap.put(key, (Integer) value);\n",
+                            indent.c_str());
+                    fprintf(out, "%s            } else if (value instanceof Long) {\n",
+                            indent.c_str());
+                    fprintf(out, "%s                if (null == longMap) {\n", indent.c_str());
+                    fprintf(out,
+                            "%s                    longMap = new "
+                            "android.util.SparseLongArray();\n",
+                            indent.c_str());
+                    fprintf(out, "%s                }\n", indent.c_str());
+                    fprintf(out, "%s                longMap.put(key, (Long) value);\n",
+                            indent.c_str());
+                    fprintf(out, "%s            } else if (value instanceof String) {\n",
+                            indent.c_str());
+                    fprintf(out, "%s                if (null == stringMap) {\n", indent.c_str());
+                    fprintf(out,
+                            "%s                    stringMap = new "
+                            "android.util.SparseArray<>();\n",
+                            indent.c_str());
+                    fprintf(out, "%s                }\n", indent.c_str());
+                    fprintf(out, "%s                stringMap.put(key, (String) value);\n",
+                            indent.c_str());
+                    fprintf(out, "%s            } else if (value instanceof Float) {\n",
+                            indent.c_str());
+                    fprintf(out, "%s                if (null == floatMap) {\n", indent.c_str());
+                    fprintf(out,
+                            "%s                    floatMap = new "
+                            "android.util.SparseArray<>();\n",
+                            indent.c_str());
+                    fprintf(out, "%s                }\n", indent.c_str());
+                    fprintf(out, "%s                floatMap.put(key, (Float) value);\n",
+                            indent.c_str());
+                    fprintf(out, "%s            }\n", indent.c_str());
+                    fprintf(out, "%s        }\n", indent.c_str());
+                    fprintf(out,
+                            "%s        builder.writeKeyValuePairs("
+                            "intMap, longMap, stringMap, floatMap);\n",
+                            indent.c_str());
+                    break;
+                default:
+                    // Unsupported types: OBJECT, DOUBLE.
+                    fprintf(stderr, "Encountered unsupported type.");
+                    return 1;
             }
             write_annotations(out, argIndex, fieldNumberToAnnotations);
             argIndex++;
@@ -251,7 +245,7 @@
             fprintf(out, "            QLogger.write(code");
             argIndex = 1;
             for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+                 arg != signature.end(); arg++) {
                 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
                     const char* uidName = attributionDecl.fields.front().name.c_str();
                     const char* tagName = attributionDecl.fields.back().name.c_str();
@@ -266,20 +260,18 @@
                 argIndex++;
             }
             fprintf(out, ");\n");
-            fprintf(out, "        }\n"); // if
+            fprintf(out, "        }\n");  // if
         }
 
-        fprintf(out, "    }\n"); // method
+        fprintf(out, "    }\n");  // method
         fprintf(out, "\n");
     }
     return 0;
-
 }
 
-int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
-                                    const string& javaClass,
-                                    const string& javaPackage, const bool supportQ,
-                                    const bool supportWorkSource) {
+int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
+                         const string& javaClass, const string& javaPackage, const bool supportQ,
+                         const bool supportWorkSource) {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
@@ -308,17 +300,14 @@
 
     // Print write methods.
     fprintf(out, "    // Write methods\n");
-    errors += write_java_methods(
-            out, atoms.signatureInfoMap, attributionDecl, supportQ);
-    errors += write_java_non_chained_methods(
-            out, atoms.nonChainedSignatureInfoMap);
+    errors += write_java_methods(out, atoms.signatureInfoMap, attributionDecl, supportQ);
+    errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap);
     if (supportWorkSource) {
         errors += write_java_work_source_methods(out, atoms.signatureInfoMap);
     }
 
     if (supportQ) {
-        errors += write_java_q_logger_class(
-                out, atoms.signatureInfoMap, attributionDecl);
+        errors += write_java_q_logger_class(out, atoms.signatureInfoMap, attributionDecl);
     }
 
     fprintf(out, "}\n");
diff --git a/tools/stats_log_api_gen/java_writer.h b/tools/stats_log_api_gen/java_writer.h
index 4e1365e..8b3b505 100644
--- a/tools/stats_log_api_gen/java_writer.h
+++ b/tools/stats_log_api_gen/java_writer.h
@@ -16,25 +16,23 @@
 
 #pragma once
 
-#include "Collation.h"
+#include <stdio.h>
+#include <string.h>
 
 #include <map>
 #include <set>
 #include <vector>
 
-#include <stdio.h>
-#include <string.h>
+#include "Collation.h"
 
 namespace android {
 namespace stats_log_api_gen {
 
 using namespace std;
 
-int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
-                         const string& javaClass,
-                         const string& javaPackage, const bool supportQ,
+int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
+                         const string& javaClass, const string& javaPackage, const bool supportQ,
                          const bool supportWorkSource);
 
 }  // namespace stats_log_api_gen
 }  // namespace android
-
diff --git a/tools/stats_log_api_gen/java_writer_q.cpp b/tools/stats_log_api_gen/java_writer_q.cpp
index 329c25d..7d22583 100644
--- a/tools/stats_log_api_gen/java_writer_q.cpp
+++ b/tools/stats_log_api_gen/java_writer_q.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "java_writer_q.h"
+
 #include "utils.h"
 
 namespace android {
@@ -24,7 +25,8 @@
     fprintf(out, "%s// Payload limits.\n", indent.c_str());
     fprintf(out, "%sprivate static final int LOGGER_ENTRY_MAX_PAYLOAD = 4068;\n", indent.c_str());
     fprintf(out,
-            "%sprivate static final int MAX_EVENT_PAYLOAD = LOGGER_ENTRY_MAX_PAYLOAD - 4;\n",
+            "%sprivate static final int MAX_EVENT_PAYLOAD = "
+            "LOGGER_ENTRY_MAX_PAYLOAD - 4;\n",
             indent.c_str());
 
     // Value types. Must match with EventLog.java and log.h.
@@ -37,36 +39,36 @@
     fprintf(out, "%sprivate static final byte FLOAT_TYPE = 4;\n", indent.c_str());
 
     // Size of each value type.
-    // Booleans, ints, floats, and enums take 5 bytes, 1 for the type and 4 for the value.
+    // Booleans, ints, floats, and enums take 5 bytes, 1 for the type and 4 for
+    // the value.
     fprintf(out, "\n");
     fprintf(out, "%s// Size of each value type.\n", indent.c_str());
     fprintf(out, "%sprivate static final int INT_TYPE_SIZE = 5;\n", indent.c_str());
     fprintf(out, "%sprivate static final int FLOAT_TYPE_SIZE = 5;\n", indent.c_str());
     // Longs take 9 bytes, 1 for the type and 8 for the value.
     fprintf(out, "%sprivate static final int LONG_TYPE_SIZE = 9;\n", indent.c_str());
-    // Strings take 5 metadata bytes: 1 byte is for the type, 4 are for the length.
+    // Strings take 5 metadata bytes: 1 byte is for the type, 4 are for the
+    // length.
     fprintf(out, "%sprivate static final int STRING_TYPE_OVERHEAD = 5;\n", indent.c_str());
     fprintf(out, "%sprivate static final int LIST_TYPE_OVERHEAD = 2;\n", indent.c_str());
 }
 
 int write_java_methods_q_schema(
-        FILE* out,
-        const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
-        const AtomDecl &attributionDecl,
-        const string& indent) {
+        FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
+        const AtomDecl& attributionDecl, const string& indent) {
     int requiredHelpers = 0;
     for (auto signatureInfoMapIt = signatureInfoMap.begin();
-            signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
+         signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
         // Print method signature.
         vector<java_type_t> signature = signatureInfoMapIt->first;
         fprintf(out, "%spublic static void write(int code", indent.c_str());
         int argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
                 for (auto chainField : attributionDecl.fields) {
-                    fprintf(out, ", %s[] %s",
-                        java_type_name(chainField.javaType), chainField.name.c_str());
+                    fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
+                            chainField.name.c_str());
                 }
             } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
                 fprintf(out, ", android.util.SparseArray<Object> valueMap");
@@ -81,190 +83,174 @@
         fprintf(out, "%s    // Initial overhead of the list, timestamp, and atom tag.\n",
                 indent.c_str());
         fprintf(out,
-                "%s    int needed = LIST_TYPE_OVERHEAD + LONG_TYPE_SIZE + INT_TYPE_SIZE;\n",
+                "%s    int needed = LIST_TYPE_OVERHEAD + LONG_TYPE_SIZE + "
+                "INT_TYPE_SIZE;\n",
                 indent.c_str());
         argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             switch (*arg) {
-            case JAVA_TYPE_BOOLEAN:
-            case JAVA_TYPE_INT:
-            case JAVA_TYPE_FLOAT:
-            case JAVA_TYPE_ENUM:
-                fprintf(out, "%s    needed += INT_TYPE_SIZE;\n", indent.c_str());
-                break;
-            case JAVA_TYPE_LONG:
-                // Longs take 9 bytes, 1 for the type and 8 for the value.
-                fprintf(out, "%s    needed += LONG_TYPE_SIZE;\n", indent.c_str());
-                break;
-            case JAVA_TYPE_STRING:
-                // Strings take 5 metadata bytes + length of byte encoded string.
-                fprintf(out, "%s    if (arg%d == null) {\n", indent.c_str(), argIndex);
-                fprintf(out, "%s        arg%d = \"\";\n", indent.c_str(), argIndex);
-                fprintf(out, "%s    }\n", indent.c_str());
-                fprintf(out,
-                        "%s    byte[] arg%dBytes = "
-                        "arg%d.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n",
-                        indent.c_str(), argIndex, argIndex);
-                fprintf(out, "%s    needed += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n",
-                        indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_BYTE_ARRAY:
-                // Byte arrays take 5 metadata bytes + length of byte array.
-                fprintf(out, "%s    if (arg%d == null) {\n", indent.c_str(), argIndex);
-                fprintf(out, "%s        arg%d = new byte[0];\n", indent.c_str(), argIndex);
-                fprintf(out, "%s    }\n", indent.c_str());
-                fprintf(out, "%s    needed += STRING_TYPE_OVERHEAD + arg%d.length;\n",
-                        indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_ATTRIBUTION_CHAIN:
-            {
-                const char* uidName = attributionDecl.fields.front().name.c_str();
-                const char* tagName = attributionDecl.fields.back().name.c_str();
-                // Null checks on the params.
-                fprintf(out, "%s    if (%s == null) {\n", indent.c_str(), uidName);
-                fprintf(out, "%s        %s = new %s[0];\n", indent.c_str(), uidName,
-                        java_type_name(attributionDecl.fields.front().javaType));
-                fprintf(out, "%s    }\n", indent.c_str());
-                fprintf(out, "%s    if (%s == null) {\n", indent.c_str(), tagName);
-                fprintf(out, "%s        %s = new %s[0];\n", indent.c_str(), tagName,
-                        java_type_name(attributionDecl.fields.back().javaType));
-                fprintf(out, "%s    }\n", indent.c_str());
+                case JAVA_TYPE_BOOLEAN:
+                case JAVA_TYPE_INT:
+                case JAVA_TYPE_FLOAT:
+                case JAVA_TYPE_ENUM:
+                    fprintf(out, "%s    needed += INT_TYPE_SIZE;\n", indent.c_str());
+                    break;
+                case JAVA_TYPE_LONG:
+                    // Longs take 9 bytes, 1 for the type and 8 for the value.
+                    fprintf(out, "%s    needed += LONG_TYPE_SIZE;\n", indent.c_str());
+                    break;
+                case JAVA_TYPE_STRING:
+                    // Strings take 5 metadata bytes + length of byte encoded string.
+                    fprintf(out, "%s    if (arg%d == null) {\n", indent.c_str(), argIndex);
+                    fprintf(out, "%s        arg%d = \"\";\n", indent.c_str(), argIndex);
+                    fprintf(out, "%s    }\n", indent.c_str());
+                    fprintf(out,
+                            "%s    byte[] arg%dBytes = "
+                            "arg%d.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n",
+                            indent.c_str(), argIndex, argIndex);
+                    fprintf(out, "%s    needed += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n",
+                            indent.c_str(), argIndex);
+                    break;
+                case JAVA_TYPE_BYTE_ARRAY:
+                    // Byte arrays take 5 metadata bytes + length of byte array.
+                    fprintf(out, "%s    if (arg%d == null) {\n", indent.c_str(), argIndex);
+                    fprintf(out, "%s        arg%d = new byte[0];\n", indent.c_str(), argIndex);
+                    fprintf(out, "%s    }\n", indent.c_str());
+                    fprintf(out, "%s    needed += STRING_TYPE_OVERHEAD + arg%d.length;\n",
+                            indent.c_str(), argIndex);
+                    break;
+                case JAVA_TYPE_ATTRIBUTION_CHAIN: {
+                    const char* uidName = attributionDecl.fields.front().name.c_str();
+                    const char* tagName = attributionDecl.fields.back().name.c_str();
+                    // Null checks on the params.
+                    fprintf(out, "%s    if (%s == null) {\n", indent.c_str(), uidName);
+                    fprintf(out, "%s        %s = new %s[0];\n", indent.c_str(), uidName,
+                            java_type_name(attributionDecl.fields.front().javaType));
+                    fprintf(out, "%s    }\n", indent.c_str());
+                    fprintf(out, "%s    if (%s == null) {\n", indent.c_str(), tagName);
+                    fprintf(out, "%s        %s = new %s[0];\n", indent.c_str(), tagName,
+                            java_type_name(attributionDecl.fields.back().javaType));
+                    fprintf(out, "%s    }\n", indent.c_str());
 
-                // First check that the lengths of the uid and tag arrays are the same.
-                fprintf(out, "%s    if (%s.length != %s.length) {\n",
-                        indent.c_str(), uidName, tagName);
-                fprintf(out, "%s        return;\n", indent.c_str());
-                fprintf(out, "%s    }\n", indent.c_str());
-                fprintf(out, "%s    int attrSize = LIST_TYPE_OVERHEAD;\n", indent.c_str());
-                fprintf(out, "%s    for (int i = 0; i < %s.length; i++) {\n",
-                        indent.c_str(), tagName);
-                fprintf(out, "%s        String str%d = (%s[i] == null) ? \"\" : %s[i];\n",
-                        indent.c_str(), argIndex, tagName, tagName);
-                fprintf(out,
-                        "%s        int str%dlen = "
-                        "str%d.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;\n",
-                        indent.c_str(), argIndex, argIndex);
-                fprintf(out,
-                        "%s        attrSize += "
-                        "LIST_TYPE_OVERHEAD + INT_TYPE_SIZE + STRING_TYPE_OVERHEAD + str%dlen;\n",
-                        indent.c_str(), argIndex);
-                fprintf(out, "%s    }\n", indent.c_str());
-                fprintf(out, "%s    needed += attrSize;\n", indent.c_str());
-                break;
-            }
-            case JAVA_TYPE_KEY_VALUE_PAIR:
-            {
-                fprintf(out,
-                        "%s    // Calculate bytes needed by Key Value Pairs.\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s    final int count = valueMap.size();\n", indent.c_str());
-                fprintf(out,
-                        "%s    android.util.SparseIntArray intMap = null;\n", indent.c_str());
-                fprintf(out,
-                        "%s    android.util.SparseLongArray longMap = null;\n", indent.c_str());
-                fprintf(out,
-                        "%s    android.util.SparseArray<String> stringMap = null;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s    android.util.SparseArray<Float> floatMap = null;\n", indent.c_str());
-                fprintf(out, "%s    int keyValuePairSize = LIST_TYPE_OVERHEAD;\n", indent.c_str());
-                fprintf(out,
-                        "%s    for (int i = 0; i < count; i++) {\n", indent.c_str());
-                fprintf(out,
-                        "%s        final int key = valueMap.keyAt(i);\n", indent.c_str());
-                fprintf(out,
-                        "%s        final Object value = valueMap.valueAt(i);\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s        if (value instanceof Integer) {\n", indent.c_str());
-                fprintf(out,
-                        "%s            keyValuePairSize += LIST_TYPE_OVERHEAD\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s                    + INT_TYPE_SIZE + INT_TYPE_SIZE;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s            if (null == intMap) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                intMap = new android.util.SparseIntArray();\n", indent.c_str());
-                fprintf(out,
-                        "%s            }\n", indent.c_str());
-                fprintf(out,
-                        "%s            intMap.put(key, (Integer) value);\n", indent.c_str());
-                fprintf(out,
-                        "%s        } else if (value instanceof Long) {\n", indent.c_str());
-                fprintf(out,
-                        "%s            keyValuePairSize += LIST_TYPE_OVERHEAD\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s                    + INT_TYPE_SIZE + LONG_TYPE_SIZE;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s            if (null == longMap) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                longMap = new android.util.SparseLongArray();\n", indent.c_str());
-                fprintf(out,
-                        "%s            }\n", indent.c_str());
-                fprintf(out,
-                        "%s            longMap.put(key, (Long) value);\n", indent.c_str());
-                fprintf(out,
-                        "%s        } else if (value instanceof String) {\n", indent.c_str());
-                fprintf(out,
-                        "%s            final String str = (value == null) ? \"\" : "
-                        "(String) value;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s            final int len = "
-                        "str.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s            keyValuePairSize += LIST_TYPE_OVERHEAD + INT_TYPE_SIZE\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s                    + STRING_TYPE_OVERHEAD + len;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s            if (null == stringMap) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                stringMap = new android.util.SparseArray<>();\n", indent.c_str());
-                fprintf(out,
-                        "%s            }\n", indent.c_str());
-                fprintf(out,
-                        "%s            stringMap.put(key, str);\n", indent.c_str());
-                fprintf(out,
-                        "%s        } else if (value instanceof Float) {\n", indent.c_str());
-                fprintf(out,
-                        "%s            keyValuePairSize += LIST_TYPE_OVERHEAD\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s                    + INT_TYPE_SIZE + FLOAT_TYPE_SIZE;\n",
-                        indent.c_str());
-                fprintf(out,
-                        "%s            if (null == floatMap) {\n", indent.c_str());
-                fprintf(out,
-                        "%s                floatMap = new android.util.SparseArray<>();\n", indent.c_str());
-                fprintf(out,
-                        "%s            }\n", indent.c_str());
-                fprintf(out,
-                        "%s            floatMap.put(key, (Float) value);\n", indent.c_str());
-                fprintf(out,
-                        "%s        }\n", indent.c_str());
-                fprintf(out,
-                        "%s    }\n", indent.c_str());
-                fprintf(out, "%s    needed += keyValuePairSize;\n", indent.c_str());
-                break;
-            }
-            default:
-                // Unsupported types: OBJECT, DOUBLE.
-                fprintf(stderr, "Module logging does not yet support Object and Double.\n");
-                return 1;
+                    // First check that the lengths of the uid and tag arrays are the
+                    // same.
+                    fprintf(out, "%s    if (%s.length != %s.length) {\n", indent.c_str(), uidName,
+                            tagName);
+                    fprintf(out, "%s        return;\n", indent.c_str());
+                    fprintf(out, "%s    }\n", indent.c_str());
+                    fprintf(out, "%s    int attrSize = LIST_TYPE_OVERHEAD;\n", indent.c_str());
+                    fprintf(out, "%s    for (int i = 0; i < %s.length; i++) {\n", indent.c_str(),
+                            tagName);
+                    fprintf(out, "%s        String str%d = (%s[i] == null) ? \"\" : %s[i];\n",
+                            indent.c_str(), argIndex, tagName, tagName);
+                    fprintf(out,
+                            "%s        int str%dlen = "
+                            "str%d.getBytes(java.nio.charset.StandardCharsets.UTF_8)."
+                            "length;\n",
+                            indent.c_str(), argIndex, argIndex);
+                    fprintf(out,
+                            "%s        attrSize += "
+                            "LIST_TYPE_OVERHEAD + INT_TYPE_SIZE + STRING_TYPE_OVERHEAD + "
+                            "str%dlen;\n",
+                            indent.c_str(), argIndex);
+                    fprintf(out, "%s    }\n", indent.c_str());
+                    fprintf(out, "%s    needed += attrSize;\n", indent.c_str());
+                    break;
+                }
+                case JAVA_TYPE_KEY_VALUE_PAIR: {
+                    fprintf(out, "%s    // Calculate bytes needed by Key Value Pairs.\n",
+                            indent.c_str());
+                    fprintf(out, "%s    final int count = valueMap.size();\n", indent.c_str());
+                    fprintf(out, "%s    android.util.SparseIntArray intMap = null;\n",
+                            indent.c_str());
+                    fprintf(out, "%s    android.util.SparseLongArray longMap = null;\n",
+                            indent.c_str());
+                    fprintf(out, "%s    android.util.SparseArray<String> stringMap = null;\n",
+                            indent.c_str());
+                    fprintf(out, "%s    android.util.SparseArray<Float> floatMap = null;\n",
+                            indent.c_str());
+                    fprintf(out, "%s    int keyValuePairSize = LIST_TYPE_OVERHEAD;\n",
+                            indent.c_str());
+                    fprintf(out, "%s    for (int i = 0; i < count; i++) {\n", indent.c_str());
+                    fprintf(out, "%s        final int key = valueMap.keyAt(i);\n", indent.c_str());
+                    fprintf(out, "%s        final Object value = valueMap.valueAt(i);\n",
+                            indent.c_str());
+                    fprintf(out, "%s        if (value instanceof Integer) {\n", indent.c_str());
+                    fprintf(out, "%s            keyValuePairSize += LIST_TYPE_OVERHEAD\n",
+                            indent.c_str());
+                    fprintf(out, "%s                    + INT_TYPE_SIZE + INT_TYPE_SIZE;\n",
+                            indent.c_str());
+                    fprintf(out, "%s            if (null == intMap) {\n", indent.c_str());
+                    fprintf(out, "%s                intMap = new android.util.SparseIntArray();\n",
+                            indent.c_str());
+                    fprintf(out, "%s            }\n", indent.c_str());
+                    fprintf(out, "%s            intMap.put(key, (Integer) value);\n",
+                            indent.c_str());
+                    fprintf(out, "%s        } else if (value instanceof Long) {\n", indent.c_str());
+                    fprintf(out, "%s            keyValuePairSize += LIST_TYPE_OVERHEAD\n",
+                            indent.c_str());
+                    fprintf(out, "%s                    + INT_TYPE_SIZE + LONG_TYPE_SIZE;\n",
+                            indent.c_str());
+                    fprintf(out, "%s            if (null == longMap) {\n", indent.c_str());
+                    fprintf(out,
+                            "%s                longMap = new "
+                            "android.util.SparseLongArray();\n",
+                            indent.c_str());
+                    fprintf(out, "%s            }\n", indent.c_str());
+                    fprintf(out, "%s            longMap.put(key, (Long) value);\n", indent.c_str());
+                    fprintf(out, "%s        } else if (value instanceof String) {\n",
+                            indent.c_str());
+                    fprintf(out,
+                            "%s            final String str = (value == null) ? \"\" : "
+                            "(String) value;\n",
+                            indent.c_str());
+                    fprintf(out,
+                            "%s            final int len = "
+                            "str.getBytes(java.nio.charset.StandardCharsets.UTF_8).length;\n",
+                            indent.c_str());
+                    fprintf(out,
+                            "%s            keyValuePairSize += LIST_TYPE_OVERHEAD + "
+                            "INT_TYPE_SIZE\n",
+                            indent.c_str());
+                    fprintf(out, "%s                    + STRING_TYPE_OVERHEAD + len;\n",
+                            indent.c_str());
+                    fprintf(out, "%s            if (null == stringMap) {\n", indent.c_str());
+                    fprintf(out,
+                            "%s                stringMap = new "
+                            "android.util.SparseArray<>();\n",
+                            indent.c_str());
+                    fprintf(out, "%s            }\n", indent.c_str());
+                    fprintf(out, "%s            stringMap.put(key, str);\n", indent.c_str());
+                    fprintf(out, "%s        } else if (value instanceof Float) {\n",
+                            indent.c_str());
+                    fprintf(out, "%s            keyValuePairSize += LIST_TYPE_OVERHEAD\n",
+                            indent.c_str());
+                    fprintf(out, "%s                    + INT_TYPE_SIZE + FLOAT_TYPE_SIZE;\n",
+                            indent.c_str());
+                    fprintf(out, "%s            if (null == floatMap) {\n", indent.c_str());
+                    fprintf(out,
+                            "%s                floatMap = new "
+                            "android.util.SparseArray<>();\n",
+                            indent.c_str());
+                    fprintf(out, "%s            }\n", indent.c_str());
+                    fprintf(out, "%s            floatMap.put(key, (Float) value);\n",
+                            indent.c_str());
+                    fprintf(out, "%s        }\n", indent.c_str());
+                    fprintf(out, "%s    }\n", indent.c_str());
+                    fprintf(out, "%s    needed += keyValuePairSize;\n", indent.c_str());
+                    break;
+                }
+                default:
+                    // Unsupported types: OBJECT, DOUBLE.
+                    fprintf(stderr, "Module logging does not yet support Object and Double.\n");
+                    return 1;
             }
             argIndex++;
         }
 
-        // Now we have the size that is needed. Check for overflow and return if needed.
+        // Now we have the size that is needed. Check for overflow and return if
+        // needed.
         fprintf(out, "%s    if (needed > MAX_EVENT_PAYLOAD) {\n", indent.c_str());
         fprintf(out, "%s        return;\n", indent.c_str());
         fprintf(out, "%s    }\n", indent.c_str());
@@ -279,7 +265,8 @@
         fprintf(out, "%s    pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
 
         // Write timestamp.
-        fprintf(out, "%s    long elapsedRealtime = SystemClock.elapsedRealtimeNanos();\n", indent.c_str());
+        fprintf(out, "%s    long elapsedRealtime = SystemClock.elapsedRealtimeNanos();\n",
+                indent.c_str());
         fprintf(out, "%s    buff[pos] = LONG_TYPE;\n", indent.c_str());
         fprintf(out, "%s    copyLong(buff, pos + 1, elapsedRealtime);\n", indent.c_str());
         fprintf(out, "%s    pos += LONG_TYPE_SIZE;\n", indent.c_str());
@@ -291,77 +278,82 @@
 
         // Write the args.
         argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             switch (*arg) {
-            case JAVA_TYPE_BOOLEAN:
-                fprintf(out, "%s    buff[pos] = INT_TYPE;\n", indent.c_str());
-                fprintf(out, "%s    copyInt(buff, pos + 1, arg%d? 1 : 0);\n",
-                        indent.c_str(), argIndex);
-                fprintf(out, "%s    pos += INT_TYPE_SIZE;\n", indent.c_str());
-                break;
-            case JAVA_TYPE_INT:
-            case JAVA_TYPE_ENUM:
-                fprintf(out, "%s    buff[pos] = INT_TYPE;\n", indent.c_str());
-                fprintf(out, "%s    copyInt(buff, pos + 1, arg%d);\n", indent.c_str(), argIndex);
-                fprintf(out, "%s    pos += INT_TYPE_SIZE;\n", indent.c_str());
-                break;
-            case JAVA_TYPE_FLOAT:
-                requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT;
-                fprintf(out, "%s    buff[pos] = FLOAT_TYPE;\n", indent.c_str());
-                fprintf(out, "%s    copyFloat(buff, pos + 1, arg%d);\n", indent.c_str(), argIndex);
-                fprintf(out, "%s    pos += FLOAT_TYPE_SIZE;\n", indent.c_str());
-                break;
-            case JAVA_TYPE_LONG:
-                fprintf(out, "%s    buff[pos] = LONG_TYPE;\n", indent.c_str());
-                fprintf(out, "%s    copyLong(buff, pos + 1, arg%d);\n", indent.c_str(), argIndex);
-                fprintf(out, "%s    pos += LONG_TYPE_SIZE;\n", indent.c_str());
-                break;
-            case JAVA_TYPE_STRING:
-                fprintf(out, "%s    buff[pos] = STRING_TYPE;\n", indent.c_str());
-                fprintf(out, "%s    copyInt(buff, pos + 1, arg%dBytes.length);\n",
-                        indent.c_str(), argIndex);
-                fprintf(out, "%s    System.arraycopy("
-                        "arg%dBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, arg%dBytes.length);\n",
-                        indent.c_str(), argIndex, argIndex);
-                fprintf(out, "%s    pos += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n",
-                        indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_BYTE_ARRAY:
-                fprintf(out, "%s    buff[pos] = STRING_TYPE;\n", indent.c_str());
-                fprintf(out, "%s    copyInt(buff, pos + 1, arg%d.length);\n",
-                        indent.c_str(), argIndex);
-                fprintf(out, "%s    System.arraycopy("
-                        "arg%d, 0, buff, pos + STRING_TYPE_OVERHEAD, arg%d.length);\n",
-                        indent.c_str(), argIndex, argIndex);
-                fprintf(out, "%s    pos += STRING_TYPE_OVERHEAD + arg%d.length;\n",
-                        indent.c_str(), argIndex);
-                break;
-            case JAVA_TYPE_ATTRIBUTION_CHAIN:
-            {
-                requiredHelpers |= JAVA_MODULE_REQUIRES_ATTRIBUTION;
-                const char* uidName = attributionDecl.fields.front().name.c_str();
-                const char* tagName = attributionDecl.fields.back().name.c_str();
+                case JAVA_TYPE_BOOLEAN:
+                    fprintf(out, "%s    buff[pos] = INT_TYPE;\n", indent.c_str());
+                    fprintf(out, "%s    copyInt(buff, pos + 1, arg%d? 1 : 0);\n", indent.c_str(),
+                            argIndex);
+                    fprintf(out, "%s    pos += INT_TYPE_SIZE;\n", indent.c_str());
+                    break;
+                case JAVA_TYPE_INT:
+                case JAVA_TYPE_ENUM:
+                    fprintf(out, "%s    buff[pos] = INT_TYPE;\n", indent.c_str());
+                    fprintf(out, "%s    copyInt(buff, pos + 1, arg%d);\n", indent.c_str(),
+                            argIndex);
+                    fprintf(out, "%s    pos += INT_TYPE_SIZE;\n", indent.c_str());
+                    break;
+                case JAVA_TYPE_FLOAT:
+                    requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT;
+                    fprintf(out, "%s    buff[pos] = FLOAT_TYPE;\n", indent.c_str());
+                    fprintf(out, "%s    copyFloat(buff, pos + 1, arg%d);\n", indent.c_str(),
+                            argIndex);
+                    fprintf(out, "%s    pos += FLOAT_TYPE_SIZE;\n", indent.c_str());
+                    break;
+                case JAVA_TYPE_LONG:
+                    fprintf(out, "%s    buff[pos] = LONG_TYPE;\n", indent.c_str());
+                    fprintf(out, "%s    copyLong(buff, pos + 1, arg%d);\n", indent.c_str(),
+                            argIndex);
+                    fprintf(out, "%s    pos += LONG_TYPE_SIZE;\n", indent.c_str());
+                    break;
+                case JAVA_TYPE_STRING:
+                    fprintf(out, "%s    buff[pos] = STRING_TYPE;\n", indent.c_str());
+                    fprintf(out, "%s    copyInt(buff, pos + 1, arg%dBytes.length);\n",
+                            indent.c_str(), argIndex);
+                    fprintf(out,
+                            "%s    System.arraycopy("
+                            "arg%dBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, "
+                            "arg%dBytes.length);\n",
+                            indent.c_str(), argIndex, argIndex);
+                    fprintf(out, "%s    pos += STRING_TYPE_OVERHEAD + arg%dBytes.length;\n",
+                            indent.c_str(), argIndex);
+                    break;
+                case JAVA_TYPE_BYTE_ARRAY:
+                    fprintf(out, "%s    buff[pos] = STRING_TYPE;\n", indent.c_str());
+                    fprintf(out, "%s    copyInt(buff, pos + 1, arg%d.length);\n", indent.c_str(),
+                            argIndex);
+                    fprintf(out,
+                            "%s    System.arraycopy("
+                            "arg%d, 0, buff, pos + STRING_TYPE_OVERHEAD, arg%d.length);\n",
+                            indent.c_str(), argIndex, argIndex);
+                    fprintf(out, "%s    pos += STRING_TYPE_OVERHEAD + arg%d.length;\n",
+                            indent.c_str(), argIndex);
+                    break;
+                case JAVA_TYPE_ATTRIBUTION_CHAIN: {
+                    requiredHelpers |= JAVA_MODULE_REQUIRES_ATTRIBUTION;
+                    const char* uidName = attributionDecl.fields.front().name.c_str();
+                    const char* tagName = attributionDecl.fields.back().name.c_str();
 
-                fprintf(out, "%s    writeAttributionChain(buff, pos, %s, %s);\n", indent.c_str(),
-                        uidName, tagName);
-                fprintf(out, "%s    pos += attrSize;\n", indent.c_str());
-                break;
-            }
-            case JAVA_TYPE_KEY_VALUE_PAIR:
-                requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT;
-                requiredHelpers |= JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS;
-                fprintf(out,
-                        "%s    writeKeyValuePairs(buff, pos, (byte) count, intMap, longMap, "
-                        "stringMap, floatMap);\n",
-                        indent.c_str());
-                fprintf(out, "%s    pos += keyValuePairSize;\n", indent.c_str());
-                break;
-            default:
-                // Unsupported types: OBJECT, DOUBLE.
-                fprintf(stderr,
-                        "Object and Double are not supported in module logging");
-                return 1;
+                    fprintf(out, "%s    writeAttributionChain(buff, pos, %s, %s);\n",
+                            indent.c_str(), uidName, tagName);
+                    fprintf(out, "%s    pos += attrSize;\n", indent.c_str());
+                    break;
+                }
+                case JAVA_TYPE_KEY_VALUE_PAIR:
+                    requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT;
+                    requiredHelpers |= JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS;
+                    fprintf(out,
+                            "%s    writeKeyValuePairs(buff, pos, (byte) count, intMap, "
+                            "longMap, "
+                            "stringMap, floatMap);\n",
+                            indent.c_str());
+                    fprintf(out, "%s    pos += keyValuePairSize;\n", indent.c_str());
+                    break;
+                default:
+                    // Unsupported types: OBJECT, DOUBLE.
+                    fprintf(stderr, "Object and Double are not supported in module logging");
+                    return 1;
             }
             argIndex++;
         }
@@ -376,11 +368,8 @@
     return 0;
 }
 
-void write_java_helpers_for_q_schema_methods(
-        FILE* out,
-        const AtomDecl &attributionDecl,
-        const int requiredHelpers,
-        const string& indent) {
+void write_java_helpers_for_q_schema_methods(FILE* out, const AtomDecl& attributionDecl,
+                                             const int requiredHelpers, const string& indent) {
     fprintf(out, "\n");
     fprintf(out, "%s// Helper methods for copying primitives\n", indent.c_str());
     fprintf(out, "%sprivate static void copyInt(byte[] buff, int pos, int val) {\n",
@@ -420,8 +409,7 @@
         fprintf(out, "%sprivate static void writeAttributionChain(byte[] buff, int pos",
                 indent.c_str());
         for (auto chainField : attributionDecl.fields) {
-            fprintf(out, ", %s[] %s",
-                java_type_name(chainField.javaType), chainField.name.c_str());
+            fprintf(out, ", %s[] %s", java_type_name(chainField.javaType), chainField.name.c_str());
         }
         fprintf(out, ") {\n");
 
@@ -437,8 +425,8 @@
         fprintf(out, "%s    for (int i = 0; i < %s.length; i++) {\n", indent.c_str(), tagName);
         // Write the list begin.
         fprintf(out, "%s        buff[pos] = LIST_TYPE;\n", indent.c_str());
-        fprintf(out, "%s        buff[pos + 1] = %lu;\n",
-                indent.c_str(), attributionDecl.fields.size());
+        fprintf(out, "%s        buff[pos + 1] = %lu;\n", indent.c_str(),
+                attributionDecl.fields.size());
         fprintf(out, "%s        pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
 
         // Write the uid.
@@ -447,18 +435,20 @@
         fprintf(out, "%s        pos += INT_TYPE_SIZE;\n", indent.c_str());
 
         // Write the tag.
-        fprintf(out, "%s        String %sStr = (%s[i] == null) ? \"\" : %s[i];\n",
-                indent.c_str(), tagName, tagName, tagName);
-        fprintf(out, "%s        byte[] %sByte = "
+        fprintf(out, "%s        String %sStr = (%s[i] == null) ? \"\" : %s[i];\n", indent.c_str(),
+                tagName, tagName, tagName);
+        fprintf(out,
+                "%s        byte[] %sByte = "
                 "%sStr.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n",
                 indent.c_str(), tagName, tagName);
         fprintf(out, "%s        buff[pos] = STRING_TYPE;\n", indent.c_str());
         fprintf(out, "%s        copyInt(buff, pos + 1, %sByte.length);\n", indent.c_str(), tagName);
-        fprintf(out, "%s        System.arraycopy("
+        fprintf(out,
+                "%s        System.arraycopy("
                 "%sByte, 0, buff, pos + STRING_TYPE_OVERHEAD, %sByte.length);\n",
                 indent.c_str(), tagName, tagName);
-        fprintf(out, "%s        pos += STRING_TYPE_OVERHEAD + %sByte.length;\n",
-                indent.c_str(), tagName);
+        fprintf(out, "%s        pos += STRING_TYPE_OVERHEAD + %sByte.length;\n", indent.c_str(),
+                tagName);
         fprintf(out, "%s    }\n", indent.c_str());
         fprintf(out, "%s}\n", indent.c_str());
         fprintf(out, "\n");
@@ -466,7 +456,8 @@
 
     if (requiredHelpers & JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS) {
         fprintf(out,
-                "%sprivate static void writeKeyValuePairs(byte[] buff, int pos, byte numPairs,\n",
+                "%sprivate static void writeKeyValuePairs(byte[] buff, int pos, "
+                "byte numPairs,\n",
                 indent.c_str());
         fprintf(out, "%s        final android.util.SparseIntArray intMap,\n", indent.c_str());
         fprintf(out, "%s        final android.util.SparseLongArray longMap,\n", indent.c_str());
@@ -515,7 +506,9 @@
         fprintf(out, "%s    }\n", indent.c_str());
 
         // Write Strings.
-        fprintf(out, "%s    final int stringMapSize = null == stringMap ? 0 : stringMap.size();\n",
+        fprintf(out,
+                "%s    final int stringMapSize = null == stringMap ? 0 : "
+                "stringMap.size();\n",
                 indent.c_str());
         fprintf(out, "%s    for (int i = 0; i < stringMapSize; i++) {\n", indent.c_str());
         fprintf(out, "%s        buff[pos] = LIST_TYPE;\n", indent.c_str());
@@ -523,7 +516,8 @@
         fprintf(out, "%s        pos += LIST_TYPE_OVERHEAD;\n", indent.c_str());
         fprintf(out, "%s        final int key = stringMap.keyAt(i);\n", indent.c_str());
         fprintf(out, "%s        final String value = stringMap.valueAt(i);\n", indent.c_str());
-        fprintf(out, "%s        final byte[] valueBytes = "
+        fprintf(out,
+                "%s        final byte[] valueBytes = "
                 "value.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n",
                 indent.c_str());
         fprintf(out, "%s        buff[pos] = INT_TYPE;\n", indent.c_str());
@@ -531,15 +525,19 @@
         fprintf(out, "%s        pos += INT_TYPE_SIZE;\n", indent.c_str());
         fprintf(out, "%s        buff[pos] = STRING_TYPE;\n", indent.c_str());
         fprintf(out, "%s        copyInt(buff, pos + 1, valueBytes.length);\n", indent.c_str());
-        fprintf(out, "%s        System.arraycopy("
-                "valueBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, valueBytes.length);\n",
+        fprintf(out,
+                "%s        System.arraycopy("
+                "valueBytes, 0, buff, pos + STRING_TYPE_OVERHEAD, "
+                "valueBytes.length);\n",
                 indent.c_str());
         fprintf(out, "%s        pos += STRING_TYPE_OVERHEAD + valueBytes.length;\n",
                 indent.c_str());
         fprintf(out, "%s    }\n", indent.c_str());
 
         // Write floats.
-        fprintf(out, "%s    final int floatMapSize = null == floatMap ? 0 : floatMap.size();\n",
+        fprintf(out,
+                "%s    final int floatMapSize = null == floatMap ? 0 : "
+                "floatMap.size();\n",
                 indent.c_str());
         fprintf(out, "%s    for (int i = 0; i < floatMapSize; i++) {\n", indent.c_str());
         fprintf(out, "%s        buff[pos] = LIST_TYPE;\n", indent.c_str());
@@ -559,12 +557,11 @@
     }
 }
 
-// This method is called in main.cpp to generate StatsLog for modules that's compatible with
-// Q at compile-time.
+// This method is called in main.cpp to generate StatsLog for modules that's
+// compatible with Q at compile-time.
 int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms,
-                                      const AtomDecl &attributionDecl,
-                                      const string& javaClass, const string& javaPackage,
-                                      const bool supportWorkSource) {
+                                      const AtomDecl& attributionDecl, const string& javaClass,
+                                      const string& javaPackage, const bool supportWorkSource) {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
@@ -590,8 +587,7 @@
     int errors = 0;
     // Print write methods
     fprintf(out, "    // Write methods\n");
-    errors += write_java_methods_q_schema(out, atoms.signatureInfoMap, attributionDecl,
-            "    ");
+    errors += write_java_methods_q_schema(out, atoms.signatureInfoMap, attributionDecl, "    ");
     errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap);
     if (supportWorkSource) {
         errors += write_java_work_source_methods(out, atoms.signatureInfoMap);
diff --git a/tools/stats_log_api_gen/java_writer_q.h b/tools/stats_log_api_gen/java_writer_q.h
index 0f33b6c..f1cfc44 100644
--- a/tools/stats_log_api_gen/java_writer_q.h
+++ b/tools/stats_log_api_gen/java_writer_q.h
@@ -16,14 +16,14 @@
 
 #pragma once
 
-#include "Collation.h"
+#include <stdio.h>
+#include <string.h>
 
 #include <map>
 #include <set>
 #include <vector>
 
-#include <stdio.h>
-#include <string.h>
+#include "Collation.h"
 
 namespace android {
 namespace stats_log_api_gen {
@@ -33,20 +33,15 @@
 void write_java_q_logging_constants(FILE* out, const string& indent);
 
 int write_java_methods_q_schema(
-        FILE* out,
-        const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
-        const AtomDecl &attributionDecl,
-        const string& indent);
+        FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
+        const AtomDecl& attributionDecl, const string& indent);
 
-void write_java_helpers_for_q_schema_methods(
-        FILE * out,
-        const AtomDecl &attributionDecl,
-        const int requiredHelpers,
-        const string& indent);
+void write_java_helpers_for_q_schema_methods(FILE* out, const AtomDecl& attributionDecl,
+                                             const int requiredHelpers, const string& indent);
 
 int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms,
-        const AtomDecl &attributionDecl, const string& javaClass,
-        const string& javaPackage, const bool supportWorkSource);
+                                      const AtomDecl& attributionDecl, const string& javaClass,
+                                      const string& javaPackage, const bool supportWorkSource);
 
 }  // namespace stats_log_api_gen
 }  // namespace android
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 4f791a3..fda5736 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -1,21 +1,20 @@
 
-#include "Collation.h"
-#include "atoms_info_writer.h"
-#include "java_writer.h"
-#include "java_writer_q.h"
-#include "native_writer.h"
-#include "utils.h"
-
-#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
 #include <map>
 #include <set>
 #include <vector>
 
-#include <getopt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include "Collation.h"
+#include "atoms_info_writer.h"
+#include "frameworks/base/cmds/statsd/src/atoms.pb.h"
+#include "java_writer.h"
+#include "java_writer_q.h"
+#include "native_writer.h"
+#include "utils.h"
 
 using namespace google::protobuf;
 using namespace std;
@@ -25,25 +24,34 @@
 
 using android::os::statsd::Atom;
 
-static void
-print_usage()
-{
+static void print_usage() {
     fprintf(stderr, "usage: stats-log-api-gen OPTIONS\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "OPTIONS\n");
     fprintf(stderr, "  --cpp FILENAME       the header file to output for write helpers\n");
     fprintf(stderr, "  --header FILENAME    the cpp file to output for write helpers\n");
     fprintf(stderr,
-            "  --atomsInfoCpp FILENAME       the header file to output for statsd metadata\n");
-    fprintf(stderr, "  --atomsInfoHeader FILENAME    the cpp file to output for statsd metadata\n");
+            "  --atomsInfoCpp FILENAME       the header file to output for "
+            "statsd metadata\n");
+    fprintf(stderr,
+            "  --atomsInfoHeader FILENAME    the cpp file to output for statsd "
+            "metadata\n");
     fprintf(stderr, "  --help               this message\n");
     fprintf(stderr, "  --java FILENAME      the java file to output\n");
     fprintf(stderr, "  --module NAME        optional, module name to generate outputs for\n");
-    fprintf(stderr, "  --namespace COMMA,SEP,NAMESPACE   required for cpp/header with module\n");
-    fprintf(stderr, "                                    comma separated namespace of the files\n");
-    fprintf(stderr,"  --importHeader NAME  required for cpp/jni to say which header to import "
+    fprintf(stderr,
+            "  --namespace COMMA,SEP,NAMESPACE   required for cpp/header with "
+            "module\n");
+    fprintf(stderr,
+            "                                    comma separated namespace of "
+            "the files\n");
+    fprintf(stderr,
+            "  --importHeader NAME  required for cpp/jni to say which header to "
+            "import "
             "for write helpers\n");
-    fprintf(stderr,"  --atomsInfoImportHeader NAME  required for cpp to say which header to import "
+    fprintf(stderr,
+            "  --atomsInfoImportHeader NAME  required for cpp to say which "
+            "header to import "
             "for statsd metadata\n");
     fprintf(stderr, "  --javaPackage PACKAGE             the package for the java file.\n");
     fprintf(stderr, "                                    required for java with module\n");
@@ -51,17 +59,18 @@
     fprintf(stderr, "                       Optional for Java with module.\n");
     fprintf(stderr, "                       Default is \"StatsLogInternal\"\n");
     fprintf(stderr, "  --supportQ           Include runtime support for Android Q.\n");
-    fprintf(stderr, "  --worksource         Include support for logging WorkSource objects.\n");
-    fprintf(stderr, "  --compileQ           Include compile-time support for Android Q "
+    fprintf(stderr,
+            "  --worksource         Include support for logging WorkSource "
+            "objects.\n");
+    fprintf(stderr,
+            "  --compileQ           Include compile-time support for Android Q "
             "(Java only).\n");
 }
 
 /**
  * Do the argument parsing and execute the tasks.
  */
-static int
-run(int argc, char const*const* argv)
-{
+static int run(int argc, char const* const* argv) {
     string cppFilename;
     string headerFilename;
     string javaFilename;
@@ -171,11 +180,8 @@
         index++;
     }
 
-    if (cppFilename.size() == 0
-            && headerFilename.size() == 0
-            && javaFilename.size() == 0
-            && atomsInfoHeaderFilename.size() == 0
-            && atomsInfoCppFilename.size() == 0) {
+    if (cppFilename.size() == 0 && headerFilename.size() == 0 && javaFilename.size() == 0 &&
+        atomsInfoHeaderFilename.size() == 0 && atomsInfoCppFilename.size() == 0) {
         print_usage();
         return 1;
     }
@@ -201,8 +207,8 @@
 
     AtomDecl attributionDecl;
     vector<java_type_t> attributionSignature;
-    collate_atom(android::os::statsd::AttributionNode::descriptor(),
-                 &attributionDecl, &attributionSignature);
+    collate_atom(android::os::statsd::AttributionNode::descriptor(), &attributionDecl,
+                 &attributionSignature);
 
     // Write the atoms info .cpp file
     if (atomsInfoCppFilename.size() != 0) {
@@ -211,8 +217,8 @@
             fprintf(stderr, "Unable to open file for write: %s\n", atomsInfoCppFilename.c_str());
             return 1;
         }
-        errorCount = android::stats_log_api_gen::write_atoms_info_cpp(
-            out, atoms, cppNamespace, atomsInfoCppHeaderImport);
+        errorCount = android::stats_log_api_gen::write_atoms_info_cpp(out, atoms, cppNamespace,
+                                                                      atomsInfoCppHeaderImport);
         fclose(out);
     }
 
@@ -227,7 +233,6 @@
         fclose(out);
     }
 
-
     // Write the .cpp file
     if (cppFilename.size() != 0) {
         FILE* out = fopen(cppFilename.c_str(), "w");
@@ -240,13 +245,14 @@
             fprintf(stderr, "Must supply --namespace if supplying a specific module\n");
             return 1;
         }
-        // If this is for a specific module, the header file to import must also be provided.
+        // If this is for a specific module, the header file to import must also be
+        // provided.
         if (moduleName != DEFAULT_MODULE_NAME && cppHeaderImport == DEFAULT_CPP_HEADER_IMPORT) {
             fprintf(stderr, "Must supply --headerImport if supplying a specific module\n");
             return 1;
         }
         errorCount = android::stats_log_api_gen::write_stats_log_cpp(
-            out, atoms, attributionDecl, cppNamespace, cppHeaderImport, supportQ);
+                out, atoms, attributionDecl, cppNamespace, cppHeaderImport, supportQ);
         fclose(out);
     }
 
@@ -261,8 +267,8 @@
         if (moduleName != DEFAULT_MODULE_NAME && cppNamespace == DEFAULT_CPP_NAMESPACE) {
             fprintf(stderr, "Must supply --namespace if supplying a specific module\n");
         }
-        errorCount = android::stats_log_api_gen::write_stats_log_header(
-            out, atoms, attributionDecl, cppNamespace);
+        errorCount = android::stats_log_api_gen::write_stats_log_header(out, atoms, attributionDecl,
+                                                                        cppNamespace);
         fclose(out);
     }
 
@@ -291,8 +297,7 @@
 
         if (compileQ) {
             errorCount = android::stats_log_api_gen::write_stats_log_java_q_for_module(
-                    out, atoms, attributionDecl, javaClass, javaPackage,
-                    supportWorkSource);
+                    out, atoms, attributionDecl, javaClass, javaPackage, supportWorkSource);
         } else {
             errorCount = android::stats_log_api_gen::write_stats_log_java(
                     out, atoms, attributionDecl, javaClass, javaPackage, supportQ,
@@ -311,9 +316,7 @@
 /**
  * Main.
  */
-int
-main(int argc, char const*const* argv)
-{
+int main(int argc, char const* const* argv) {
     GOOGLE_PROTOBUF_VERIFY_VERSION;
 
     return android::stats_log_api_gen::run(argc, argv);
diff --git a/tools/stats_log_api_gen/native_writer.cpp b/tools/stats_log_api_gen/native_writer.cpp
index 90dcae4..0cf3225 100644
--- a/tools/stats_log_api_gen/native_writer.cpp
+++ b/tools/stats_log_api_gen/native_writer.cpp
@@ -15,45 +15,38 @@
  */
 
 #include "native_writer.h"
+
 #include "utils.h"
 
 namespace android {
 namespace stats_log_api_gen {
 
-static void write_annotations(
-        FILE* out, int argIndex,
-        const FieldNumberToAnnotations& fieldNumberToAnnotations,
-        const string& methodPrefix,
-        const string& methodSuffix) {
+static void write_annotations(FILE* out, int argIndex,
+                              const FieldNumberToAnnotations& fieldNumberToAnnotations,
+                              const string& methodPrefix, const string& methodSuffix) {
     auto fieldNumberToAnnotationsIt = fieldNumberToAnnotations.find(argIndex);
     if (fieldNumberToAnnotationsIt == fieldNumberToAnnotations.end()) {
         return;
     }
-    const set<shared_ptr<Annotation>>& annotations =
-            fieldNumberToAnnotationsIt->second;
+    const set<shared_ptr<Annotation>>& annotations = fieldNumberToAnnotationsIt->second;
     for (const shared_ptr<Annotation>& annotation : annotations) {
         // TODO(b/151744250): Group annotations for same atoms.
         // TODO(b/151786433): Write atom constant name instead of atom id literal.
         fprintf(out, "    if (code == %d) {\n", annotation->atomId);
-        switch(annotation->type) {
-            // TODO(b/151776731): Check for reset state annotation and only include reset state
-            // when field value == default state annotation value.
+        switch (annotation->type) {
+            // TODO(b/151776731): Check for reset state annotation and only include
+            // reset state when field value == default state annotation value.
             case ANNOTATION_TYPE_INT:
                 // TODO(b/151786433): Write annotation constant name instead of
                 // annotation id literal.
-                fprintf(out, "        %saddInt32Annotation(%s%d, %d);\n",
-                        methodPrefix.c_str(),
-                        methodSuffix.c_str(),
-                        annotation->annotationId,
-                        annotation->value.intValue);
+                fprintf(out, "        %saddInt32Annotation(%s%d, %d);\n", methodPrefix.c_str(),
+                        methodSuffix.c_str(), annotation->annotationId, annotation->value.intValue);
                 break;
             case ANNOTATION_TYPE_BOOL:
                 // TODO(b/151786433): Write annotation constant name instead of
                 // annotation id literal.
-                fprintf(out, "        %saddBoolAnnotation(%s%d, %s);\n",
-                        methodPrefix.c_str(),
-                        methodSuffix.c_str(),
-                        annotation->annotationId,
+                fprintf(out, "        %saddBoolAnnotation(%s%d, %s);\n", methodPrefix.c_str(),
+                        methodSuffix.c_str(), annotation->annotationId,
                         annotation->value.boolValue ? "true" : "false");
                 break;
             default:
@@ -61,29 +54,28 @@
         }
         fprintf(out, "    }\n");
     }
-
 }
 
 static int write_native_stats_write_methods(FILE* out, const Atoms& atoms,
-        const AtomDecl& attributionDecl, const bool supportQ) {
+                                            const AtomDecl& attributionDecl, const bool supportQ) {
     fprintf(out, "\n");
     for (auto signatureInfoMapIt = atoms.signatureInfoMap.begin();
-            signatureInfoMapIt != atoms.signatureInfoMap.end(); signatureInfoMapIt++) {
+         signatureInfoMapIt != atoms.signatureInfoMap.end(); signatureInfoMapIt++) {
         vector<java_type_t> signature = signatureInfoMapIt->first;
         const FieldNumberToAnnotations& fieldNumberToAnnotations = signatureInfoMapIt->second;
         // Key value pairs not supported in native.
         if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
             continue;
         }
-        write_native_method_signature(out, "int stats_write", signature,
-                attributionDecl, " {");
+        write_native_method_signature(out, "int stats_write", signature, attributionDecl, " {");
 
         int argIndex = 1;
         if (supportQ) {
             fprintf(out, "    StatsEventCompat event;\n");
             fprintf(out, "    event.setAtomId(code);\n");
+            write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAnnotations, "event.", "");
             for (vector<java_type_t>::const_iterator arg = signature.begin();
-                    arg != signature.end(); arg++) {
+                 arg != signature.end(); arg++) {
                 switch (*arg) {
                     case JAVA_TYPE_ATTRIBUTION_CHAIN: {
                         const char* uidName = attributionDecl.fields.front().name.c_str();
@@ -99,7 +91,7 @@
                     case JAVA_TYPE_BOOLEAN:
                         fprintf(out, "    event.writeBool(arg%d);\n", argIndex);
                         break;
-                    case JAVA_TYPE_INT: // Fall through.
+                    case JAVA_TYPE_INT:  // Fall through.
                     case JAVA_TYPE_ENUM:
                         fprintf(out, "    event.writeInt32(arg%d);\n", argIndex);
                         break;
@@ -124,8 +116,10 @@
         } else {
             fprintf(out, "    AStatsEvent* event = AStatsEvent_obtain();\n");
             fprintf(out, "    AStatsEvent_setAtomId(event, code);\n");
+            write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAnnotations, "AStatsEvent_",
+                              "event, ");
             for (vector<java_type_t>::const_iterator arg = signature.begin();
-                    arg != signature.end(); arg++) {
+                 arg != signature.end(); arg++) {
                 switch (*arg) {
                     case JAVA_TYPE_ATTRIBUTION_CHAIN: {
                         const char* uidName = attributionDecl.fields.front().name.c_str();
@@ -140,13 +134,14 @@
                     case JAVA_TYPE_BYTE_ARRAY:
                         fprintf(out,
                                 "    AStatsEvent_writeByteArray(event, "
-                                "reinterpret_cast<const uint8_t*>(arg%d.arg), arg%d.arg_length);\n",
+                                "reinterpret_cast<const uint8_t*>(arg%d.arg), "
+                                "arg%d.arg_length);\n",
                                 argIndex, argIndex);
                         break;
                     case JAVA_TYPE_BOOLEAN:
                         fprintf(out, "    AStatsEvent_writeBool(event, arg%d);\n", argIndex);
                         break;
-                    case JAVA_TYPE_INT: // Fall through.
+                    case JAVA_TYPE_INT:  // Fall through.
                     case JAVA_TYPE_ENUM:
                         fprintf(out, "    AStatsEvent_writeInt32(event, arg%d);\n", argIndex);
                         break;
@@ -165,7 +160,7 @@
                         return 1;
                 }
                 write_annotations(out, argIndex, fieldNumberToAnnotations, "AStatsEvent_",
-                        "event, ");
+                                  "event, ");
                 argIndex++;
             }
             fprintf(out, "    const int ret = AStatsEvent_write(event);\n");
@@ -178,10 +173,10 @@
 }
 
 static void write_native_stats_write_non_chained_methods(FILE* out, const Atoms& atoms,
-        const AtomDecl& attributionDecl) {
+                                                         const AtomDecl& attributionDecl) {
     fprintf(out, "\n");
     for (auto signature_it = atoms.nonChainedSignatureInfoMap.begin();
-            signature_it != atoms.nonChainedSignatureInfoMap.end(); signature_it++) {
+         signature_it != atoms.nonChainedSignatureInfoMap.end(); signature_it++) {
         vector<java_type_t> signature = signature_it->first;
         // Key value pairs not supported in native.
         if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
@@ -189,7 +184,7 @@
         }
 
         write_native_method_signature(out, "int stats_write_non_chained", signature,
-                attributionDecl, " {");
+                                      attributionDecl, " {");
 
         vector<java_type_t> newSignature;
 
@@ -212,17 +207,14 @@
 
         fprintf(out, "}\n\n");
     }
-
 }
 
 static void write_native_method_header(
-        FILE* out,
-        const string& methodName,
+        FILE* out, const string& methodName,
         const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap,
-        const AtomDecl &attributionDecl) {
-
+        const AtomDecl& attributionDecl) {
     for (auto signatureInfoMapIt = signatureInfoMap.begin();
-            signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
+         signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
         vector<java_type_t> signature = signatureInfoMapIt->first;
 
         // Key value pairs not supported in native.
@@ -233,9 +225,9 @@
     }
 }
 
-int write_stats_log_cpp(FILE *out, const Atoms &atoms, const AtomDecl &attributionDecl,
-                        const string& cppNamespace,
-                        const string& importHeader, const bool supportQ) {
+int write_stats_log_cpp(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
+                        const string& cppNamespace, const string& importHeader,
+                        const bool supportQ) {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
@@ -260,8 +252,8 @@
     return 0;
 }
 
-int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
-        const string& cppNamespace) {
+int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
+                           const string& cppNamespace) {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
@@ -286,21 +278,18 @@
     fprintf(out, "//\n");
     fprintf(out, "// Constants for enum values\n");
     fprintf(out, "//\n\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-        atom != atoms.decls.end(); atom++) {
-
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         for (vector<AtomField>::const_iterator field = atom->fields.begin();
-            field != atom->fields.end(); field++) {
+             field != atom->fields.end(); field++) {
             if (field->javaType == JAVA_TYPE_ENUM) {
-                fprintf(out, "// Values for %s.%s\n", atom->message.c_str(),
-                    field->name.c_str());
+                fprintf(out, "// Values for %s.%s\n", atom->message.c_str(), field->name.c_str());
                 for (map<int, string>::const_iterator value = field->enumValues.begin();
-                    value != field->enumValues.end(); value++) {
+                     value != field->enumValues.end(); value++) {
                     fprintf(out, "const int32_t %s__%s__%s = %d;\n",
-                        make_constant_name(atom->message).c_str(),
-                        make_constant_name(field->name).c_str(),
-                        make_constant_name(value->second).c_str(),
-                        value->first);
+                            make_constant_name(atom->message).c_str(),
+                            make_constant_name(field->name).c_str(),
+                            make_constant_name(value->second).c_str(), value->first);
                 }
                 fprintf(out, "\n");
             }
@@ -325,8 +314,8 @@
     fprintf(out, "//\n");
     fprintf(out, "// Write flattened methods\n");
     fprintf(out, "//\n");
-    write_native_method_header(out, "int stats_write_non_chained",
-            atoms.nonChainedSignatureInfoMap, attributionDecl);
+    write_native_method_header(out, "int stats_write_non_chained", atoms.nonChainedSignatureInfoMap,
+                               attributionDecl);
 
     fprintf(out, "\n");
     write_closing_namespace(out, cppNamespace);
diff --git a/tools/stats_log_api_gen/native_writer.h b/tools/stats_log_api_gen/native_writer.h
index 6e60325..264d4db 100644
--- a/tools/stats_log_api_gen/native_writer.h
+++ b/tools/stats_log_api_gen/native_writer.h
@@ -16,22 +16,22 @@
 
 #pragma once
 
-#include "Collation.h"
-
 #include <stdio.h>
 #include <string.h>
 
+#include "Collation.h"
+
 namespace android {
 namespace stats_log_api_gen {
 
 using namespace std;
 
-int write_stats_log_cpp(FILE *out, const Atoms &atoms, const AtomDecl &attributionDecl,
-        const string& cppNamespace, const string& importHeader,
-        const bool supportQ);
+int write_stats_log_cpp(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
+                        const string& cppNamespace, const string& importHeader,
+                        const bool supportQ);
 
-int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
-        const string& cppNamespace);
+int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
+                           const string& cppNamespace);
 
 }  // namespace stats_log_api_gen
 }  // namespace android
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index 5032ac0..9878926 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -15,12 +15,11 @@
  */
 
 #include <gtest/gtest.h>
-
-#include "frameworks/base/tools/stats_log_api_gen/test.pb.h"
-#include "Collation.h"
-
 #include <stdio.h>
 
+#include "Collation.h"
+#include "frameworks/base/tools/stats_log_api_gen/test.pb.h"
+
 namespace android {
 namespace stats_log_api_gen {
 
@@ -31,14 +30,13 @@
 /**
  * Return whether the map contains a vector of the elements provided.
  */
-static bool
-map_contains_vector(const map<vector<java_type_t>, FieldNumberToAnnotations>& s, int count, ...)
-{
+static bool map_contains_vector(const map<vector<java_type_t>, FieldNumberToAnnotations>& s,
+                                int count, ...) {
     va_list args;
     vector<java_type_t> v;
 
     va_start(args, count);
-    for (int i=0; i<count; i++) {
+    for (int i = 0; i < count; i++) {
         v.push_back((java_type_t)va_arg(args, int));
     }
     va_end(args);
@@ -49,34 +47,33 @@
 /**
  * Expect that the provided map contains the elements provided.
  */
-#define EXPECT_MAP_CONTAINS_SIGNATURE(s, ...) \
-    do { \
-        int count = sizeof((int[]){__VA_ARGS__})/sizeof(int); \
+#define EXPECT_MAP_CONTAINS_SIGNATURE(s, ...)                    \
+    do {                                                         \
+        int count = sizeof((int[]){__VA_ARGS__}) / sizeof(int);  \
         EXPECT_TRUE(map_contains_vector(s, count, __VA_ARGS__)); \
-    } while(0)
+    } while (0)
 
 /** Expects that the provided atom has no enum values for any field. */
-#define EXPECT_NO_ENUM_FIELD(atom) \
-    do { \
+#define EXPECT_NO_ENUM_FIELD(atom)                                           \
+    do {                                                                     \
         for (vector<AtomField>::const_iterator field = atom->fields.begin(); \
-             field != atom->fields.end(); field++) { \
-            EXPECT_TRUE(field->enumValues.empty()); \
-        } \
-    } while(0)
+             field != atom->fields.end(); field++) {                         \
+            EXPECT_TRUE(field->enumValues.empty());                          \
+        }                                                                    \
+    } while (0)
 
 /** Expects that exactly one specific field has expected enum values. */
-#define EXPECT_HAS_ENUM_FIELD(atom, field_name, values)        \
-    do { \
+#define EXPECT_HAS_ENUM_FIELD(atom, field_name, values)                      \
+    do {                                                                     \
         for (vector<AtomField>::const_iterator field = atom->fields.begin(); \
-             field != atom->fields.end(); field++) { \
-            if (field->name == field_name) { \
-                EXPECT_EQ(field->enumValues, values); \
-            } else { \
-                EXPECT_TRUE(field->enumValues.empty()); \
-            } \
-        } \
-    } while(0)
-
+             field != atom->fields.end(); field++) {                         \
+            if (field->name == field_name) {                                 \
+                EXPECT_EQ(field->enumValues, values);                        \
+            } else {                                                         \
+                EXPECT_TRUE(field->enumValues.empty());                      \
+            }                                                                \
+        }                                                                    \
+    } while (0)
 
 /**
  * Test a correct collation, with all the types.
@@ -95,23 +92,22 @@
     EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_INT, JAVA_TYPE_INT);
 
     // AllTypesAtom
-    EXPECT_MAP_CONTAINS_SIGNATURE(
-        atoms.signatureInfoMap,
-        JAVA_TYPE_ATTRIBUTION_CHAIN, // AttributionChain
-        JAVA_TYPE_FLOAT,             // float
-        JAVA_TYPE_LONG,              // int64
-        JAVA_TYPE_LONG,              // uint64
-        JAVA_TYPE_INT,               // int32
-        JAVA_TYPE_LONG,              // fixed64
-        JAVA_TYPE_INT,               // fixed32
-        JAVA_TYPE_BOOLEAN,           // bool
-        JAVA_TYPE_STRING,            // string
-        JAVA_TYPE_INT,               // uint32
-        JAVA_TYPE_INT,               // AnEnum
-        JAVA_TYPE_INT,               // sfixed32
-        JAVA_TYPE_LONG,              // sfixed64
-        JAVA_TYPE_INT,               // sint32
-        JAVA_TYPE_LONG               // sint64
+    EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap,
+                                  JAVA_TYPE_ATTRIBUTION_CHAIN,  // AttributionChain
+                                  JAVA_TYPE_FLOAT,              // float
+                                  JAVA_TYPE_LONG,               // int64
+                                  JAVA_TYPE_LONG,               // uint64
+                                  JAVA_TYPE_INT,                // int32
+                                  JAVA_TYPE_LONG,               // fixed64
+                                  JAVA_TYPE_INT,                // fixed32
+                                  JAVA_TYPE_BOOLEAN,            // bool
+                                  JAVA_TYPE_STRING,             // string
+                                  JAVA_TYPE_INT,                // uint32
+                                  JAVA_TYPE_INT,                // AnEnum
+                                  JAVA_TYPE_INT,                // sfixed32
+                                  JAVA_TYPE_LONG,               // sfixed64
+                                  JAVA_TYPE_INT,                // sint32
+                                  JAVA_TYPE_LONG                // sint64
     );
 
     set<AtomDecl>::const_iterator atom = atoms.decls.begin();
@@ -156,7 +152,8 @@
 }
 
 /**
- * Test that atoms that have non-primitive types or repeated fields are rejected.
+ * Test that atoms that have non-primitive types or repeated fields are
+ * rejected.
  */
 TEST(CollationTest, FailOnBadTypes) {
     Atoms atoms;
@@ -170,18 +167,20 @@
  */
 TEST(CollationTest, FailOnSkippedFieldsSingle) {
     Atoms atoms;
-    int errorCount = collate_atoms(BadSkippedFieldSingle::descriptor(), DEFAULT_MODULE_NAME, &atoms);
+    int errorCount =
+            collate_atoms(BadSkippedFieldSingle::descriptor(), DEFAULT_MODULE_NAME, &atoms);
 
     EXPECT_EQ(1, errorCount);
 }
 
 /**
- * Test that atoms that skip field numbers (not in the first position, and multiple
- * times) are rejected.
+ * Test that atoms that skip field numbers (not in the first position, and
+ * multiple times) are rejected.
  */
 TEST(CollationTest, FailOnSkippedFieldsMultiple) {
     Atoms atoms;
-    int errorCount = collate_atoms(BadSkippedFieldMultiple::descriptor(), DEFAULT_MODULE_NAME, &atoms);
+    int errorCount =
+            collate_atoms(BadSkippedFieldMultiple::descriptor(), DEFAULT_MODULE_NAME, &atoms);
 
     EXPECT_EQ(2, errorCount);
 }
@@ -191,11 +190,11 @@
  * rejected.
  */
 TEST(CollationTest, FailBadAttributionNodePosition) {
-  Atoms atoms;
-  int errorCount =
-      collate_atoms(BadAttributionNodePosition::descriptor(), DEFAULT_MODULE_NAME, &atoms);
+    Atoms atoms;
+    int errorCount =
+            collate_atoms(BadAttributionNodePosition::descriptor(), DEFAULT_MODULE_NAME, &atoms);
 
-  EXPECT_EQ(1, errorCount);
+    EXPECT_EQ(1, errorCount);
 }
 
 TEST(CollationTest, FailOnBadStateAtomOptions) {
@@ -270,8 +269,8 @@
             const set<shared_ptr<Annotation>>& annotations = fieldNumberToAnnotations.at(1);
             EXPECT_EQ(2u, annotations.size());
             for (const shared_ptr<Annotation> annotation : annotations) {
-                EXPECT_TRUE(annotation->annotationId == ANNOTATION_ID_IS_UID
-                        || annotation->annotationId == ANNOTATION_ID_STATE_OPTION);
+                EXPECT_TRUE(annotation->annotationId == ANNOTATION_ID_IS_UID ||
+                            annotation->annotationId == ANNOTATION_ID_STATE_OPTION);
                 if (ANNOTATION_ID_IS_UID == annotation->annotationId) {
                     EXPECT_EQ(1, annotation->atomId);
                     EXPECT_EQ(ANNOTATION_TYPE_BOOL, annotation->type);
@@ -303,12 +302,11 @@
         EXPECT_EQ(1u, fieldNumberToAnnotations.size());
         int fieldNumber = 1;
         EXPECT_NE(fieldNumberToAnnotations.end(), fieldNumberToAnnotations.find(fieldNumber));
-        const set<shared_ptr<Annotation>>& annotations =
-                fieldNumberToAnnotations.at(fieldNumber);
+        const set<shared_ptr<Annotation>>& annotations = fieldNumberToAnnotations.at(fieldNumber);
         EXPECT_EQ(2u, annotations.size());
         for (const shared_ptr<Annotation> annotation : annotations) {
-            EXPECT_TRUE(annotation->annotationId == ANNOTATION_ID_IS_UID
-                    || annotation->annotationId == ANNOTATION_ID_STATE_OPTION);
+            EXPECT_TRUE(annotation->annotationId == ANNOTATION_ID_IS_UID ||
+                        annotation->annotationId == ANNOTATION_ID_STATE_OPTION);
             if (ANNOTATION_ID_IS_UID == annotation->annotationId) {
                 EXPECT_EQ(1, annotation->atomId);
                 EXPECT_EQ(ANNOTATION_TYPE_BOOL, annotation->type);
diff --git a/tools/stats_log_api_gen/utils.cpp b/tools/stats_log_api_gen/utils.cpp
index 7314127..0262488 100644
--- a/tools/stats_log_api_gen/utils.cpp
+++ b/tools/stats_log_api_gen/utils.cpp
@@ -22,9 +22,9 @@
 namespace stats_log_api_gen {
 
 static void build_non_chained_decl_map(const Atoms& atoms,
-                                std::map<int, set<AtomDecl>::const_iterator>* decl_map) {
+                                       std::map<int, set<AtomDecl>::const_iterator>* decl_map) {
     for (set<AtomDecl>::const_iterator atom = atoms.non_chained_decls.begin();
-        atom != atoms.non_chained_decls.end(); atom++) {
+         atom != atoms.non_chained_decls.end(); atom++) {
         decl_map->insert(std::make_pair(atom->code, atom));
     }
 }
@@ -36,7 +36,7 @@
     string result;
     const int N = str.size();
     bool underscore_next = false;
-    for (int i=0; i<N; i++) {
+    for (int i = 0; i < N; i++) {
         char c = str[i];
         if (c >= 'A' && c <= 'Z') {
             if (underscore_next) {
@@ -99,7 +99,8 @@
 }
 
 // Native
-// Writes namespaces for the cpp and header files, returning the number of namespaces written.
+// Writes namespaces for the cpp and header files, returning the number of
+// namespaces written.
 void write_namespace(FILE* out, const string& cppNamespaces) {
     vector<string> cppNamespaceVec = android::base::Split(cppNamespaces, ",");
     for (string cppNamespace : cppNamespaceVec) {
@@ -115,35 +116,31 @@
     }
 }
 
-static void write_cpp_usage(
-    FILE* out, const string& method_name, const string& atom_code_name,
-    const AtomDecl& atom, const AtomDecl &attributionDecl) {
-    fprintf(out, "     * Usage: %s(StatsLog.%s", method_name.c_str(),
-            atom_code_name.c_str());
+static void write_cpp_usage(FILE* out, const string& method_name, const string& atom_code_name,
+                            const AtomDecl& atom, const AtomDecl& attributionDecl) {
+    fprintf(out, "     * Usage: %s(StatsLog.%s", method_name.c_str(), atom_code_name.c_str());
 
-    for (vector<AtomField>::const_iterator field = atom.fields.begin();
-            field != atom.fields.end(); field++) {
+    for (vector<AtomField>::const_iterator field = atom.fields.begin(); field != atom.fields.end();
+         field++) {
         if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
             for (auto chainField : attributionDecl.fields) {
                 if (chainField.javaType == JAVA_TYPE_STRING) {
-                    fprintf(out, ", const std::vector<%s>& %s",
-                         cpp_type_name(chainField.javaType),
-                         chainField.name.c_str());
+                    fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType),
+                            chainField.name.c_str());
                 } else {
                     fprintf(out, ", const %s* %s, size_t %s_length",
-                         cpp_type_name(chainField.javaType),
-                         chainField.name.c_str(), chainField.name.c_str());
+                            cpp_type_name(chainField.javaType), chainField.name.c_str(),
+                            chainField.name.c_str());
                 }
             }
         } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
-            fprintf(out, ", const std::map<int, int32_t>& %s_int"
-                         ", const std::map<int, int64_t>& %s_long"
-                         ", const std::map<int, char const*>& %s_str"
-                         ", const std::map<int, float>& %s_float",
-                         field->name.c_str(),
-                         field->name.c_str(),
-                         field->name.c_str(),
-                         field->name.c_str());
+            fprintf(out,
+                    ", const std::map<int, int32_t>& %s_int"
+                    ", const std::map<int, int64_t>& %s_long"
+                    ", const std::map<int, char const*>& %s_str"
+                    ", const std::map<int, float>& %s_float",
+                    field->name.c_str(), field->name.c_str(), field->name.c_str(),
+                    field->name.c_str());
         } else {
             fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
         }
@@ -162,8 +159,8 @@
 
     size_t i = 0;
     // Print atom constants
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-        atom != atoms.decls.end(); atom++) {
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         string constant = make_constant_name(atom->name);
         fprintf(out, "\n");
         fprintf(out, "    /**\n");
@@ -173,7 +170,7 @@
         auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atom->code);
         if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
             write_cpp_usage(out, "stats_write_non_chained", constant, *non_chained_decl->second,
-                attributionDecl);
+                            attributionDecl);
         }
         fprintf(out, "     */\n");
         char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
@@ -186,30 +183,30 @@
 }
 
 void write_native_method_signature(FILE* out, const string& methodName,
-        const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
-        const string& closer) {
+                                   const vector<java_type_t>& signature,
+                                   const AtomDecl& attributionDecl, const string& closer) {
     fprintf(out, "%s(int32_t code", methodName.c_str());
     int argIndex = 1;
-    for (vector<java_type_t>::const_iterator arg = signature.begin();
-        arg != signature.end(); arg++) {
+    for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+         arg++) {
         if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
             for (auto chainField : attributionDecl.fields) {
                 if (chainField.javaType == JAVA_TYPE_STRING) {
-                        fprintf(out, ", const std::vector<%s>& %s",
-                             cpp_type_name(chainField.javaType),
-                             chainField.name.c_str());
+                    fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType),
+                            chainField.name.c_str());
                 } else {
-                        fprintf(out, ", const %s* %s, size_t %s_length",
-                             cpp_type_name(chainField.javaType),
-                             chainField.name.c_str(), chainField.name.c_str());
+                    fprintf(out, ", const %s* %s, size_t %s_length",
+                            cpp_type_name(chainField.javaType), chainField.name.c_str(),
+                            chainField.name.c_str());
                 }
             }
         } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-            fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
-                         "const std::map<int, int64_t>& arg%d_2, "
-                         "const std::map<int, char const*>& arg%d_3, "
-                         "const std::map<int, float>& arg%d_4",
-                         argIndex, argIndex, argIndex, argIndex);
+            fprintf(out,
+                    ", const std::map<int, int32_t>& arg%d_1, "
+                    "const std::map<int, int64_t>& arg%d_2, "
+                    "const std::map<int, char const*>& arg%d_3, "
+                    "const std::map<int, float>& arg%d_4",
+                    argIndex, argIndex, argIndex, argIndex);
         } else {
             fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
         }
@@ -219,27 +216,27 @@
 }
 
 void write_native_method_call(FILE* out, const string& methodName,
-        const vector<java_type_t>& signature, const AtomDecl& attributionDecl, int argIndex) {
+                              const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
+                              int argIndex) {
     fprintf(out, "%s(code", methodName.c_str());
-    for (vector<java_type_t>::const_iterator arg = signature.begin();
-       arg != signature.end(); arg++) {
-       if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-           for (auto chainField : attributionDecl.fields) {
-               if (chainField.javaType == JAVA_TYPE_STRING) {
-                       fprintf(out, ", %s",
+    for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+         arg++) {
+        if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+            for (auto chainField : attributionDecl.fields) {
+                if (chainField.javaType == JAVA_TYPE_STRING) {
+                    fprintf(out, ", %s", chainField.name.c_str());
+                } else {
+                    fprintf(out, ",  %s,  %s_length", chainField.name.c_str(),
                             chainField.name.c_str());
-               } else {
-                       fprintf(out, ",  %s,  %s_length",
-                            chainField.name.c_str(), chainField.name.c_str());
-               }
-           }
-       } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-           fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex,
-                   argIndex, argIndex, argIndex);
-       } else {
-           fprintf(out, ", arg%d", argIndex);
-       }
-       argIndex++;
+                }
+            }
+        } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+            fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex, argIndex, argIndex,
+                    argIndex);
+        } else {
+            fprintf(out, ", arg%d", argIndex);
+        }
+        argIndex++;
     }
     fprintf(out, ");\n");
 }
@@ -252,8 +249,8 @@
     build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
 
     // Print constants for the atom codes.
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-            atom != atoms.decls.end(); atom++) {
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         string constant = make_constant_name(atom->name);
         fprintf(out, "\n");
         fprintf(out, "    /**\n");
@@ -271,20 +268,19 @@
 
 void write_java_enum_values(FILE* out, const Atoms& atoms) {
     fprintf(out, "    // Constants for enum values.\n\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-        atom != atoms.decls.end(); atom++) {
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin(); atom != atoms.decls.end();
+         atom++) {
         for (vector<AtomField>::const_iterator field = atom->fields.begin();
-            field != atom->fields.end(); field++) {
+             field != atom->fields.end(); field++) {
             if (field->javaType == JAVA_TYPE_ENUM) {
                 fprintf(out, "    // Values for %s.%s\n", atom->message.c_str(),
-                    field->name.c_str());
+                        field->name.c_str());
                 for (map<int, string>::const_iterator value = field->enumValues.begin();
-                    value != field->enumValues.end(); value++) {
+                     value != field->enumValues.end(); value++) {
                     fprintf(out, "    public static final int %s__%s__%s = %d;\n",
-                        make_constant_name(atom->message).c_str(),
-                        make_constant_name(field->name).c_str(),
-                        make_constant_name(value->second).c_str(),
-                        value->first);
+                            make_constant_name(atom->message).c_str(),
+                            make_constant_name(field->name).c_str(),
+                            make_constant_name(value->second).c_str(), value->first);
                 }
                 fprintf(out, "\n");
             }
@@ -293,11 +289,11 @@
 }
 
 void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name,
-        const AtomDecl& atom) {
-    fprintf(out, "     * Usage: StatsLog.%s(StatsLog.%s",
-        method_name.c_str(), atom_code_name.c_str());
-    for (vector<AtomField>::const_iterator field = atom.fields.begin();
-        field != atom.fields.end(); field++) {
+                      const AtomDecl& atom) {
+    fprintf(out, "     * Usage: StatsLog.%s(StatsLog.%s", method_name.c_str(),
+            atom_code_name.c_str());
+    for (vector<AtomField>::const_iterator field = atom.fields.begin(); field != atom.fields.end();
+         field++) {
         if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
             fprintf(out, ", android.os.WorkSource workSource");
         } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
@@ -312,16 +308,15 @@
 }
 
 int write_java_non_chained_methods(
-        FILE* out,
-        const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap) {
+        FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap) {
     for (auto signatureInfoMapIt = signatureInfoMap.begin();
-            signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
+         signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
         // Print method signature.
         fprintf(out, "    public static void write_non_chained(int code");
         vector<java_type_t> signature = signatureInfoMapIt->first;
         int argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
                 fprintf(stderr, "Non chained signatures should not have attribution chains.\n");
                 return 1;
@@ -337,8 +332,8 @@
 
         fprintf(out, "        write(code");
         argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             // First two args are uid and tag of attribution chain.
             if (argIndex == 1) {
                 fprintf(out, ", new int[] {arg%d}", argIndex);
@@ -357,23 +352,24 @@
 }
 
 int write_java_work_source_methods(
-        FILE* out,
-        const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap) {
+        FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap) {
     fprintf(out, "    // WorkSource methods.\n");
     for (auto signatureInfoMapIt = signatureInfoMap.begin();
-            signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
+         signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
         vector<java_type_t> signature = signatureInfoMapIt->first;
         // Determine if there is Attribution in this signature.
         int attributionArg = -1;
         int argIndexMax = 0;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             argIndexMax++;
             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
                 if (attributionArg > -1) {
                     fprintf(stderr, "An atom contains multiple AttributionNode fields.\n");
                     fprintf(stderr, "This is not supported. Aborting WorkSource method writing.\n");
-                    fprintf(out, "\n// Invalid for WorkSource: more than one attribution chain.\n");
+                    fprintf(out,
+                            "\n// Invalid for WorkSource: more than one attribution "
+                            "chain.\n");
                     return 1;
                 }
                 attributionArg = argIndexMax;
@@ -387,8 +383,8 @@
         // Method header (signature)
         fprintf(out, "    public static void write(int code");
         int argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
+        for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+             arg++) {
             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
                 fprintf(out, ", android.os.WorkSource ws");
             } else {
@@ -398,36 +394,40 @@
         }
         fprintf(out, ") {\n");
 
-        // write_non_chained() component. TODO: Remove when flat uids are no longer needed.
+        // write_non_chained() component. TODO: Remove when flat uids are no longer
+        // needed.
         fprintf(out, "        for (int i = 0; i < ws.size(); ++i) {\n");
         fprintf(out, "            write_non_chained(code");
         for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
             if (argIndex == attributionArg) {
                 fprintf(out, ", ws.getUid(i), ws.getPackageName(i)");
             } else {
-               fprintf(out, ", arg%d", argIndex);
+                fprintf(out, ", arg%d", argIndex);
             }
         }
         fprintf(out, ");\n");
-        fprintf(out, "        }\n"); // close for-loop
+        fprintf(out, "        }\n");  // close for-loop
 
         // write() component.
-        fprintf(out, "        java.util.List<android.os.WorkSource.WorkChain> workChains = "
+        fprintf(out,
+                "        java.util.List<android.os.WorkSource.WorkChain> workChains = "
                 "ws.getWorkChains();\n");
         fprintf(out, "        if (workChains != null) {\n");
-        fprintf(out, "            for (android.os.WorkSource.WorkChain wc : workChains) {\n");
+        fprintf(out,
+                "            for (android.os.WorkSource.WorkChain wc : workChains) "
+                "{\n");
         fprintf(out, "                write(code");
         for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
             if (argIndex == attributionArg) {
                 fprintf(out, ", wc.getUids(), wc.getTags()");
             } else {
-               fprintf(out, ", arg%d", argIndex);
+                fprintf(out, ", arg%d", argIndex);
             }
         }
         fprintf(out, ");\n");
-        fprintf(out, "            }\n"); // close for-loop
-        fprintf(out, "        }\n"); // close if
-        fprintf(out, "    }\n"); // close method
+        fprintf(out, "            }\n");  // close for-loop
+        fprintf(out, "        }\n");      // close if
+        fprintf(out, "    }\n");          // close method
     }
     return 0;
 }
diff --git a/tools/stats_log_api_gen/utils.h b/tools/stats_log_api_gen/utils.h
index a6b3ef9..468f323 100644
--- a/tools/stats_log_api_gen/utils.h
+++ b/tools/stats_log_api_gen/utils.h
@@ -16,14 +16,14 @@
 
 #pragma once
 
-#include "Collation.h"
+#include <stdio.h>
+#include <string.h>
 
 #include <map>
 #include <set>
 #include <vector>
 
-#include <stdio.h>
-#include <string.h>
+#include "Collation.h"
 
 namespace android {
 namespace stats_log_api_gen {
@@ -52,11 +52,12 @@
 void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl);
 
 void write_native_method_signature(FILE* out, const string& methodName,
-        const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
-        const string& closer);
+                                   const vector<java_type_t>& signature,
+                                   const AtomDecl& attributionDecl, const string& closer);
 
 void write_native_method_call(FILE* out, const string& methodName,
-        const vector<java_type_t>& signature, const AtomDecl& attributionDecl, int argIndex = 1);
+                              const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
+                              int argIndex = 1);
 
 // Common Java helpers.
 void write_java_atom_codes(FILE* out, const Atoms& atoms);
@@ -64,14 +65,13 @@
 void write_java_enum_values(FILE* out, const Atoms& atoms);
 
 void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name,
-        const AtomDecl& atom);
+                      const AtomDecl& atom);
 
-int write_java_non_chained_methods(FILE* out, const map<vector<java_type_t>,
-        FieldNumberToAnnotations>& signatureInfoMap);
+int write_java_non_chained_methods(
+        FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap);
 
 int write_java_work_source_methods(
-        FILE* out,
-        const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap);
+        FILE* out, const map<vector<java_type_t>, FieldNumberToAnnotations>& signatureInfoMap);
 
 }  // namespace stats_log_api_gen
 }  // namespace android
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 9d9a495..6147861 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -81,7 +81,6 @@
     libs: [
         "framework-annotations-lib",
         "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
-        "unsupportedappusage-annotation", // for dalvik.annotation.compat.UnsupportedAppUsage
         "framework-telephony-stubs",
     ],
     srcs: [
@@ -170,24 +169,23 @@
 java_library {
     name: "framework-wifi-stubs-publicapi",
     srcs: [":framework-wifi-stubs-srcs-publicapi"],
+    defaults: ["framework-module-stubs-lib-defaults-publicapi"],
+    // TODO(b/151134996): remove this
     sdk_version: "current",
-    installable: false,
 }
 
 java_library {
     name: "framework-wifi-stubs-systemapi",
     srcs: [":framework-wifi-stubs-srcs-systemapi"],
-    sdk_version: "system_current",
     libs: ["framework-annotations-lib"],
-    installable: false,
+    defaults: ["framework-module-stubs-lib-defaults-systemapi"],
 }
 
 java_library {
     name: "framework-wifi-stubs-module_libs_api",
     srcs: [":framework-wifi-stubs-srcs-module_libs_api"],
-    sdk_version: "module_current",
     libs: ["framework-annotations-lib"],
-    installable: false,
+    defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
 }
 
 // defaults for tests that need to build against framework-wifi's @hide APIs
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index a269e17..457e0db 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -516,9 +516,6 @@
     public WifiConfiguration toWifiConfiguration() {
         WifiConfiguration wifiConfig = new WifiConfiguration();
         wifiConfig.SSID = mSsid;
-        if (mBssid != null) {
-            wifiConfig.BSSID = mBssid.toString();
-        }
         wifiConfig.preSharedKey = mPassphrase;
         wifiConfig.hiddenSSID = mHiddenSsid;
         wifiConfig.apChannel = mChannel;
@@ -662,8 +659,6 @@
         /**
          * Specifies a BSSID for the AP.
          * <p>
-         * Only supported when configuring a local-only hotspot.
-         * <p>
          * <li>If not set, defaults to null.</li>
          * @param bssid BSSID, or null to have the BSSID chosen by the framework. The caller is
          *              responsible for avoiding collisions.
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index ba68d17..b110a61 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -2356,6 +2356,8 @@
         sbuf.append(" lcuid=" + lastConnectUid);
         sbuf.append(" allowAutojoin=" + allowAutojoin);
         sbuf.append(" noInternetAccessExpected=" + noInternetAccessExpected);
+        sbuf.append(" mostRecentlyConnected=" + isMostRecentlyConnected);
+
         sbuf.append(" ");
 
         if (this.lastConnected != 0) {
@@ -2964,4 +2966,11 @@
         return mPasspointUniqueId;
     }
 
+    /**
+     * If network is one of the most recently connected.
+     * For framework internal use only. Do not parcel.
+     * @hide
+     */
+    public boolean isMostRecentlyConnected = false;
+
 }