Merge "Defined new APIs to whitelist content capture for specific URLs."
diff --git a/Android.bp b/Android.bp
index a6745a9..8a3f76c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -318,6 +318,7 @@
         "core/java/android/service/vr/IVrListener.aidl",
         "core/java/android/service/vr/IVrManager.aidl",
         "core/java/android/service/vr/IVrStateCallbacks.aidl",
+        "core/java/android/service/watchdog/IExplicitHealthCheckService.aidl",
         "core/java/android/print/ILayoutResultCallback.aidl",
         "core/java/android/print/IPrinterDiscoveryObserver.aidl",
         "core/java/android/print/IPrintDocumentAdapter.aidl",
@@ -475,7 +476,10 @@
         "media/java/android/media/IMediaHTTPConnection.aidl",
         "media/java/android/media/IMediaHTTPService.aidl",
         "media/java/android/media/IMediaResourceMonitor.aidl",
+        "media/java/android/media/IMediaRoute2Callback.aidl",
+        "media/java/android/media/IMediaRoute2Provider.aidl",
         "media/java/android/media/IMediaRouterClient.aidl",
+        "media/java/android/media/IMediaRouter2ManagerClient.aidl",
         "media/java/android/media/IMediaRouterService.aidl",
         "media/java/android/media/IMediaScannerListener.aidl",
         "media/java/android/media/IMediaScannerService.aidl",
diff --git a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
index 0c30302..1f26188 100644
--- a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
@@ -47,7 +47,7 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         assertNotNull(mKernelCpuThreadReader);
         while (state.keepRunning()) {
-            this.mKernelCpuThreadReader.getCurrentProcessCpuUsage();
+            this.mKernelCpuThreadReader.getProcessCpuUsage();
         }
     }
 }
diff --git a/api/current.txt b/api/current.txt
index 9d59ab3..2a26368 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6963,7 +6963,7 @@
 
   public abstract static class DevicePolicyManager.InstallSystemUpdateCallback {
     ctor public DevicePolicyManager.InstallSystemUpdateCallback();
-    method public void onInstallUpdateError(int, String);
+    method public void onInstallUpdateError(int, @NonNull String);
     field public static final int UPDATE_ERROR_BATTERY_LOW = 5; // 0x5
     field public static final int UPDATE_ERROR_FILE_NOT_FOUND = 4; // 0x4
     field public static final int UPDATE_ERROR_INCORRECT_OS_VERSION = 2; // 0x2
@@ -11384,6 +11384,7 @@
   public class PackageInstaller {
     method public void abandonSession(int);
     method public int createSession(@NonNull android.content.pm.PackageInstaller.SessionParams) throws java.io.IOException;
+    method @Nullable public android.content.pm.PackageInstaller.SessionInfo getActiveStagedSession();
     method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllSessions();
     method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getMySessions();
     method @Nullable public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int);
@@ -11468,6 +11469,7 @@
     method @NonNull public String getStagedSessionErrorMessage();
     method @NonNull public android.os.UserHandle getUser();
     method public boolean isActive();
+    method public boolean isCommitted();
     method public boolean isMultiPackage();
     method public boolean isSealed();
     method public boolean isStaged();
@@ -13087,10 +13089,10 @@
     method @Deprecated public String buildUnionSubQuery(String, String[], java.util.Set<java.lang.String>, int, String, String, String[], String, String);
     method public int delete(@NonNull android.database.sqlite.SQLiteDatabase, @Nullable String, @Nullable String[]);
     method @Nullable public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory();
-    method public boolean getDistinct();
     method @Nullable public java.util.Map<java.lang.String,java.lang.String> getProjectionMap();
-    method public boolean getStrict();
     method @Nullable public String getTables();
+    method public boolean isDistinct();
+    method public boolean isStrict();
     method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String);
     method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String, String);
     method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String, String, android.os.CancellationSignal);
@@ -29265,7 +29267,6 @@
     method public abstract boolean isRelative();
     method public android.net.Uri normalizeScheme();
     method public static android.net.Uri parse(String);
-    method @NonNull public String toSafeString();
     method public abstract String toString();
     method public static android.net.Uri withAppendedPath(android.net.Uri, String);
     method public static void writeToParcel(android.os.Parcel, android.net.Uri);
diff --git a/api/system-current.txt b/api/system-current.txt
index 3d403607..23cecf4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1740,7 +1740,7 @@
   }
 
   public class ShortcutManager {
-    method @NonNull public java.util.List<android.content.pm.ShortcutManager.ShareShortcutInfo> getShareTargets(@NonNull android.content.IntentFilter);
+    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS) public java.util.List<android.content.pm.ShortcutManager.ShareShortcutInfo> getShareTargets(@NonNull android.content.IntentFilter);
     method public boolean hasShareTargets(@NonNull String);
   }
 
@@ -4233,6 +4233,10 @@
     field public static final int TAG_SYSTEM_PROBE = -190; // 0xffffff42
   }
 
+  public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable {
+    method @NonNull public String toSafeString();
+  }
+
   public class VpnService extends android.app.Service {
     method @RequiresPermission(android.Manifest.permission.CONTROL_VPN) public static void prepareAndAuthorize(android.content.Context);
   }
@@ -5664,7 +5668,7 @@
   }
 
   public static interface DynamicSystemClient.OnStatusChangedListener {
-    method public void onStatusChanged(int, int, long);
+    method public void onStatusChanged(int, int, long, @Nullable Throwable);
   }
 
 }
@@ -6896,6 +6900,22 @@
 
 }
 
+package android.service.watchdog {
+
+  public abstract class ExplicitHealthCheckService extends android.app.Service {
+    ctor public ExplicitHealthCheckService();
+    method public final void notifyHealthCheckPassed(@NonNull String);
+    method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+    method public abstract void onCancelHealthCheck(@NonNull String);
+    method @NonNull public abstract java.util.List<java.lang.String> onGetRequestedPackages();
+    method @NonNull public abstract java.util.List<java.lang.String> onGetSupportedPackages();
+    method public abstract void onRequestHealthCheck(@NonNull String);
+    field public static final String BIND_PERMISSION = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
+    field public static final String SERVICE_INTERFACE = "android.service.watchdog.ExplicitHealthCheckService";
+  }
+
+}
+
 package android.telecom {
 
   @Deprecated public class AudioState implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index f186e238..6c0b928 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2150,9 +2150,9 @@
     method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static long getContributedMediaSize(android.content.Context, String, android.os.UserHandle) throws java.io.IOException;
     method @NonNull public static java.io.File getVolumePath(@NonNull String) throws java.io.FileNotFoundException;
     method @NonNull public static java.util.Collection<java.io.File> getVolumeScanPaths(@NonNull String) throws java.io.FileNotFoundException;
-    field public static final String EXTRA_ORIGINATED_FROM_SHELL = "android.intent.extra.originated_from_shell";
-    field public static final String SCAN_FILE_CALL = "scan_file";
-    field public static final String SCAN_VOLUME_CALL = "scan_volume";
+    method public static android.net.Uri scanFile(android.content.Context, java.io.File);
+    method public static android.net.Uri scanFileFromShell(android.content.Context, java.io.File);
+    method public static void scanVolume(android.content.Context, java.io.File);
   }
 
   public final class Settings {
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index a6c7cae..8e7277c 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -101,7 +101,7 @@
 
 BootAnimation::BootAnimation(sp<Callbacks> callbacks)
         : Thread(false), mClockEnabled(true), mTimeIsAccurate(false),
-        mTimeFormat12Hour(false), mTimeCheckThread(NULL), mCallbacks(callbacks) {
+        mTimeFormat12Hour(false), mTimeCheckThread(nullptr), mCallbacks(callbacks) {
     mSession = new SurfaceComposerClient();
 
     std::string powerCtl = android::base::GetProperty("sys.powerctl", "");
@@ -156,7 +156,7 @@
 status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets,
         const char* name) {
     Asset* asset = assets.open(name, Asset::ACCESS_BUFFER);
-    if (asset == NULL)
+    if (asset == nullptr)
         return NO_INIT;
     SkBitmap bitmap;
     sk_sp<SkData> data = SkData::MakeWithoutCopy(asset->getBuffer(false),
@@ -234,7 +234,7 @@
         case kN32_SkColorType:
             if (!mUseNpotTextures && (tw != w || th != h)) {
                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA,
-                        GL_UNSIGNED_BYTE, 0);
+                        GL_UNSIGNED_BYTE, nullptr);
                 glTexSubImage2D(GL_TEXTURE_2D, 0,
                         0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p);
             } else {
@@ -246,7 +246,7 @@
         case kRGB_565_SkColorType:
             if (!mUseNpotTextures && (tw != w || th != h)) {
                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB,
-                        GL_UNSIGNED_SHORT_5_6_5, 0);
+                        GL_UNSIGNED_SHORT_5_6_5, nullptr);
                 glTexSubImage2D(GL_TEXTURE_2D, 0,
                         0, 0, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p);
             } else {
@@ -304,10 +304,10 @@
 
     EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
 
-    eglInitialize(display, 0, 0);
+    eglInitialize(display, nullptr, nullptr);
     eglChooseConfig(display, attribs, &config, 1, &numConfigs);
-    surface = eglCreateWindowSurface(display, config, s.get(), NULL);
-    context = eglCreateContext(display, config, NULL, NULL);
+    surface = eglCreateWindowSurface(display, config, s.get(), nullptr);
+    context = eglCreateContext(display, config, nullptr, nullptr);
     eglQuerySurface(display, surface, EGL_WIDTH, &w);
     eglQuerySurface(display, surface, EGL_HEIGHT, &h);
 
@@ -671,7 +671,7 @@
     // Parse the description file
     for (;;) {
         const char* endl = strstr(s, "\n");
-        if (endl == NULL) break;
+        if (endl == nullptr) break;
         String8 line(s, endl - s);
         const char* l = line.string();
         int fps = 0;
@@ -699,8 +699,8 @@
             part.count = count;
             part.pause = pause;
             part.path = path;
-            part.audioData = NULL;
-            part.animation = NULL;
+            part.audioData = nullptr;
+            part.animation = nullptr;
             if (!parseColor(color, part.backgroundColor)) {
                 SLOGE("> invalid color '#%s'", color);
                 part.backgroundColor[0] = 0.0f;
@@ -716,9 +716,9 @@
             part.playUntilComplete = false;
             part.count = 1;
             part.pause = 0;
-            part.audioData = NULL;
+            part.audioData = nullptr;
             part.animation = loadAnimation(String8(SYSTEM_BOOTANIMATION_FILE));
-            if (part.animation != NULL)
+            if (part.animation != nullptr)
                 animation.parts.add(part);
         }
         s = ++endl;
@@ -731,7 +731,7 @@
 {
     // read all the data structures
     const size_t pcount = animation.parts.size();
-    void *cookie = NULL;
+    void *cookie = nullptr;
     ZipFileRO* zip = animation.zip;
     if (!zip->startIteration(&cookie)) {
         return false;
@@ -739,7 +739,7 @@
 
     ZipEntryRO entry;
     char name[ANIM_ENTRY_NAME_MAX];
-    while ((entry = zip->nextEntry(cookie)) != NULL) {
+    while ((entry = zip->nextEntry(cookie)) != nullptr) {
         const int foundEntryName = zip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
         if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
             SLOGE("Error fetching entry file name");
@@ -762,7 +762,7 @@
                 if (path == animation.parts[j].path) {
                     uint16_t method;
                     // supports only stored png files
-                    if (zip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) {
+                    if (zip->getEntryInfo(entry, &method, nullptr, nullptr, nullptr, nullptr, nullptr)) {
                         if (method == ZipFileRO::kCompressStored) {
                             FileMap* map = zip->createEntryFileMap(entry);
                             if (map) {
@@ -800,7 +800,7 @@
         for (size_t frameIdx = 0; frameIdx < part.frames.size(); frameIdx++) {
             const char* endl = strstr(trimDataStr, "\n");
             // No more trimData for this part.
-            if (endl == NULL) {
+            if (endl == nullptr) {
                 break;
             }
             String8 line(trimDataStr, endl - trimDataStr);
@@ -927,7 +927,7 @@
         glBindTexture(GL_TEXTURE_2D, 0);
 
         // Handle animation package
-        if (part.animation != NULL) {
+        if (part.animation != nullptr) {
             playAnimation(*part.animation);
             if (exitPending())
                 break;
@@ -1001,7 +1001,7 @@
                     spec.tv_nsec = (now + delay) % 1000000000;
                     int err;
                     do {
-                        err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
+                        err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, nullptr);
                     } while (err<0 && errno == EINTR);
                 }
 
@@ -1090,13 +1090,13 @@
     if (mLoadedFiles.indexOf(fn) >= 0) {
         SLOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
             fn.string());
-        return NULL;
+        return nullptr;
     }
     ZipFileRO *zip = ZipFileRO::open(fn);
-    if (zip == NULL) {
+    if (zip == nullptr) {
         SLOGE("Failed to open animation zip \"%s\": %s",
             fn.string(), strerror(errno));
-        return NULL;
+        return nullptr;
     }
 
     Animation *animation =  new Animation;
@@ -1107,7 +1107,7 @@
 
     parseAnimationDesc(*animation);
     if (!preloadZip(*animation)) {
-        return NULL;
+        return nullptr;
     }
 
 
@@ -1135,7 +1135,7 @@
     }
 
     FILE* file = fopen(LAST_TIME_CHANGED_FILE_PATH, "r");
-    if (file != NULL) {
+    if (file != nullptr) {
       long long lastChangedTime = 0;
       fscanf(file, "%lld", &lastChangedTime);
       fclose(file);
diff --git a/cmds/bootanimation/audioplay.cpp b/cmds/bootanimation/audioplay.cpp
index 874aab0..c5e16c6 100644
--- a/cmds/bootanimation/audioplay.cpp
+++ b/cmds/bootanimation/audioplay.cpp
@@ -39,14 +39,14 @@
 using namespace android;
 
 // engine interfaces
-static SLObjectItf engineObject = NULL;
+static SLObjectItf engineObject = nullptr;
 static SLEngineItf engineEngine;
 
 // output mix interfaces
-static SLObjectItf outputMixObject = NULL;
+static SLObjectItf outputMixObject = nullptr;
 
 // buffer queue player interfaces
-static SLObjectItf bqPlayerObject = NULL;
+static SLObjectItf bqPlayerObject = nullptr;
 static SLPlayItf bqPlayerPlay;
 static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
 static SLMuteSoloItf bqPlayerMuteSolo;
@@ -89,7 +89,7 @@
 }
 
 bool hasPlayer() {
-    return (engineObject != NULL && bqPlayerObject != NULL);
+    return (engineObject != nullptr && bqPlayerObject != nullptr);
 }
 
 // create the engine and output mix objects
@@ -97,7 +97,7 @@
     SLresult result;
 
     // create engine
-    result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
+    result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr);
     if (result != SL_RESULT_SUCCESS) {
         ALOGE("slCreateEngine failed with result %d", result);
         return false;
@@ -121,7 +121,7 @@
     (void)result;
 
     // create output mix
-    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
+    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, nullptr, nullptr);
     if (result != SL_RESULT_SUCCESS) {
         ALOGE("sl engine CreateOutputMix failed with result %d", result);
         return false;
@@ -173,7 +173,7 @@
 
     // configure audio sink
     SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
-    SLDataSink audioSnk = {&loc_outmix, NULL};
+    SLDataSink audioSnk = {&loc_outmix, nullptr};
 
     // create audio player
     const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_ANDROIDCONFIGURATION};
@@ -236,7 +236,7 @@
     (void)result;
 
     // register callback on the buffer queue
-    result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
+    result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, nullptr);
     if (result != SL_RESULT_SUCCESS) {
         ALOGE("sl bqPlayerBufferQueue RegisterCallback failed with result %d", result);
         return false;
@@ -261,7 +261,7 @@
                   const uint8_t** oSoundBuf, unsigned* oSoundBufSize) {
     *oSoundBuf = clipBuf;
     *oSoundBufSize = clipBufSize;
-    *oChunkFormat = NULL;
+    *oChunkFormat = nullptr;
     const RiffWaveHeader* wavHeader = (const RiffWaveHeader*)*oSoundBuf;
     if (*oSoundBufSize < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
         (wavHeader->wave_id != ID_WAVE)) {
@@ -303,7 +303,7 @@
         }
     }
 
-    if (*oChunkFormat == NULL) {
+    if (*oChunkFormat == nullptr) {
         ALOGE("format not found in WAV file");
         return false;
     }
@@ -435,7 +435,7 @@
 
     SLresult result;
 
-    if (NULL != bqPlayerPlay) {
+    if (nullptr != bqPlayerPlay) {
         // set the player's state
         result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay,
             isPlaying ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_STOPPED);
@@ -445,28 +445,28 @@
 
 void destroy() {
     // destroy buffer queue audio player object, and invalidate all associated interfaces
-    if (bqPlayerObject != NULL) {
+    if (bqPlayerObject != nullptr) {
         CHATTY("destroying audio player");
         (*bqPlayerObject)->Destroy(bqPlayerObject);
-        bqPlayerObject = NULL;
-        bqPlayerPlay = NULL;
-        bqPlayerBufferQueue = NULL;
-        bqPlayerMuteSolo = NULL;
-        bqPlayerVolume = NULL;
+        bqPlayerObject = nullptr;
+        bqPlayerPlay = nullptr;
+        bqPlayerBufferQueue = nullptr;
+        bqPlayerMuteSolo = nullptr;
+        bqPlayerVolume = nullptr;
     }
 
     // destroy output mix object, and invalidate all associated interfaces
-    if (outputMixObject != NULL) {
+    if (outputMixObject != nullptr) {
         (*outputMixObject)->Destroy(outputMixObject);
-        outputMixObject = NULL;
+        outputMixObject = nullptr;
     }
 
     // destroy engine object, and invalidate all associated interfaces
-    if (engineObject != NULL) {
+    if (engineObject != nullptr) {
         CHATTY("destroying audio engine");
         (*engineObject)->Destroy(engineObject);
-        engineObject = NULL;
-        engineEngine = NULL;
+        engineObject = nullptr;
+        engineEngine = nullptr;
     }
 }
 
diff --git a/cmds/bootanimation/iot/iotbootanimation_main.cpp b/cmds/bootanimation/iot/iotbootanimation_main.cpp
index 2a3d376..ae35297 100644
--- a/cmds/bootanimation/iot/iotbootanimation_main.cpp
+++ b/cmds/bootanimation/iot/iotbootanimation_main.cpp
@@ -60,7 +60,7 @@
 
         mBootAction = new BootAction();
         if (!mBootAction->init(library_path, mBootParameters)) {
-            mBootAction = NULL;
+            mBootAction = nullptr;
         }
     };
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4c05497..4b0c05f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2166,7 +2166,7 @@
          * reported back to the IT admin to be read.
          */
         public void onInstallUpdateError(
-                @InstallUpdateCallbackErrorConstants int errorCode, String errorMessage) {
+                @InstallUpdateCallbackErrorConstants int errorCode, @NonNull String errorMessage) {
         }
     }
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 032e5ac..d87171e 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3115,21 +3115,14 @@
      * <p>
      * The path to the file is contained in {@link Intent#getData()}.
      *
-     * @deprecated Starting in the {@link android.os.Build.VERSION_CODES#Q}
-     *             release, shared storage paths are sandboxed per application,
-     *             and this broadcast cannot correctly translate those sandboxed
-     *             paths. Callers will need to instead migrate to using
-     *             {@link MediaScannerConnection}.
+     * @deprecated Callers should migrate to inserting items directly into
+     *             {@link MediaStore}, where they will be automatically scanned
+     *             after each mutation.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     @Deprecated
     public static final String ACTION_MEDIA_SCANNER_SCAN_FILE = "android.intent.action.MEDIA_SCANNER_SCAN_FILE";
 
-    /** @hide */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @Deprecated
-    public static final String ACTION_MEDIA_SCANNER_SCAN_VOLUME = "android.intent.action.MEDIA_SCANNER_SCAN_VOLUME";
-
    /**
      * Broadcast Action:  The "Media Button" was pressed.  Includes a single
      * extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 3edd17a..1e6cea3 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -497,6 +497,36 @@
     }
 
     /**
+     * Returns an active staged session, or {@code null} if there is none.
+     *
+     * <p>Staged session is active iff:
+     * <ul>
+     *     <li>It is committed.
+     *     <li>It is not applied.
+     *     <li>It is not failed.
+     * </ul>
+     *
+     * <p>In case of a multi-apk session, parent session will be returned.
+     */
+    public @Nullable SessionInfo getActiveStagedSession() {
+        final List<SessionInfo> stagedSessions = getStagedSessions();
+        for (SessionInfo s : stagedSessions) {
+            if (s.isStagedSessionApplied() || s.isStagedSessionFailed()) {
+                // Finalized session.
+                continue;
+            }
+            if (s.getParentSessionId() != SessionInfo.INVALID_ID) {
+                // Child session.
+                continue;
+            }
+            if (s.isCommitted()) {
+                return s;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Uninstall the given package, removing it completely from the device. This
      * method is available to:
      * <ul>
@@ -1770,6 +1800,9 @@
         private String mStagedSessionErrorMessage;
 
         /** {@hide} */
+        public boolean isCommitted;
+
+        /** {@hide} */
         @UnsupportedAppUsage
         public SessionInfo() {
         }
@@ -1809,6 +1842,7 @@
             isStagedSessionFailed = source.readBoolean();
             mStagedSessionErrorCode = source.readInt();
             mStagedSessionErrorMessage = source.readString();
+            isCommitted = source.readBoolean();
         }
 
         /**
@@ -2181,6 +2215,13 @@
             mStagedSessionErrorMessage = errorMessage;
         }
 
+        /**
+         * Whenever this session was committed.
+         */
+        public boolean isCommitted() {
+            return isCommitted;
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -2218,6 +2259,7 @@
             dest.writeBoolean(isStagedSessionFailed);
             dest.writeInt(mStagedSessionErrorCode);
             dest.writeString(mStagedSessionErrorMessage);
+            dest.writeBoolean(isCommitted);
         }
 
         public static final Parcelable.Creator<SessionInfo>
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index b480939..35d1eac 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -166,6 +166,9 @@
     private static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO = 1.333f;
     private static final float DEFAULT_PRE_Q_MIN_ASPECT_RATIO_WATCH = 1f;
 
+    private static final int DEFAULT_MIN_SDK_VERSION = 1;
+    private static final int DEFAULT_TARGET_SDK_VERSION = 0;
+
     // TODO: switch outError users to PackageParserException
     // TODO: refactor "codePath" to "apkPath"
 
@@ -466,6 +469,8 @@
         public final int versionCodeMajor;
         public final int revisionCode;
         public final int installLocation;
+        public final int minSdkVersion;
+        public final int targetSdkVersion;
         public final VerifierInfo[] verifiers;
         public final SigningDetails signingDetails;
         public final boolean coreApp;
@@ -484,7 +489,8 @@
                 int revisionCode, int installLocation, List<VerifierInfo> verifiers,
                 SigningDetails signingDetails, boolean coreApp,
                 boolean debuggable, boolean multiArch, boolean use32bitAbi,
-                boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits) {
+                boolean useEmbeddedDex, boolean extractNativeLibs, boolean isolatedSplits,
+                int minSdkVersion, int targetSdkVersion) {
             this.codePath = codePath;
             this.packageName = packageName;
             this.splitName = splitName;
@@ -505,6 +511,8 @@
             this.extractNativeLibs = extractNativeLibs;
             this.isolatedSplits = isolatedSplits;
             this.isSplitRequired = isSplitRequired;
+            this.minSdkVersion = minSdkVersion;
+            this.targetSdkVersion = targetSdkVersion;
         }
 
         public long getLongVersionCode() {
@@ -1712,6 +1720,8 @@
         int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
         int versionCode = 0;
         int versionCodeMajor = 0;
+        int targetSdkVersion = DEFAULT_TARGET_SDK_VERSION;
+        int minSdkVersion = DEFAULT_MIN_SDK_VERSION;
         int revisionCode = 0;
         boolean coreApp = false;
         boolean debuggable = false;
@@ -1749,7 +1759,7 @@
             }
         }
 
-        // Only search the tree when the tag is directly below <manifest>
+        // Only search the tree when the tag is the direct child of <manifest> tag
         int type;
         final int searchDepth = parser.getDepth() + 1;
 
@@ -1800,13 +1810,25 @@
                             PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                             "<uses-split> tag requires 'android:name' attribute");
                 }
+            } else if (TAG_USES_SDK.equals(parser.getName())) {
+                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+                    final String attr = attrs.getAttributeName(i);
+                    if ("targetSdkVersion".equals(attr)) {
+                        targetSdkVersion = attrs.getAttributeIntValue(i,
+                                DEFAULT_TARGET_SDK_VERSION);
+                    }
+                    if ("minSdkVersion".equals(attr)) {
+                        minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
+                    }
+                }
             }
         }
 
         return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
                 configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor,
                 revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable,
-                multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, isolatedSplits);
+                multiArch, use32bitAbi, useEmbeddedDex, extractNativeLibs, isolatedSplits,
+                minSdkVersion, targetSdkVersion);
     }
 
     /**
@@ -8350,6 +8372,7 @@
         }
     }
 
+    // TODO(b/129261524): Clean up API
     public static PackageInfo generatePackageInfoFromApex(File apexFile, boolean collectCerts)
             throws PackageParserException {
         PackageInfo pi = new PackageInfo();
@@ -8369,25 +8392,27 @@
             }
         }
 
-        // TODO(b/123086053) properly fill in the ApplicationInfo with data from AndroidManifest
+        PackageParser.ApkLite apk = PackageParser.parseApkLite(apexFile, parseFlags);
+
+        // Properly fill in the ApplicationInfo with data from AndroidManifest
         // Add ApplicationInfo to the PackageInfo.
+        // TODO(b/129267599)
         ApplicationInfo ai = new ApplicationInfo();
+        ai.packageName = apk.packageName;
         ai.sourceDir = apexFile.getPath();
         ai.flags = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_INSTALLED;
         ai.enabled = true;
-        ai.targetSdkVersion = 28;
-        ai.targetSandboxVersion = 0;
-        pi.applicationInfo = ai;
-
-
-        // TODO(b/123052859): We should avoid these repeated calls to parseApkLite each time
-        // we want to generate information for APEX modules.
-        PackageParser.ApkLite apk = PackageParser.parseApkLite(apexFile, parseFlags);
+        ai.minSdkVersion = apk.minSdkVersion;
+        ai.targetSdkVersion = apk.targetSdkVersion;
+        ai.targetSandboxVersion = PARSE_DEFAULT_TARGET_SANDBOX;
+        ai.setVersionCode(apk.getLongVersionCode());
 
         pi.packageName = apk.packageName;
-        ai.packageName = apk.packageName;
+        pi.splitNames = new String[]{apk.splitName};
         pi.setLongVersionCode(apk.getLongVersionCode());
-        ai.setVersionCode(apk.getLongVersionCode());
+        pi.applicationInfo = ai;
+        pi.coreApp = apk.coreApp;
+
 
         if (collectCerts) {
             if (apk.signingDetails.hasPastSigningCertificates()) {
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index df67117..f851799 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -15,8 +15,10 @@
  */
 package android.content.pm;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
@@ -565,6 +567,7 @@
      */
     @NonNull
     @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_APP_PREDICTIONS)
     public List<ShareShortcutInfo> getShareTargets(@NonNull IntentFilter filter) {
         try {
             return mService.getShareTargets(mContext.getPackageName(), filter,
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index a73a719..bad80b8 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -80,7 +80,7 @@
      * Get if the query is marked as {@code DISTINCT}, as last configured by
      * {@link #setDistinct(boolean)}.
      */
-    public boolean getDistinct() {
+    public boolean isDistinct() {
         return mDistinct;
     }
 
@@ -215,7 +215,7 @@
     }
 
     /**
-     * Sets the cursor factory to be used for the query, as last configured by
+     * Gets the cursor factory to be used for the query, as last configured by
      * {@link #setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory)}.
      */
     public @Nullable SQLiteDatabase.CursorFactory getCursorFactory() {
@@ -251,7 +251,7 @@
      * Get if the query is marked as strict, as last configured by
      * {@link #setStrict(boolean)}.
      */
-    public boolean getStrict() {
+    public boolean isStrict() {
         return mStrict;
     }
 
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 9e0d95b..c3166e9 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.os.Environment;
@@ -380,9 +381,10 @@
      * returned as {@code tel:xxx-xxx-xxxx} and {@code http://example.com/path/to/item/} is
      * returned as {@code http://example.com/...}.
      * @return the common forms PII redacted string of this URI
+     * @hide
      */
-    @NonNull
-    public String toSafeString() {
+    @SystemApi
+    public @NonNull String toSafeString() {
         String scheme = getScheme();
         String ssp = getSchemeSpecificPart();
         if (scheme != null) {
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 6a01e56..7cc7ccd 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -30,6 +30,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.os.storage.IStorageManager;
 import android.provider.Settings;
 import android.telephony.euicc.EuiccManager;
 import android.text.TextUtils;
@@ -38,6 +39,8 @@
 import android.view.Display;
 import android.view.WindowManager;
 
+import com.android.internal.content.PackageHelper;
+
 import libcore.io.Streams;
 
 import java.io.ByteArrayInputStream;
@@ -854,6 +857,21 @@
     /** {@hide} */
     public static void rebootPromptAndWipeUserData(Context context, String reason)
             throws IOException {
+        boolean checkpointing = false;
+
+        // If we are running in checkpointing mode, we should not prompt a wipe.
+        // Checkpointing may save us. If it doesn't, we will wind up here again.
+        try {
+            IStorageManager storageManager = PackageHelper.getStorageManager();
+            if (storageManager.needsCheckpoint()) {
+                Log.i(TAG, "Rescue Party requested wipe. Aborting update instead.");
+                storageManager.abortChanges("rescueparty", false);
+            }
+            return;
+        } catch (RemoteException e) {
+            Log.i(TAG, "Failed to handle with checkpointing. Continuing with wipe.");
+        }
+
         String reasonArg = null;
         if (!TextUtils.isEmpty(reason)) {
             reasonArg = "--reason=" + sanitizeArg(reason);
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 33a6ee8..87367ac 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -19,6 +19,7 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.ComponentName;
@@ -31,6 +32,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
+import android.os.ParcelableException;
 import android.os.RemoteException;
 import android.util.Slog;
 
@@ -100,9 +102,10 @@
          * @param status status code, also defined in {@code DynamicSystemClient}.
          * @param cause cause code, also defined in {@code DynamicSystemClient}.
          * @param progress number of bytes installed.
+         * @param detail additional detail about the error if available, otherwise null.
          */
         void onStatusChanged(@InstallationStatus int status, @StatusChangedCause int cause,
-                @BytesLong long progress);
+                @BytesLong long progress, @Nullable Throwable detail);
     }
 
     /*
@@ -177,6 +180,12 @@
      */
     public static final String KEY_INSTALLED_SIZE = "KEY_INSTALLED_SIZE";
 
+    /**
+     * Message key, used when the service is sending exception detail to the client.
+     * @hide
+     */
+    public static final String KEY_EXCEPTION_DETAIL = "KEY_EXCEPTION_DETAIL";
+
     /*
      * Intent Actions
      */
@@ -248,7 +257,7 @@
             } catch (RemoteException e) {
                 Slog.e(TAG, "Unable to get status from installation service");
                 mExecutor.execute(() -> {
-                    mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0);
+                    mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0, e);
                 });
             }
         }
@@ -396,14 +405,19 @@
                 int status = msg.arg1;
                 int cause = msg.arg2;
                 // obj is non-null
-                long progress = ((Bundle) msg.obj).getLong(KEY_INSTALLED_SIZE);
+                Bundle bundle = (Bundle) msg.obj;
+                long progress = bundle.getLong(KEY_INSTALLED_SIZE);
+                ParcelableException t = (ParcelableException) bundle.getSerializable(
+                        KEY_EXCEPTION_DETAIL);
+
+                Throwable detail = t == null ? null : t.getCause();
 
                 if (mExecutor != null) {
                     mExecutor.execute(() -> {
-                        mListener.onStatusChanged(status, cause, progress);
+                        mListener.onStatusChanged(status, cause, progress, detail);
                     });
                 } else {
-                    mListener.onStatusChanged(status, cause, progress);
+                    mListener.onStatusChanged(status, cause, progress, detail);
                 }
                 break;
             default:
diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl
index 25f67f8..9db4111 100644
--- a/core/java/android/os/storage/IStorageManager.aidl
+++ b/core/java/android/os/storage/IStorageManager.aidl
@@ -193,4 +193,6 @@
     void commitChanges() = 83;
     boolean supportsCheckpoint() = 84;
     void startCheckpoint(int numTries) = 85;
+    boolean needsCheckpoint() = 86;
+    void abortChanges(in String message, boolean retry) = 87;
 }
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index dc5fdc0..7feeeee 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -115,9 +115,9 @@
      */
     public static final String VOLUME_EXTERNAL = "external";
 
-    /** {@hide} */ @TestApi
+    /** {@hide} */
     public static final String SCAN_FILE_CALL = "scan_file";
-    /** {@hide} */ @TestApi
+    /** {@hide} */
     public static final String SCAN_VOLUME_CALL = "scan_volume";
 
     /**
@@ -126,7 +126,6 @@
      *
      * {@hide}
      */
-    @TestApi
     public static final String EXTRA_ORIGINATED_FROM_SHELL =
             "android.intent.extra.originated_from_shell";
 
@@ -3539,12 +3538,32 @@
     }
 
     /** @hide */
+    @TestApi
     public static Uri scanFile(Context context, File file) {
+        return scan(context, SCAN_FILE_CALL, file, false);
+    }
+
+    /** @hide */
+    @TestApi
+    public static Uri scanFileFromShell(Context context, File file) {
+        return scan(context, SCAN_FILE_CALL, file, true);
+    }
+
+    /** @hide */
+    @TestApi
+    public static void scanVolume(Context context, File file) {
+        scan(context, SCAN_VOLUME_CALL, file, false);
+    }
+
+    /** @hide */
+    private static Uri scan(Context context, String method, File file,
+            boolean originatedFromShell) {
         final ContentResolver resolver = context.getContentResolver();
         try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
             final Bundle in = new Bundle();
             in.putParcelable(Intent.EXTRA_STREAM, Uri.fromFile(file));
-            final Bundle out = client.call(SCAN_FILE_CALL, null, in);
+            in.putBoolean(EXTRA_ORIGINATED_FROM_SHELL, originatedFromShell);
+            final Bundle out = client.call(method, null, in);
             return out.getParcelable(Intent.EXTRA_STREAM);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
diff --git a/core/java/android/service/carrier/CarrierService.java b/core/java/android/service/carrier/CarrierService.java
index c351d89..aeb186b 100644
--- a/core/java/android/service/carrier/CarrierService.java
+++ b/core/java/android/service/carrier/CarrierService.java
@@ -114,9 +114,7 @@
      * this UX, so a carrier app must be sure to call with active set to false
      * sometime after calling with it set to true.
      * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * or the calling app has carrier privileges.
+     * Requires Permission: calling app has carrier privileges.
      *
      * @param active Whether the carrier network change is or shortly will be
      *               active. Set this value to true to begin showing
diff --git a/core/java/android/service/watchdog/ExplicitHealthCheckService.java b/core/java/android/service/watchdog/ExplicitHealthCheckService.java
new file mode 100644
index 0000000..015fba1
--- /dev/null
+++ b/core/java/android/service/watchdog/ExplicitHealthCheckService.java
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ */
+
+package android.service.watchdog;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A service to provide packages supporting explicit health checks and route checks to these
+ * packages on behalf of the package watchdog.
+ *
+ * <p>To extend this class, you must declare the service in your manifest file with the
+ * {@link android.Manifest.permission#BIND_EXPLICIT_HEALTH_CHECK_SERVICE} permission,
+ * and include an intent filter with the {@link #SERVICE_INTERFACE} action. In adddition,
+ * your implementation must live in {@link PackageManger#SYSTEM_SHARED_LIBRARY_SERVICES}.
+ * For example:</p>
+ * <pre>
+ *     &lt;service android:name=".FooExplicitHealthCheckService"
+ *             android:exported="true"
+ *             android:priority="100"
+ *             android:permission="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"&gt;
+ *         &lt;intent-filter&gt;
+ *             &lt;action android:name="android.service.watchdog.ExplicitHealthCheckService" /&gt;
+ *         &lt;/intent-filter&gt;
+ *     &lt;/service&gt;
+ * </pre>
+ * @hide
+ */
+@SystemApi
+public abstract class ExplicitHealthCheckService extends Service {
+
+    private static final String TAG = "ExplicitHealthCheckService";
+
+    /**
+     * {@link Bundle} key for a {@link List} of {@link String} value.
+     *
+     * {@hide}
+     */
+    public static final String EXTRA_SUPPORTED_PACKAGES =
+            "android.service.watchdog.extra.supported_packages";
+
+    /**
+     * {@link Bundle} key for a {@link List} of {@link String} value.
+     *
+     * {@hide}
+     */
+    public static final String EXTRA_REQUESTED_PACKAGES =
+            "android.service.watchdog.extra.requested_packages";
+
+    /**
+     * {@link Bundle} key for a {@link String} value.
+     *
+     * {@hide}
+     */
+    public static final String EXTRA_HEALTH_CHECK_PASSED_PACKAGE =
+            "android.service.watchdog.extra.health_check_passed_package";
+
+    /**
+     * The Intent action that a service must respond to. Add it to the intent filter of the service
+     * in its manifest.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.service.watchdog.ExplicitHealthCheckService";
+
+    /**
+     * The permission that a service must require to ensure that only Android system can bind to it.
+     * If this permission is not enforced in the AndroidManifest of the service, the system will
+     * skip that service.
+     */
+    public static final String BIND_PERMISSION =
+            "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
+
+    private final ExplicitHealthCheckServiceWrapper mWrapper =
+            new ExplicitHealthCheckServiceWrapper();
+
+    /**
+     * Called when the system requests an explicit health check for {@code packageName}.
+     *
+     * <p> When {@code packageName} passes the check, implementors should call
+     * {@link #notifyHealthCheckPassed} to inform the system.
+     *
+     * <p> It could take many hours before a {@code packageName} passes a check and implementors
+     * should never drop requests unless {@link onCancel} is called or the service dies.
+     *
+     * <p> Requests should not be queued and additional calls while expecting a result for
+     * {@code packageName} should have no effect.
+     */
+    public abstract void onRequestHealthCheck(@NonNull String packageName);
+
+    /**
+     * Called when the system cancels the explicit health check request for {@code packageName}.
+     * Should do nothing if there are is no active request for {@code packageName}.
+     */
+    public abstract void onCancelHealthCheck(@NonNull String packageName);
+
+    /**
+     * Called when the system requests for all the packages supporting explicit health checks. The
+     * system may request an explicit health check for any of these packages with
+     * {@link #onRequestHealthCheck}.
+     *
+     * @return all packages supporting explicit health checks
+     */
+    @NonNull public abstract List<String> onGetSupportedPackages();
+
+    /**
+     * Called when the system requests for all the packages that it has currently requested
+     * an explicit health check for.
+     *
+     * @return all packages expecting an explicit health check result
+     */
+    @NonNull public abstract List<String> onGetRequestedPackages();
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+    @Nullable private RemoteCallback mCallback;
+
+    @Override
+    @NonNull
+    public final IBinder onBind(@NonNull Intent intent) {
+        return mWrapper;
+    }
+
+    /**
+     * Implementors should call this to notify the system when explicit health check passes
+     * for {@code packageName};
+     */
+    public final void notifyHealthCheckPassed(@NonNull String packageName) {
+        mHandler.post(() -> {
+            if (mCallback != null) {
+                Objects.requireNonNull(packageName,
+                        "Package passing explicit health check must be non-null");
+                Bundle bundle = new Bundle();
+                bundle.putString(EXTRA_HEALTH_CHECK_PASSED_PACKAGE, packageName);
+                mCallback.sendResult(bundle);
+            } else {
+                Log.wtf(TAG, "System missed explicit health check result for " + packageName);
+            }
+        });
+    }
+
+    private class ExplicitHealthCheckServiceWrapper extends IExplicitHealthCheckService.Stub {
+        @Override
+        public void setCallback(RemoteCallback callback) throws RemoteException {
+            mHandler.post(() -> {
+                mCallback = callback;
+            });
+        }
+
+        @Override
+        public void request(String packageName) throws RemoteException {
+            mHandler.post(() -> ExplicitHealthCheckService.this.onRequestHealthCheck(packageName));
+        }
+
+        @Override
+        public void cancel(String packageName) throws RemoteException {
+            mHandler.post(() -> ExplicitHealthCheckService.this.onCancelHealthCheck(packageName));
+        }
+
+        @Override
+        public void getSupportedPackages(RemoteCallback callback) throws RemoteException {
+            mHandler.post(() -> sendPackages(callback, EXTRA_SUPPORTED_PACKAGES,
+                    ExplicitHealthCheckService.this.onGetSupportedPackages()));
+        }
+
+        @Override
+        public void getRequestedPackages(RemoteCallback callback) throws RemoteException {
+            mHandler.post(() -> sendPackages(callback, EXTRA_REQUESTED_PACKAGES,
+                    ExplicitHealthCheckService.this.onGetRequestedPackages()));
+        }
+
+        private void sendPackages(RemoteCallback callback, String key, List<String> packages) {
+            Objects.requireNonNull(packages,
+                    "Supported and requested package list must be non-null");
+            Bundle bundle = new Bundle();
+            bundle.putStringArrayList(key, new ArrayList<>(packages));
+            callback.sendResult(bundle);
+        }
+    }
+}
diff --git a/core/java/android/service/watchdog/IExplicitHealthCheckService.aidl b/core/java/android/service/watchdog/IExplicitHealthCheckService.aidl
new file mode 100644
index 0000000..78c0328d
--- /dev/null
+++ b/core/java/android/service/watchdog/IExplicitHealthCheckService.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.service.watchdog;
+
+import android.os.RemoteCallback;
+
+/**
+ * @hide
+ */
+oneway interface IExplicitHealthCheckService
+{
+    void setCallback(in @nullable RemoteCallback callback);
+    void request(String packageName);
+    void cancel(String packageName);
+    void getSupportedPackages(in RemoteCallback callback);
+    void getRequestedPackages(in RemoteCallback callback);
+}
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index df8690d..f9e601f 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -105,11 +105,7 @@
                 Log.i(LOG_TAG, "Caching window: " + window.getId());
             }
             final int windowId = window.getId();
-            AccessibilityWindowInfo oldWindow = mWindowCache.get(windowId);
-            if (oldWindow != null) {
-                oldWindow.recycle();
-            }
-            mWindowCache.put(windowId, AccessibilityWindowInfo.obtain(window));
+            mWindowCache.put(windowId, new AccessibilityWindowInfo(window));
         }
     }
 
@@ -225,7 +221,7 @@
             if (info != null) {
                 // Return a copy since the client calls to AccessibilityNodeInfo#recycle()
                 // will wipe the data of the cached info.
-                info = AccessibilityNodeInfo.obtain(info);
+                info = new AccessibilityNodeInfo(info);
             }
             if (DEBUG) {
                 Log.i(LOG_TAG, "get(" + accessibilityNodeId + ") = " + info);
@@ -257,7 +253,7 @@
                 List<AccessibilityWindowInfo> windows = new ArrayList<>(sortedWindowCount);
                 for (int i = sortedWindowCount - 1; i >= 0; i--) {
                     AccessibilityWindowInfo window = sortedWindows.valueAt(i);
-                    windows.add(AccessibilityWindowInfo.obtain(window));
+                    windows.add(new AccessibilityWindowInfo(window));
                     sortedWindows.removeAt(i);
                 }
 
@@ -271,7 +267,7 @@
         synchronized (mLock) {
             AccessibilityWindowInfo window = mWindowCache.get(windowId);
             if (window != null) {
-               return AccessibilityWindowInfo.obtain(window);
+                return new AccessibilityWindowInfo(window);
             }
             return null;
         }
@@ -326,14 +322,12 @@
                 final long oldParentId = oldInfo.getParentNodeId();
                 if (info.getParentNodeId() != oldParentId) {
                     clearSubTreeLocked(windowId, oldParentId);
-                } else {
-                    oldInfo.recycle();
                 }
            }
 
             // Cache a copy since the client calls to AccessibilityNodeInfo#recycle()
             // will wipe the data of the cached info.
-            AccessibilityNodeInfo clone = AccessibilityNodeInfo.obtain(info);
+            AccessibilityNodeInfo clone = new AccessibilityNodeInfo(info);
             nodes.put(sourceId, clone);
             if (clone.isAccessibilityFocused()) {
                 if (mAccessibilityFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
@@ -371,12 +365,7 @@
     }
 
     private void clearWindowCache() {
-        final int windowCount = mWindowCache.size();
-        for (int i = windowCount - 1; i >= 0; i--) {
-            AccessibilityWindowInfo window = mWindowCache.valueAt(i);
-            window.recycle();
-            mWindowCache.removeAt(i);
-        }
+        mWindowCache.clear();
         mIsAllWindowsCached = false;
     }
 
@@ -391,13 +380,6 @@
         if (nodes == null) {
             return;
         }
-        // Recycle the nodes before clearing the cache.
-        final int nodeCount = nodes.size();
-        for (int i = nodeCount - 1; i >= 0; i--) {
-            AccessibilityNodeInfo info = nodes.valueAt(i);
-            nodes.removeAt(i);
-            info.recycle();
-        }
         mNodeCache.remove(windowId);
     }
 
@@ -440,11 +422,9 @@
         for (int i = 0; i < childCount; i++) {
             final long childNodeId = current.getChildId(i);
             if (clearSubTreeRecursiveLocked(nodes, childNodeId)) {
-                current.recycle();
                 return true;
             }
         }
-        current.recycle();
         return false;
     }
 
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index a8a787e..385d491 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -654,6 +654,7 @@
      */
     private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32;
 
+    // TODO(b/129300068): Remove sNumInstancesInUse.
     private static AtomicInteger sNumInstancesInUse;
 
     /**
@@ -766,6 +767,11 @@
         /* do nothing */
     }
 
+    /** @hide */
+    AccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        init(info);
+    }
+
     /**
      * Sets the source.
      * <p>
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
index 4383f8a..1e2321c 100644
--- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -96,6 +96,7 @@
     private static final int MAX_POOL_SIZE = 10;
     private static final SynchronizedPool<AccessibilityWindowInfo> sPool =
             new SynchronizedPool<AccessibilityWindowInfo>(MAX_POOL_SIZE);
+    // TODO(b/129300068): Remove sNumInstancesInUse.
     private static AtomicInteger sNumInstancesInUse;
 
     // Data.
@@ -115,6 +116,11 @@
         /* do nothing - hide constructor */
     }
 
+    /** @hide */
+    AccessibilityWindowInfo(AccessibilityWindowInfo info) {
+        init(info);
+    }
+
     /**
      * Gets the title of the window.
      *
@@ -448,26 +454,7 @@
      */
     public static AccessibilityWindowInfo obtain(AccessibilityWindowInfo info) {
         AccessibilityWindowInfo infoClone = obtain();
-
-        infoClone.mType = info.mType;
-        infoClone.mLayer = info.mLayer;
-        infoClone.mBooleanProperties = info.mBooleanProperties;
-        infoClone.mId = info.mId;
-        infoClone.mParentId = info.mParentId;
-        infoClone.mBoundsInScreen.set(info.mBoundsInScreen);
-        infoClone.mTitle = info.mTitle;
-        infoClone.mAnchorId = info.mAnchorId;
-
-        if (info.mChildIds != null && info.mChildIds.size() > 0) {
-            if (infoClone.mChildIds == null) {
-                infoClone.mChildIds = info.mChildIds.clone();
-            } else {
-                infoClone.mChildIds.addAll(info.mChildIds);
-            }
-        }
-
-        infoClone.mConnectionId = info.mConnectionId;
-
+        infoClone.init(info);
         return infoClone;
     }
 
@@ -529,6 +516,32 @@
         parcel.writeInt(mConnectionId);
     }
 
+    /**
+     * Initializes this instance from another one.
+     *
+     * @param other The other instance.
+     */
+    private void init(AccessibilityWindowInfo other) {
+        mType = other.mType;
+        mLayer = other.mLayer;
+        mBooleanProperties = other.mBooleanProperties;
+        mId = other.mId;
+        mParentId = other.mParentId;
+        mBoundsInScreen.set(other.mBoundsInScreen);
+        mTitle = other.mTitle;
+        mAnchorId = other.mAnchorId;
+
+        if (other.mChildIds != null && other.mChildIds.size() > 0) {
+            if (mChildIds == null) {
+                mChildIds = other.mChildIds.clone();
+            } else {
+                mChildIds.addAll(other.mChildIds);
+            }
+        }
+
+        mConnectionId = other.mConnectionId;
+    }
+
     private void initFromParcel(Parcel parcel) {
         mType = parcel.readInt();
         mLayer = parcel.readInt();
diff --git a/core/java/android/view/inspector/PropertyReader.java b/core/java/android/view/inspector/PropertyReader.java
index b5020ce..5be0e3f 100644
--- a/core/java/android/view/inspector/PropertyReader.java
+++ b/core/java/android/view/inspector/PropertyReader.java
@@ -133,10 +133,11 @@
     void readColor(int id, @ColorInt int value);
 
     /**
-     * Read a color packed into a {@link ColorLong} as a property.
+     * Read a color packed into a {@code ColorLong} as a property.
      *
      * @param id Identifier of the property from a {@link PropertyMapper}
-     * @param value Value of the property
+     * @param value Value of the property packed as a {@code ColorLong}. See the
+     *              {@link Color} class for details of the packing.
      * @throws PropertyTypeMismatchException If the property ID is not mapped as a color
      */
     void readColor(int id, @ColorLong long value);
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index 248e026..e4de158 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -32,15 +32,18 @@
 import java.util.function.Predicate;
 
 /**
- * Given a process, will iterate over the child threads of the process, and return the CPU usage
- * statistics for each child thread. The CPU usage statistics contain the amount of time spent in a
- * frequency band.
+ * Iterates over processes, and all threads owned by those processes, and return the CPU usage for
+ * each thread. The CPU usage statistics contain the amount of time spent in a frequency band. CPU
+ * usage is collected using {@link ProcTimeInStateReader}.
+ *
+ * <p>We only collect CPU data for processes and threads that are owned by certain UIDs. These UIDs
+ * are configured via {@link #setUidPredicate}.
  *
  * <p>Frequencies are bucketed together to reduce the amount of data created. This means that we
- * return less frequencies than provided by {@link ProcTimeInStateReader}. The number of
- * frequencies is configurable by {@link #setNumBuckets}. Frequencies are reported as the lowest
- * frequency in that range. Frequencies are spread as evenly as possible across the buckets. The
- * buckets do not cross over the little/big frequencies reported.
+ * return less frequencies than provided by {@link ProcTimeInStateReader}. The number of frequencies
+ * is configurable by {@link #setNumBuckets}. Frequencies are reported as the lowest frequency in
+ * that range. Frequencies are spread as evenly as possible across the buckets. The buckets do not
+ * cross over the little/big frequencies reported.
  *
  * <p>N.B.: In order to bucket across little/big frequencies correctly, we assume that the {@code
  * time_in_state} file contains every little core frequency in ascending order, followed by every
@@ -60,56 +63,39 @@
     private static final String CPU_STATISTICS_FILENAME = "time_in_state";
 
     /**
-     * The name of the file to read process command line invocation from, must be found in
-     * {@code /proc/$PID/}
+     * The name of the file to read process command line invocation from, must be found in {@code
+     * /proc/$PID/}
      */
     private static final String PROCESS_NAME_FILENAME = "cmdline";
 
     /**
-     * The name of the file to read thread name from, must be found in
-     * {@code /proc/$PID/task/$TID}
+     * The name of the file to read thread name from, must be found in {@code /proc/$PID/task/$TID}
      */
     private static final String THREAD_NAME_FILENAME = "comm";
 
-    /**
-     * Glob pattern for the process directory names under {@code proc}
-     */
+    /** Glob pattern for the process directory names under {@code proc} */
     private static final String PROCESS_DIRECTORY_FILTER = "[0-9]*";
 
-    /**
-     * Default process name when the name can't be read
-     */
+    /** Default process name when the name can't be read */
     private static final String DEFAULT_PROCESS_NAME = "unknown_process";
 
-    /**
-     * Default thread name when the name can't be read
-     */
+    /** Default thread name when the name can't be read */
     private static final String DEFAULT_THREAD_NAME = "unknown_thread";
 
-    /**
-     * Default mount location of the {@code proc} filesystem
-     */
+    /** Default mount location of the {@code proc} filesystem */
     private static final Path DEFAULT_PROC_PATH = Paths.get("/proc");
 
-    /**
-     * The initial {@code time_in_state} file for {@link ProcTimeInStateReader}
-     */
+    /** The initial {@code time_in_state} file for {@link ProcTimeInStateReader} */
     private static final Path DEFAULT_INITIAL_TIME_IN_STATE_PATH =
             DEFAULT_PROC_PATH.resolve("self/time_in_state");
 
-    /**
-     * Value returned when there was an error getting an integer ID value (e.g. PID, UID)
-     */
+    /** Value returned when there was an error getting an integer ID value (e.g. PID, UID) */
     private static final int ID_ERROR = -1;
 
-    /**
-     * Thread ID used when reporting CPU used by other threads
-     */
+    /** Thread ID used when reporting CPU used by other threads */
     private static final int OTHER_THREADS_ID = -1;
 
-    /**
-     * Thread name used when reporting CPU used by other threads
-     */
+    /** Thread name used when reporting CPU used by other threads */
     private static final String OTHER_THREADS_NAME = "__OTHER_THREADS";
 
     /**
@@ -124,9 +110,7 @@
      */
     private int mMinimumTotalCpuUsageMillis;
 
-    /**
-     * Where the proc filesystem is mounted
-     */
+    /** Where the proc filesystem is mounted */
     private final Path mProcPath;
 
     /**
@@ -135,14 +119,10 @@
      */
     private int[] mFrequenciesKhz;
 
-    /**
-     * Used to read and parse {@code time_in_state} files
-     */
+    /** Used to read and parse {@code time_in_state} files */
     private final ProcTimeInStateReader mProcTimeInStateReader;
 
-    /**
-     * Used to sort frequencies and usage times into buckets
-     */
+    /** Used to sort frequencies and usage times into buckets */
     private FrequencyBucketCreator mFrequencyBucketCreator;
 
     private final Injector mInjector;
@@ -150,10 +130,9 @@
     /**
      * Create with a path where `proc` is mounted. Used primarily for testing
      *
-     * @param procPath               where `proc` is mounted (to find, see {@code mount | grep
-     *                               ^proc})
+     * @param procPath where `proc` is mounted (to find, see {@code mount | grep ^proc})
      * @param initialTimeInStatePath where the initial {@code time_in_state} file exists to define
-     *                               format
+     *     format
      */
     @VisibleForTesting
     public KernelCpuThreadReader(
@@ -162,7 +141,8 @@
             int minimumTotalCpuUsageMillis,
             Path procPath,
             Path initialTimeInStatePath,
-            Injector injector) throws IOException {
+            Injector injector)
+            throws IOException {
         mUidPredicate = uidPredicate;
         mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis;
         mProcPath = procPath;
@@ -205,7 +185,7 @@
      * #setUidPredicate}.
      */
     @Nullable
-    public ArrayList<ProcessCpuUsage> getProcessCpuUsageByUids() {
+    public ArrayList<ProcessCpuUsage> getProcessCpuUsage() {
         if (DEBUG) {
             Slog.d(TAG, "Reading CPU thread usages for processes owned by UIDs");
         }
@@ -213,7 +193,7 @@
         final ArrayList<ProcessCpuUsage> processCpuUsages = new ArrayList<>();
 
         try (DirectoryStream<Path> processPaths =
-                     Files.newDirectoryStream(mProcPath, PROCESS_DIRECTORY_FILTER)) {
+                Files.newDirectoryStream(mProcPath, PROCESS_DIRECTORY_FILTER)) {
             for (Path processPath : processPaths) {
                 final int processId = getProcessId(processPath);
                 final int uid = mInjector.getUidForPid(processId);
@@ -231,7 +211,7 @@
                 }
             }
         } catch (IOException e) {
-            Slog.w("Failed to iterate over process paths", e);
+            Slog.w(TAG, "Failed to iterate over process paths", e);
             return null;
         }
 
@@ -248,30 +228,68 @@
     }
 
     /**
-     * Read all of the CPU usage statistics for each child thread of the current process
-     *
-     * @return process CPU usage containing usage of all child threads
+     * Get the CPU frequencies that correspond to the times reported in {@link
+     * ThreadCpuUsage#usageTimesMillis}
      */
     @Nullable
-    public ProcessCpuUsage getCurrentProcessCpuUsage() {
-        return getProcessCpuUsage(mProcPath.resolve("self"), mInjector.myPid(), mInjector.myUid());
+    public int[] getCpuFrequenciesKhz() {
+        return mFrequenciesKhz;
+    }
+
+    /** Set the number of frequency buckets to use */
+    void setNumBuckets(int numBuckets) {
+        if (numBuckets < 1) {
+            Slog.w(TAG, "Number of buckets must be at least 1, but was " + numBuckets);
+            return;
+        }
+        // If `numBuckets` hasn't changed since the last set, do nothing
+        if (mFrequenciesKhz != null && mFrequenciesKhz.length == numBuckets) {
+            return;
+        }
+        mFrequencyBucketCreator =
+                new FrequencyBucketCreator(mProcTimeInStateReader.getFrequenciesKhz(), numBuckets);
+        mFrequenciesKhz =
+                mFrequencyBucketCreator.getBucketMinFrequencies(
+                        mProcTimeInStateReader.getFrequenciesKhz());
+    }
+
+    /** Set the UID predicate for {@link #getProcessCpuUsage} */
+    void setUidPredicate(Predicate<Integer> uidPredicate) {
+        mUidPredicate = uidPredicate;
+    }
+
+    /**
+     * If a thread has strictly less than {@code minimumTotalCpuUsageMillis} total CPU usage, it
+     * will not be reported
+     */
+    void setMinimumTotalCpuUsageMillis(int minimumTotalCpuUsageMillis) {
+        if (minimumTotalCpuUsageMillis < 0) {
+            Slog.w(TAG, "Negative minimumTotalCpuUsageMillis: " + minimumTotalCpuUsageMillis);
+            return;
+        }
+        mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis;
     }
 
     /**
      * Read all of the CPU usage statistics for each child thread of a process
      *
      * @param processPath the {@code /proc} path of the thread
-     * @param processId   the ID of the process
-     * @param uid         the ID of the user who owns the process
+     * @param processId the ID of the process
+     * @param uid the ID of the user who owns the process
      * @return process CPU usage containing usage of all child threads. Null if the process exited
-     * and its {@code proc} directory was removed while collecting information
+     *     and its {@code proc} directory was removed while collecting information
      */
     @Nullable
     private ProcessCpuUsage getProcessCpuUsage(Path processPath, int processId, int uid) {
         if (DEBUG) {
-            Slog.d(TAG, "Reading CPU thread usages with directory " + processPath
-                    + " process ID " + processId
-                    + " and user ID " + uid);
+            Slog.d(
+                    TAG,
+                    "Reading CPU thread usages with directory "
+                            + processPath
+                            + " process ID "
+                            + processId
+                            + " and user ID "
+                            + uid);
         }
 
         int[] filteredThreadsCpuUsage = null;
@@ -305,64 +323,15 @@
 
         // Add the filtered out thread CPU usage under an "other threads" ThreadCpuUsage
         if (filteredThreadsCpuUsage != null) {
-            threadCpuUsages.add(new ThreadCpuUsage(
-                    OTHER_THREADS_ID, OTHER_THREADS_NAME, filteredThreadsCpuUsage));
+            threadCpuUsages.add(
+                    new ThreadCpuUsage(
+                            OTHER_THREADS_ID, OTHER_THREADS_NAME, filteredThreadsCpuUsage));
         }
 
         if (DEBUG) {
             Slog.d(TAG, "Read CPU usage of " + threadCpuUsages.size() + " threads");
         }
-        return new ProcessCpuUsage(
-                processId,
-                getProcessName(processPath),
-                uid,
-                threadCpuUsages);
-    }
-
-    /**
-     * Set the number of frequency buckets to use
-     */
-    void setNumBuckets(int numBuckets) {
-        if (numBuckets < 1) {
-            Slog.w(TAG, "Number of buckets must be at least 1, but was " + numBuckets);
-            return;
-        }
-        // If `numBuckets` hasn't changed since the last set, do nothing
-        if (mFrequenciesKhz != null && mFrequenciesKhz.length == numBuckets) {
-            return;
-        }
-        mFrequencyBucketCreator = new FrequencyBucketCreator(
-                mProcTimeInStateReader.getFrequenciesKhz(), numBuckets);
-        mFrequenciesKhz = mFrequencyBucketCreator.getBucketMinFrequencies(
-                mProcTimeInStateReader.getFrequenciesKhz());
-    }
-
-    /**
-     * Set the UID predicate for {@link #getProcessCpuUsageByUids}
-     */
-    void setUidPredicate(Predicate<Integer> uidPredicate) {
-        mUidPredicate = uidPredicate;
-    }
-
-    /**
-     * If a thread has strictly less than {@code minimumTotalCpuUsageMillis} total CPU usage, it
-     * will not be reported
-     */
-    void setMinimumTotalCpuUsageMillis(int minimumTotalCpuUsageMillis) {
-        if (minimumTotalCpuUsageMillis < 0) {
-            Slog.w(TAG, "Negative minimumTotalCpuUsageMillis: " + minimumTotalCpuUsageMillis);
-            return;
-        }
-        mMinimumTotalCpuUsageMillis = minimumTotalCpuUsageMillis;
-    }
-
-    /**
-     * Get the CPU frequencies that correspond to the times reported in
-     * {@link ThreadCpuUsage#usageTimesMillis}
-     */
-    @Nullable
-    public int[] getCpuFrequenciesKhz() {
-        return mFrequenciesKhz;
+        return new ProcessCpuUsage(processId, getProcessName(processPath), uid, threadCpuUsages);
     }
 
     /**
@@ -370,7 +339,7 @@
      *
      * @param threadDirectory the {@code /proc} directory of the thread
      * @return thread CPU usage. Null if the thread exited and its {@code proc} directory was
-     * removed while collecting information
+     *     removed while collecting information
      */
     @Nullable
     private ThreadCpuUsage getThreadCpuUsage(Path threadDirectory) {
@@ -398,27 +367,21 @@
         return new ThreadCpuUsage(threadId, threadName, cpuUsages);
     }
 
-    /**
-     * Get the command used to start a process
-     */
+    /** Get the command used to start a process */
     private String getProcessName(Path processPath) {
         final Path processNamePath = processPath.resolve(PROCESS_NAME_FILENAME);
 
-        final String processName =
-                ProcStatsUtil.readSingleLineProcFile(processNamePath.toString());
+        final String processName = ProcStatsUtil.readSingleLineProcFile(processNamePath.toString());
         if (processName != null) {
             return processName;
         }
         return DEFAULT_PROCESS_NAME;
     }
 
-    /**
-     * Get the name of a thread, given the {@code /proc} path of the thread
-     */
+    /** Get the name of a thread, given the {@code /proc} path of the thread */
     private String getThreadName(Path threadPath) {
         final Path threadNamePath = threadPath.resolve(THREAD_NAME_FILENAME);
-        final String threadName =
-                ProcStatsUtil.readNullSeparatedFile(threadNamePath.toString());
+        final String threadName = ProcStatsUtil.readNullSeparatedFile(threadNamePath.toString());
         if (threadName == null) {
             return DEFAULT_THREAD_NAME;
         }
@@ -441,9 +404,8 @@
         }
     }
 
-    /**
-     * Get the sum of all CPU usage across all frequencies
-     */
+    /** Get the sum of all CPU usage across all frequencies */
+    @SuppressWarnings("ForLoopReplaceableByForEach")
     private static int totalCpuUsage(int[] cpuUsage) {
         int total = 0;
         for (int i = 0; i < cpuUsage.length; i++) {
@@ -452,9 +414,7 @@
         return total;
     }
 
-    /**
-     * Add two CPU frequency usages together
-     */
+    /** Add two CPU frequency usages together */
     private static int[] sumCpuUsage(int[] a, int[] b) {
         int[] summed = new int[a.length];
         for (int i = 0; i < a.length; i++) {
@@ -463,9 +423,7 @@
         return summed;
     }
 
-    /**
-     * Puts frequencies and usage times into buckets
-     */
+    /** Puts frequencies and usage times into buckets */
     @VisibleForTesting
     public static class FrequencyBucketCreator {
         private final int mNumBuckets;
@@ -480,7 +438,7 @@
          * Buckets based of a list of frequencies
          *
          * @param frequencies the frequencies to base buckets off
-         * @param numBuckets  how many buckets to create
+         * @param numBuckets how many buckets to create
          */
         @VisibleForTesting
         public FrequencyBucketCreator(long[] frequencies, int numBuckets) {
@@ -502,20 +460,20 @@
 
             // Ensure that we don't have more buckets than frequencies
             mLittleNumBuckets = Math.min(littleNumBuckets, mBigFrequenciesStartIndex);
-            mBigNumBuckets = Math.min(
-                    bigNumBuckets, frequencies.length - mBigFrequenciesStartIndex);
+            mBigNumBuckets =
+                    Math.min(bigNumBuckets, frequencies.length - mBigFrequenciesStartIndex);
             mNumBuckets = mLittleNumBuckets + mBigNumBuckets;
 
             // Set the size of each little and big bucket. If they have no buckets, the size is zero
-            mLittleBucketSize = mLittleNumBuckets == 0 ? 0 :
-                    mBigFrequenciesStartIndex / mLittleNumBuckets;
-            mBigBucketSize = mBigNumBuckets == 0 ? 0 :
-                    (frequencies.length - mBigFrequenciesStartIndex) / mBigNumBuckets;
+            mLittleBucketSize =
+                    mLittleNumBuckets == 0 ? 0 : mBigFrequenciesStartIndex / mLittleNumBuckets;
+            mBigBucketSize =
+                    mBigNumBuckets == 0
+                            ? 0
+                            : (frequencies.length - mBigFrequenciesStartIndex) / mBigNumBuckets;
         }
 
-        /**
-         * Find the index where frequencies change from little core to big core
-         */
+        /** Find the index where frequencies change from little core to big core */
         @VisibleForTesting
         public static int getBigFrequenciesStartIndex(long[] frequenciesKhz) {
             for (int i = 0; i < frequenciesKhz.length - 1; i++) {
@@ -527,16 +485,14 @@
             return frequenciesKhz.length;
         }
 
-        /**
-         * Get the minimum frequency in each bucket
-         */
+        /** Get the minimum frequency in each bucket */
         @VisibleForTesting
         public int[] getBucketMinFrequencies(long[] frequenciesKhz) {
             Preconditions.checkArgument(frequenciesKhz.length == mNumFrequencies);
             // If there's only one bucket, we bucket everything together so the first bucket is the
             // min frequency
             if (mNumBuckets == 1) {
-                return new int[]{(int) frequenciesKhz[0]};
+                return new int[] {(int) frequenciesKhz[0]};
             }
 
             final int[] bucketMinFrequencies = new int[mNumBuckets];
@@ -561,6 +517,7 @@
          * @return the bucketed usage times
          */
         @VisibleForTesting
+        @SuppressWarnings("ForLoopReplaceableByForEach")
         public int[] getBucketedValues(long[] values) {
             Preconditions.checkArgument(values.length == mNumFrequencies);
             final int[] bucketed = new int[mNumBuckets];
@@ -580,18 +537,18 @@
             }
             // Initialize the big buckets
             for (int i = mBigFrequenciesStartIndex; i < values.length; i++) {
-                final int bucketIndex = Math.min(
-                        mLittleNumBuckets + (i - mBigFrequenciesStartIndex) / mBigBucketSize,
-                        mNumBuckets - 1);
+                final int bucketIndex =
+                        Math.min(
+                                mLittleNumBuckets
+                                        + (i - mBigFrequenciesStartIndex) / mBigBucketSize,
+                                mNumBuckets - 1);
                 bucketed[bucketIndex] += values[i];
             }
             return bucketed;
         }
     }
 
-    /**
-     * CPU usage of a process
-     */
+    /** CPU usage of a process */
     public static class ProcessCpuUsage {
         public final int processId;
         public final String processName;
@@ -610,46 +567,23 @@
         }
     }
 
-    /**
-     * CPU usage of a thread
-     */
+    /** CPU usage of a thread */
     public static class ThreadCpuUsage {
         public final int threadId;
         public final String threadName;
         public final int[] usageTimesMillis;
 
-        ThreadCpuUsage(
-                int threadId,
-                String threadName,
-                int[] usageTimesMillis) {
+        ThreadCpuUsage(int threadId, String threadName, int[] usageTimesMillis) {
             this.threadId = threadId;
             this.threadName = threadName;
             this.usageTimesMillis = usageTimesMillis;
         }
     }
 
-    /**
-     * Used to inject static methods from {@link Process}
-     */
+    /** Used to inject static methods from {@link Process} */
     @VisibleForTesting
     public static class Injector {
-        /**
-         * Get the PID of the current process
-         */
-        public int myPid() {
-            return Process.myPid();
-        }
-
-        /**
-         * Get the UID that owns the current process
-         */
-        public int myUid() {
-            return Process.myUid();
-        }
-
-        /**
-         * Get the UID for the process with ID {@code pid}
-         */
+        /** Get the UID for the process with ID {@code pid} */
         public int getUidForPid(int pid) {
             return Process.getUidForPid(pid);
         }
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
index b8dfe0d..3851ce6 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReaderSettingsObserver.java
@@ -47,33 +47,29 @@
 public class KernelCpuThreadReaderSettingsObserver extends ContentObserver {
     private static final String TAG = "KernelCpuThreadReaderSettingsObserver";
 
-    /**
-     * The number of frequency buckets to report
-     */
+    /** The number of frequency buckets to report */
     private static final String NUM_BUCKETS_SETTINGS_KEY = "num_buckets";
+
     private static final int NUM_BUCKETS_DEFAULT = 8;
 
-    /**
-     * List of UIDs to report data for
-     */
+    /** List of UIDs to report data for */
     private static final String COLLECTED_UIDS_SETTINGS_KEY = "collected_uids";
+
     private static final String COLLECTED_UIDS_DEFAULT = "0-0;1000-1000";
 
-    /**
-     * Minimum total CPU usage to report
-     */
+    /** Minimum total CPU usage to report */
     private static final String MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY =
             "minimum_total_cpu_usage_millis";
+
     private static final int MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT = 10000;
 
     private final Context mContext;
 
-    @Nullable
-    private final KernelCpuThreadReader mKernelCpuThreadReader;
+    @Nullable private final KernelCpuThreadReader mKernelCpuThreadReader;
 
     /**
-     * @return returns a created {@link KernelCpuThreadReader} that will be modified by any
-     * change in settings, returns null if creation failed
+     * @return returns a created {@link KernelCpuThreadReader} that will be modified by any change
+     *     in settings, returns null if creation failed
      */
     @Nullable
     public static KernelCpuThreadReader getSettingsModifiedReader(Context context) {
@@ -81,10 +77,10 @@
         KernelCpuThreadReaderSettingsObserver settingsObserver =
                 new KernelCpuThreadReaderSettingsObserver(context);
         // Register the observer to listen for setting changes
-        Uri settingsUri =
-                Settings.Global.getUriFor(Settings.Global.KERNEL_CPU_THREAD_READER);
-        context.getContentResolver().registerContentObserver(
-                settingsUri, false, settingsObserver, UserHandle.USER_SYSTEM);
+        Uri settingsUri = Settings.Global.getUriFor(Settings.Global.KERNEL_CPU_THREAD_READER);
+        context.getContentResolver()
+                .registerContentObserver(
+                        settingsUri, false, settingsObserver, UserHandle.USER_SYSTEM);
         // Return the observer's reader
         return settingsObserver.mKernelCpuThreadReader;
     }
@@ -92,10 +88,11 @@
     private KernelCpuThreadReaderSettingsObserver(Context context) {
         super(BackgroundThread.getHandler());
         mContext = context;
-        mKernelCpuThreadReader = KernelCpuThreadReader.create(
-                NUM_BUCKETS_DEFAULT,
-                UidPredicate.fromString(COLLECTED_UIDS_DEFAULT),
-                MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT);
+        mKernelCpuThreadReader =
+                KernelCpuThreadReader.create(
+                        NUM_BUCKETS_DEFAULT,
+                        UidPredicate.fromString(COLLECTED_UIDS_DEFAULT),
+                        MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT);
     }
 
     @Override
@@ -103,9 +100,7 @@
         updateReader();
     }
 
-    /**
-     * Update the reader with new settings
-     */
+    /** Update the reader with new settings */
     private void updateReader() {
         if (mKernelCpuThreadReader == null) {
             return;
@@ -113,8 +108,10 @@
 
         final KeyValueListParser parser = new KeyValueListParser(',');
         try {
-            parser.setString(Settings.Global.getString(
-                    mContext.getContentResolver(), Settings.Global.KERNEL_CPU_THREAD_READER));
+            parser.setString(
+                    Settings.Global.getString(
+                            mContext.getContentResolver(),
+                            Settings.Global.KERNEL_CPU_THREAD_READER));
         } catch (IllegalArgumentException e) {
             Slog.e(TAG, "Bad settings", e);
             return;
@@ -122,8 +119,9 @@
 
         final UidPredicate uidPredicate;
         try {
-            uidPredicate = UidPredicate.fromString(
-                    parser.getString(COLLECTED_UIDS_SETTINGS_KEY, COLLECTED_UIDS_DEFAULT));
+            uidPredicate =
+                    UidPredicate.fromString(
+                            parser.getString(COLLECTED_UIDS_SETTINGS_KEY, COLLECTED_UIDS_DEFAULT));
         } catch (NumberFormatException e) {
             Slog.w(TAG, "Failed to get UID predicate", e);
             return;
@@ -132,14 +130,13 @@
         mKernelCpuThreadReader.setNumBuckets(
                 parser.getInt(NUM_BUCKETS_SETTINGS_KEY, NUM_BUCKETS_DEFAULT));
         mKernelCpuThreadReader.setUidPredicate(uidPredicate);
-        mKernelCpuThreadReader.setMinimumTotalCpuUsageMillis(parser.getInt(
-                MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY,
-                MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT));
+        mKernelCpuThreadReader.setMinimumTotalCpuUsageMillis(
+                parser.getInt(
+                        MINIMUM_TOTAL_CPU_USAGE_MILLIS_SETTINGS_KEY,
+                        MINIMUM_TOTAL_CPU_USAGE_MILLIS_DEFAULT));
     }
 
-    /**
-     * Check whether a UID belongs to a set of UIDs
-     */
+    /** Check whether a UID belongs to a set of UIDs */
     @VisibleForTesting
     public static class UidPredicate implements Predicate<Integer> {
         private static final Pattern UID_RANGE_PATTERN = Pattern.compile("([0-9]+)-([0-9]+)");
@@ -150,14 +147,14 @@
          * Create a UID predicate from a string representing a list of UID ranges
          *
          * <p>UID ranges are a pair of integers separated by a '-'. If you want to specify a single
-         * UID (e.g. UID 1000), you can use {@code 1000-1000}. Lists of ranges are separated by
-         * a single ';'. For example, this would be a valid string representation: {@code
+         * UID (e.g. UID 1000), you can use {@code 1000-1000}. Lists of ranges are separated by a
+         * single ';'. For example, this would be a valid string representation: {@code
          * "1000-1999;2003-2003;2004-2004;2050-2060"}.
          *
          * <p>We do not use ',' to delimit as it is already used in separating different setting
          * arguments.
          *
-         * @throws NumberFormatException    if the input string is incorrectly formatted
+         * @throws NumberFormatException if the input string is incorrectly formatted
          * @throws IllegalArgumentException if an UID range has a lower end than start
          */
         @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
@@ -169,9 +166,10 @@
                     throw new NumberFormatException(
                             "Failed to recognize as number range: " + uidSpecifier);
                 }
-                acceptedUidRanges.add(Range.create(
-                        Integer.parseInt(uidRangeMatcher.group(1)),
-                        Integer.parseInt(uidRangeMatcher.group(2))));
+                acceptedUidRanges.add(
+                        Range.create(
+                                Integer.parseInt(uidRangeMatcher.group(1)),
+                                Integer.parseInt(uidRangeMatcher.group(2))));
             }
             return new UidPredicate(acceptedUidRanges);
         }
@@ -181,6 +179,7 @@
         }
 
         @Override
+        @SuppressWarnings("ForLoopReplaceableByForEach")
         public boolean test(Integer uid) {
             for (int i = 0; i < mAcceptedUidRanges.size(); i++) {
                 if (mAcceptedUidRanges.get(i).contains(uid)) {
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index b4be3f5..0c53fb2 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2319,4 +2319,10 @@
 
     // Settings > Display > Theme
     DARK_UI_SETTINGS = 1698;
+
+    // Settings > global bubble settings
+    BUBBLE_SETTINGS = 1699;
+
+    // Settings > app > bubble settings
+    APP_BUBBLE_SETTINGS = 1700;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index cb8ece7..8bfa038 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4426,6 +4426,13 @@
     <permission android:name="android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE"
         android:protectionLevel="signature" />
 
+    <!-- Must be required by an {@link android.service.watchdog.ExplicitHealthCheckService} to
+         ensure that only the system can bind to it.
+         @hide This is not a third-party API (intended for OEMs and system apps).
+    -->
+    <permission android:name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @hide Permission that allows configuring appops.
      <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.MANAGE_APPOPS"
diff --git a/core/tests/coretests/res/raw/com_android_tzdata.apex b/core/tests/coretests/res/raw/com_android_tzdata.apex
index 72294de..06ea8fa 100644
--- a/core/tests/coretests/res/raw/com_android_tzdata.apex
+++ b/core/tests/coretests/res/raw/com_android_tzdata.apex
Binary files differ
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
index aa0e0cd..0798c0c 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -500,18 +500,30 @@
         File apexFile = copyRawResourceToFile("com.android.tzdata.apex",
                 R.raw.com_android_tzdata);
         PackageInfo pi = PackageParser.generatePackageInfoFromApex(apexFile, false);
-        assertEquals("com.google.android.tzdata", pi.packageName);
         assertEquals("com.google.android.tzdata", pi.applicationInfo.packageName);
-        assertEquals(1, pi.getLongVersionCode());
+        assertTrue(pi.applicationInfo.enabled);
+        assertEquals(28, pi.applicationInfo.targetSdkVersion);
         assertEquals(1, pi.applicationInfo.longVersionCode);
+
+        assertEquals("com.google.android.tzdata", pi.packageName);
+        assertTrue(pi.splitNames.length > 0);
+        assertEquals(1, pi.getLongVersionCode());
         assertNull(pi.signingInfo);
+        assertNull(pi.signatures);
+        assertTrue(pi.isApex);
 
         pi = PackageParser.generatePackageInfoFromApex(apexFile, true);
-        assertEquals("com.google.android.tzdata", pi.packageName);
         assertEquals("com.google.android.tzdata", pi.applicationInfo.packageName);
-        assertEquals(1, pi.getLongVersionCode());
+        assertTrue(pi.applicationInfo.enabled);
+        assertEquals(28, pi.applicationInfo.targetSdkVersion);
         assertEquals(1, pi.applicationInfo.longVersionCode);
+
+        assertEquals("com.google.android.tzdata", pi.packageName);
+        assertTrue(pi.splitNames.length > 0);
+        assertEquals(1, pi.getLongVersionCode());
         assertNotNull(pi.signingInfo);
+        assertNotNull(pi.signatures);
         assertTrue(pi.signingInfo.getApkContentsSigners().length > 0);
+        assertTrue(pi.isApex);
     }
 }
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index 1cb71f9..1a22a70 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -45,7 +45,6 @@
 
 import java.util.Arrays;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
 
 @LargeTest
 @RunWith(AndroidJUnit4.class)
@@ -61,16 +60,12 @@
 
     AccessibilityCache mAccessibilityCache;
     AccessibilityCache.AccessibilityNodeRefresher mAccessibilityNodeRefresher;
-    AtomicInteger mNumA11yNodeInfosInUse = new AtomicInteger(0);
-    AtomicInteger mNumA11yWinInfosInUse = new AtomicInteger(0);
 
     @Before
     public void setUp() {
         mAccessibilityNodeRefresher = mock(AccessibilityCache.AccessibilityNodeRefresher.class);
         when(mAccessibilityNodeRefresher.refreshNode(anyObject(), anyBoolean())).thenReturn(true);
         mAccessibilityCache = new AccessibilityCache(mAccessibilityNodeRefresher);
-        AccessibilityNodeInfo.setNumInstancesInUseCounter(mNumA11yNodeInfosInUse);
-        AccessibilityWindowInfo.setNumInstancesInUseCounter(mNumA11yWinInfosInUse);
     }
 
     @After
@@ -78,8 +73,6 @@
         // Make sure we're recycling all of our window and node infos
         mAccessibilityCache.clear();
         AccessibilityInteractionClient.getInstance().clearCache();
-        assertEquals(0, mNumA11yWinInfosInUse.get());
-        assertEquals(0, mNumA11yNodeInfosInUse.get());
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
index 1c84829..e9cad0a 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
@@ -35,6 +35,7 @@
 import java.time.Duration;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Optional;
 import java.util.OptionalDouble;
 import java.util.concurrent.CountDownLatch;
 import java.util.function.Consumer;
@@ -126,17 +127,21 @@
         final KernelCpuThreadReader kernelCpuThreadReader =
                 KernelCpuThreadReader.create(8, uid -> uid == Process.myUid(), 0);
         assertNotNull(kernelCpuThreadReader);
-        final ProcessCpuUsage currentProcessCpuUsage =
-                kernelCpuThreadReader.getCurrentProcessCpuUsage();
+        kernelCpuThreadReader.setUidPredicate(uid -> uid == Process.myUid());
+        final Optional<ProcessCpuUsage> currentProcessCpuUsage =
+                kernelCpuThreadReader.getProcessCpuUsage().stream()
+                        .filter(p -> p.processId == Process.myPid())
+                        .findFirst();
+        assertTrue(currentProcessCpuUsage.isPresent());
 
         // Threads can terminate, as we've finished crawling them from /proc
         threadFinishedLatch.countDown();
 
         // Check that we've got times for every thread we spawned
-        final List<ThreadCpuUsage> threadCpuUsages = currentProcessCpuUsage.threadCpuUsages
-                .stream()
-                .filter((thread) -> thread.threadName.startsWith(tag))
-                .collect(Collectors.toList());
+        final List<ThreadCpuUsage> threadCpuUsages =
+                currentProcessCpuUsage.get().threadCpuUsages.stream()
+                        .filter((thread) -> thread.threadName.startsWith(tag))
+                        .collect(Collectors.toList());
         assertEquals(
                 "Incorrect number of threads returned by KernelCpuThreadReader",
                 numSamples, threadCpuUsages.size());
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
index e6e7a85..61209e2 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderTest.java
@@ -25,6 +25,7 @@
 
 import android.content.Context;
 import android.os.FileUtils;
+import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -44,27 +45,10 @@
 import java.util.Comparator;
 import java.util.function.Predicate;
 
+@Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class KernelCpuThreadReaderTest {
-
-    private static final int UID = 1000;
-    private static final int PROCESS_ID = 1234;
-    private static final int[] THREAD_IDS = {0, 1000, 1235, 4321};
-    private static final String PROCESS_NAME = "test_process";
-    private static final String[] THREAD_NAMES = {
-            "test_thread_1", "test_thread_2", "test_thread_3", "test_thread_4"
-    };
-    private static final int[] THREAD_CPU_FREQUENCIES = {
-            1000, 2000, 3000, 4000,
-    };
-    private static final int[][] THREAD_CPU_TIMES = {
-            {1, 0, 0, 1},
-            {0, 0, 0, 0},
-            {1000, 1000, 1000, 1000},
-            {0, 1, 2, 3},
-    };
-
     private File mProcDirectory;
 
     @Before
@@ -79,41 +63,6 @@
     }
 
     @Test
-    public void testReader_currentProcess() throws IOException {
-        KernelCpuThreadReader.Injector processUtils =
-                new KernelCpuThreadReader.Injector() {
-                    @Override
-                    public int myPid() {
-                        return PROCESS_ID;
-                    }
-
-                    @Override
-                    public int myUid() {
-                        return UID;
-                    }
-
-                    @Override
-                    public int getUidForPid(int pid) {
-                        return 0;
-                    }
-                };
-        setupDirectory(mProcDirectory.toPath().resolve("self"), THREAD_IDS, PROCESS_NAME,
-                THREAD_NAMES, THREAD_CPU_FREQUENCIES, THREAD_CPU_TIMES);
-
-        final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
-                8,
-                uid -> 1000 <= uid && uid < 2000,
-                0,
-                mProcDirectory.toPath(),
-                mProcDirectory.toPath().resolve("self/task/" + THREAD_IDS[0] + "/time_in_state"),
-                processUtils);
-        final KernelCpuThreadReader.ProcessCpuUsage processCpuUsage =
-                kernelCpuThreadReader.getCurrentProcessCpuUsage();
-        checkResults(processCpuUsage, kernelCpuThreadReader.getCpuFrequenciesKhz(), UID, PROCESS_ID,
-                THREAD_IDS, PROCESS_NAME, THREAD_NAMES, THREAD_CPU_FREQUENCIES, THREAD_CPU_TIMES);
-    }
-
-    @Test
     public void testReader_byUids() throws IOException {
         int[] uids = new int[]{0, 2, 3, 4, 5, 6000};
         Predicate<Integer> uidPredicate = uid -> uid == 0 || uid >= 4;
@@ -121,16 +70,6 @@
         KernelCpuThreadReader.Injector processUtils =
                 new KernelCpuThreadReader.Injector() {
                     @Override
-                    public int myPid() {
-                        return 0;
-                    }
-
-                    @Override
-                    public int myUid() {
-                        return 0;
-                    }
-
-                    @Override
                     public int getUidForPid(int pid) {
                         return pid;
                     }
@@ -150,7 +89,7 @@
                 mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"),
                 processUtils);
         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsageByUids =
-                kernelCpuThreadReader.getProcessCpuUsageByUids();
+                kernelCpuThreadReader.getProcessCpuUsage();
         processCpuUsageByUids.sort(Comparator.comparing(usage -> usage.processId));
 
         assertEquals(expectedUids.length, processCpuUsageByUids.size());
@@ -173,16 +112,6 @@
         KernelCpuThreadReader.Injector processUtils =
                 new KernelCpuThreadReader.Injector() {
                     @Override
-                    public int myPid() {
-                        return 0;
-                    }
-
-                    @Override
-                    public int myUid() {
-                        return 0;
-                    }
-
-                    @Override
                     public int getUidForPid(int pid) {
                         return pid;
                     }
@@ -206,7 +135,7 @@
                 mProcDirectory.toPath().resolve(uids[0] + "/task/" + uids[0] + "/time_in_state"),
                 processUtils);
         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsageByUids =
-                kernelCpuThreadReader.getProcessCpuUsageByUids();
+                kernelCpuThreadReader.getProcessCpuUsage();
         processCpuUsageByUids.sort(Comparator.comparing(usage -> usage.uid));
 
         assertEquals(expectedUids.length, processCpuUsageByUids.size());
@@ -220,7 +149,7 @@
 
     @Test
     public void testReader_otherThreads() throws IOException {
-        final Path processPath = mProcDirectory.toPath().resolve("self");
+        final Path processPath = mProcDirectory.toPath().resolve("1000");
         setupDirectory(
                 processPath,
                 new int[]{1, 2, 3},
@@ -228,39 +157,34 @@
                 new String[]{"thread1", "thread2", "thread3"},
                 new int[]{1000, 2000},
                 new int[][]{{0, 100}, {10, 0}, {0, 300}});
-        final KernelCpuThreadReader kernelCpuThreadReader = new KernelCpuThreadReader(
-                8,
-                i -> true,
-                2000,
-                mProcDirectory.toPath(),
-                processPath.resolve("task/1/time_in_state"),
+        KernelCpuThreadReader.Injector injector =
                 new KernelCpuThreadReader.Injector() {
                     @Override
-                    public int myPid() {
-                        return 1000;
-                    }
-
-                    @Override
-                    public int myUid() {
-                        return 0;
-                    }
-
-                    @Override
                     public int getUidForPid(int pid) {
                         return 0;
                     }
-                });
+                };
+        final KernelCpuThreadReader kernelCpuThreadReader =
+                new KernelCpuThreadReader(
+                        8,
+                        uid -> true,
+                        2000,
+                        mProcDirectory.toPath(),
+                        processPath.resolve("task/1/time_in_state"),
+                        injector);
+        ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages =
+                kernelCpuThreadReader.getProcessCpuUsage();
+        assertEquals(1, processCpuUsages.size());
         checkResults(
-                kernelCpuThreadReader.getCurrentProcessCpuUsage(),
+                processCpuUsages.get(0),
                 kernelCpuThreadReader.getCpuFrequenciesKhz(),
                 0,
                 1000,
-                new int[]{-1, 3},
+                new int[] {-1, 3},
                 "process",
-                new String[]{"__OTHER_THREADS", "thread3"},
-                new int[]{1000, 2000},
-                new int[][]{{100, 1000}, {0, 3000}}
-        );
+                new String[] {"__OTHER_THREADS", "thread3"},
+                new int[] {1000, 2000},
+                new int[][] {{10, 100}, {0, 300}});
     }
 
     private void setupDirectory(Path processPath, int[] threadIds, String processName,
@@ -289,8 +213,7 @@
             final OutputStream timeInStateStream =
                     Files.newOutputStream(threadPath.resolve("time_in_state"));
             for (int j = 0; j < cpuFrequencies.length; j++) {
-                final String line = String.valueOf(cpuFrequencies[j]) + " "
-                        + String.valueOf(cpuTimes[i][j]) + "\n";
+                final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n";
                 timeInStateStream.write(line.getBytes());
             }
             timeInStateStream.close();
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index bb47658..3968f84 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -66,6 +66,14 @@
 }
 
 prebuilt_etc {
+    name: "privapp_whitelist_com.android.emergency",
+    product_specific: true,
+    sub_dir: "permissions",
+    src: "com.android.emergency.xml",
+    filename_from_src: true,
+}
+
+prebuilt_etc {
     name: "privapp_whitelist_com.android.launcher3",
     product_specific: true,
     sub_dir: "permissions",
diff --git a/data/etc/com.android.emergency.xml b/data/etc/com.android.emergency.xml
new file mode 100644
index 0000000..28f99dd
--- /dev/null
+++ b/data/etc/com.android.emergency.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<permissions>
+    <privapp-permissions package="com.android.emergency">
+        <!-- Required to place emergency calls from emergency info screen. -->
+        <permission name="android.permission.CALL_PRIVILEGED"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index afb5071..347edc5 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -50,12 +50,6 @@
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
     </privapp-permissions>
 
-    <privapp-permissions package="com.android.emergency">
-        <!-- Required to place emergency calls from emergency info screen. -->
-        <permission name="android.permission.CALL_PRIVILEGED"/>
-        <permission name="android.permission.MANAGE_USERS"/>
-    </privapp-permissions>
-
     <privapp-permissions package="com.android.externalstorage">
         <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
         <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
@@ -124,6 +118,7 @@
         <permission name="android.permission.REQUEST_INCIDENT_REPORT_APPROVAL"/>
         <permission name="android.permission.APPROVE_INCIDENT_REPORTS"/>
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+        <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.phone">
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 4bd344f..170dec2 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1813,21 +1813,22 @@
     }
 
     /**
-     * Fills the bitmap's pixels with the specified {@link Color}.
+     * Fills the bitmap's pixels with the specified {@code ColorLong}.
      *
+     * @param color The color to fill as packed by the {@link Color} class.
      * @throws IllegalStateException if the bitmap is not mutable.
-     * @throws IllegalArgumentException if the color space encoded in the long
-     *                                  is invalid or unknown.
+     * @throws IllegalArgumentException if the color space encoded in the
+     *                                  {@code ColorLong} is invalid or unknown.
      *
      */
-    public void eraseColor(@ColorLong long c) {
+    public void eraseColor(@ColorLong long color) {
         checkRecycled("Can't erase a recycled bitmap");
         if (!isMutable()) {
             throw new IllegalStateException("cannot erase immutable bitmaps");
         }
 
-        ColorSpace cs = Color.colorSpace(c);
-        nativeErase(mNativePtr, cs.getNativeInstance(), c);
+        ColorSpace cs = Color.colorSpace(color);
+        nativeErase(mNativePtr, cs.getNativeInstance(), color);
     }
 
     /**
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 7b3f3da..df64204 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1683,8 +1683,9 @@
      * Fill the entire canvas' bitmap (restricted to the current clip) with the specified color,
      * using srcover porterduff mode.
      *
-     * @param color the color to draw onto the canvas
-     * @throws IllegalArgumentException if the color space encoded in the long
+     * @param color the {@code ColorLong} to draw onto the canvas. See the {@link Color}
+     *              class for details about {@code ColorLong}s.
+     * @throws IllegalArgumentException if the color space encoded in the {@code ColorLong}
      *                                  is invalid or unknown.
      */
     public void drawColor(@ColorLong long color) {
@@ -1695,7 +1696,7 @@
      * Fill the entire canvas' bitmap (restricted to the current clip) with the specified color and
      * porter-duff xfermode.
      *
-     * @param color the color to draw with
+     * @param color the color to draw onto the canvas
      * @param mode the porter-duff mode to apply to the color
      *
      * @deprecated use {@link #drawColor(int, BlendMode)} instead
@@ -1709,7 +1710,7 @@
      * Fill the entire canvas' bitmap (restricted to the current clip) with the specified color and
      * blendmode.
      *
-     * @param color the color to draw with
+     * @param color the color to draw onto the canvas
      * @param mode the blendmode to apply to the color
      */
     public void drawColor(@ColorInt int color, @NonNull BlendMode mode) {
@@ -1720,9 +1721,10 @@
      * Fill the entire canvas' bitmap (restricted to the current clip) with the specified color and
      * blendmode.
      *
-     * @param color the color to draw with
+     * @param color the {@code ColorLong} to draw onto the canvas. See the {@link Color}
+     *              class for details about {@code ColorLong}s.
      * @param mode the blendmode to apply to the color
-     * @throws IllegalArgumentException if the color space encoded in the long
+     * @throws IllegalArgumentException if the color space encoded in the {@code ColorLong}
      *                                  is invalid or unknown.
      */
     public void drawColor(@ColorLong long color, @NonNull BlendMode mode) {
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index c485461..db5f065 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1029,9 +1029,8 @@
      * premultiplied, meaning that alpha can be any value, regardless of the
      * values of r,g,b. See the {@link Color} class for more details.
      *
-     * @see Color for APIs that help manipulate a color long.
-     *
-     * @return the paint's color (and alpha).
+     * @return the paint's color, alpha, and {@code ColorSpace} encoded as a
+     *      {@code ColorLong}
      */
     @ColorLong
     public long getColorLong() {
@@ -1052,7 +1051,7 @@
     }
 
     /**
-     * Set the paint's color with a {@link ColorLong}. Note that the color is
+     * Set the paint's color with a {@code ColorLong}. Note that the color is
      * a long with an encoded {@link ColorSpace} as well as alpha and r,g,b.
      * These values are not premultiplied, meaning that alpha can be any value,
      * regardless of the values of r,g,b. See the {@link Color} class for more
@@ -1060,8 +1059,8 @@
      *
      * @param color The new color (including alpha and {@link ColorSpace})
      *      to set in the paint.
-     * @throws IllegalArgumentException if the color space encoded in the long
-     *      is invalid or unknown.
+     * @throws IllegalArgumentException if the color space encoded in the
+     *      {@code ColorLong} is invalid or unknown.
      */
     public void setColor(@ColorLong long color) {
         ColorSpace cs = Color.colorSpace(color);
@@ -1491,8 +1490,8 @@
      * The alpha of the shadow will be the paint's alpha if the shadow color is
      * opaque, or the alpha from the shadow color if not.
      *
-     * @throws IllegalArgumentException if the color space encoded in the long
-     *      is invalid or unknown.
+     * @throws IllegalArgumentException if the color space encoded in the
+     *      {@code ColorLong} is invalid or unknown.
      */
     public void setShadowLayer(float radius, float dx, float dy, @ColorLong long shadowColor) {
         ColorSpace cs = Color.colorSpace(shadowColor);
@@ -1559,8 +1558,11 @@
 
     /**
      * Returns the color of the shadow layer.
+     *
+     * @return the shadow layer's color encoded as a {@link ColorLong}.
      * @see #setShadowLayer(float,float,float,int)
      * @see #setShadowLayer(float,float,float,long)
+     * @see Color
      */
     public @ColorLong long getShadowLayerColorLong() {
         return mShadowLayerColor;
diff --git a/media/java/android/media/IMediaRoute2Callback.aidl b/media/java/android/media/IMediaRoute2Callback.aidl
new file mode 100644
index 0000000..f03c8ab
--- /dev/null
+++ b/media/java/android/media/IMediaRoute2Callback.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright 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.
+ */
+
+package android.media;
+
+/**
+ * @hide
+ */
+oneway interface IMediaRoute2Callback {
+    void onRouteSelected(int uid, String routeId);
+}
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
new file mode 100644
index 0000000..b97dcc5
--- /dev/null
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 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.
+ */
+
+package android.media;
+
+import android.media.IMediaRoute2Callback;
+
+/**
+ * {@hide}
+ */
+oneway interface IMediaRoute2Provider {
+    void setCallback(IMediaRoute2Callback callback);
+    void selectRoute(int uid, String id);
+}
diff --git a/media/java/android/media/IMediaRouter2ManagerClient.aidl b/media/java/android/media/IMediaRouter2ManagerClient.aidl
new file mode 100644
index 0000000..234551b
--- /dev/null
+++ b/media/java/android/media/IMediaRouter2ManagerClient.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 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.
+ */
+
+package android.media;
+
+/**
+ * {@hide}
+ */
+oneway interface IMediaRouter2ManagerClient {
+    void onRouteSelected(int uid, String routeId);
+    void onControlCategoriesChanged(int uid, in List<String> categories);
+}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 3308fc9..59f1d0d 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.media.IMediaRouterClient;
+import android.media.IMediaRouter2ManagerClient;
 import android.media.MediaRouterClientState;
 
 /**
@@ -29,8 +30,15 @@
     MediaRouterClientState getState(IMediaRouterClient client);
     boolean isPlaybackActive(IMediaRouterClient client);
 
+    void setControlCategories(IMediaRouterClient client, in List<String> categories);
     void setDiscoveryRequest(IMediaRouterClient client, int routeTypes, boolean activeScan);
     void setSelectedRoute(IMediaRouterClient client, String routeId, boolean explicit);
     void requestSetVolume(IMediaRouterClient client, String routeId, int volume);
     void requestUpdateVolume(IMediaRouterClient client, String routeId, int direction);
+
+    void registerManagerAsUser(IMediaRouter2ManagerClient callback,
+            String packageName, int userId);
+    void unregisterManager(IMediaRouter2ManagerClient callback);
+    void setRemoteRoute(IMediaRouter2ManagerClient callback,
+            int uid, String routeId, boolean explicit);
 }
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
new file mode 100644
index 0000000..04ddc30
--- /dev/null
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 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.
+ */
+
+package android.media;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public abstract class MediaRoute2ProviderService extends Service {
+    private static final String TAG = "MediaRouteProviderSrv";
+
+    public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService";
+
+    private final Handler mHandler;
+    private ProviderStub mStub;
+    private IMediaRoute2Callback mCallback;
+
+    public MediaRoute2ProviderService() {
+        mHandler = new Handler(Looper.getMainLooper());
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            if (mStub == null) {
+                mStub = new ProviderStub();
+            }
+            return mStub;
+        }
+        return null;
+    }
+
+    /**
+     * Called when selectRoute is called on a route of the provider.
+     *
+     * @param uid The target application uid
+     * @param routeId The id of the target route
+     */
+    public abstract void onSelect(int uid, String routeId);
+
+    /**
+     * Updates provider info from selected route and appliation.
+     *
+     * TODO: When provider descriptor is defined, this should update the descriptor correctly.
+     *
+     * @param uid
+     * @param routeId
+     */
+    public void updateProvider(int uid, String routeId) {
+        if (mCallback != null) {
+            try {
+                //TODO: After publishState() is fully implemented, delete this.
+                mCallback.onRouteSelected(uid, routeId);
+            } catch (RemoteException ex) {
+                Log.d(TAG, "Failed to update provider");
+            }
+        }
+        publishState();
+    }
+
+    void setCallback(IMediaRoute2Callback callback) {
+        mCallback = callback;
+        publishState();
+    }
+
+    void publishState() {
+        //TODO: Send provider descriptor to the MediaRouterService
+    }
+
+    final class ProviderStub extends IMediaRoute2Provider.Stub {
+        ProviderStub() { }
+
+        @Override
+        public void setCallback(IMediaRoute2Callback callback) {
+            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::setCallback,
+                    MediaRoute2ProviderService.this, callback));
+        }
+
+        @Override
+        public void selectRoute(int uid, String id) {
+            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSelect,
+                    MediaRoute2ProviderService.this, uid, id));
+        }
+    }
+}
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 3444e92..5a89d8c 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -347,6 +347,17 @@
             return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
         }
 
+        void setControlCategories(List<String> categories) {
+            if (mClient != null) {
+                try {
+                    mMediaRouterService.setControlCategories(mClient,
+                            categories);
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "Unable to set control categories.", ex);
+                }
+            }
+        }
+
         private void updatePresentationDisplays(int changedDisplayId) {
             final int count = mRoutes.size();
             for (int i = 0; i < count; i++) {
@@ -919,6 +930,25 @@
         return -1;
     }
 
+    //TODO: Remove @hide when it is ready.
+    //TODO: Provide pre-defined categories for app developers.
+    /**
+     * Sets control categories of the client application.
+     * Control categories can be used to filter out media routes
+     * that don't correspond with the client application.
+     * The only routes that match any of the categories will be shown on other applications.
+     *
+     * @hide
+     * @param categories Categories to set
+     */
+    public void setControlCategories(@NonNull List<String> categories) {
+        if (categories == null) {
+            throw new IllegalArgumentException("Categories must not be null");
+        }
+        sStatic.setControlCategories(categories);
+    }
+
+
     /**
      * Select the specified route to use for output of the given media types.
      * <p class="note">
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
new file mode 100644
index 0000000..ac5958e
--- /dev/null
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright 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.
+ */
+
+package android.media;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class MediaRouter2Manager {
+    private static final String TAG = "MediaRouter2Manager";
+    private static final Object sLock = new Object();
+
+    @GuardedBy("sLock")
+    private static MediaRouter2Manager sInstance;
+
+    final String mPackageName;
+
+    private Context mContext;
+    private Client mClient;
+    private final IMediaRouterService mMediaRouterService;
+    final Handler mHandler;
+
+    @GuardedBy("sLock")
+    final ArrayList<CallbackRecord> mCallbacks = new ArrayList<>();
+
+    /**
+     * Gets an instance of media router manager that controls media route of other apps.
+     * @param context
+     * @return
+     */
+    public static MediaRouter2Manager getInstance(@NonNull Context context) {
+        if (context == null) {
+            throw new IllegalArgumentException("context must not be null");
+        }
+        synchronized (sLock) {
+            if (sInstance == null) {
+                sInstance = new MediaRouter2Manager(context);
+            }
+            return sInstance;
+        }
+    }
+
+    private MediaRouter2Manager(Context context) {
+        mContext = context.getApplicationContext();
+        mMediaRouterService = IMediaRouterService.Stub.asInterface(
+                ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
+        mPackageName = mContext.getPackageName();
+        mHandler = new Handler(context.getMainLooper());
+    }
+
+    /**
+     * Registers a callback to listen route info.
+     *
+     * @param executor The executor that runs the callback.
+     * @param callback The callback to add.
+     */
+    public void addCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull Callback callback) {
+
+        if (executor == null) {
+            throw new IllegalArgumentException("executor must not be null");
+        }
+        if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+
+        synchronized (sLock) {
+            final int index = findCallbackRecord(callback);
+            if (index >= 0) {
+                Log.w(TAG, "Ignore adding the same callback twice.");
+                return;
+            }
+            if (mCallbacks.size() == 0) {
+                Client client = new Client();
+                try {
+                    mMediaRouterService.registerManagerAsUser(client, mPackageName,
+                            UserHandle.myUserId());
+                    mClient = client;
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "Unable to register media router manager.", ex);
+                }
+            }
+            mCallbacks.add(new CallbackRecord(executor, callback));
+        }
+    }
+
+    /**
+     * Removes the specified callback.
+     *
+     * @param callback The callback to remove.
+     */
+    public void removeCallback(@NonNull Callback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback must not be null");
+        }
+
+        synchronized (sLock) {
+            final int index = findCallbackRecord(callback);
+            if (index < 0) {
+                Log.w(TAG, "Ignore removing unknown callback. " + callback);
+                return;
+            }
+            mCallbacks.remove(index);
+            if (mCallbacks.size() == 0 && mClient != null) {
+                try {
+                    mMediaRouterService.unregisterManager(mClient);
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "Unable to unregister media router manager", ex);
+                }
+                mClient = null;
+            }
+        }
+    }
+
+    private int findCallbackRecord(Callback callback) {
+        final int count = mCallbacks.size();
+        for (int i = 0; i < count; i++) {
+            if (mCallbacks.get(i).mCallback == callback) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Selects media route for the specified application uid.
+     *
+     * @param uid The uid of the application that should change it's media route.
+     * @param routeId The id of the route to select
+     */
+    public void selectRoute(int uid, String routeId) {
+        if (mClient != null) {
+            try {
+                mMediaRouterService.setRemoteRoute(mClient, uid, routeId, /* explicit= */true);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Unable to select media route", ex);
+            }
+        }
+    }
+
+    /**
+     * Unselects media route for the specified application uid.
+     *
+     * @param uid The uid of the application that should stop routing.
+     */
+    public void unselectRoute(int uid) {
+        if (mClient != null) {
+            try {
+                mMediaRouterService.setRemoteRoute(mClient, uid, null, /* explicit= */ true);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Unable to select media route", ex);
+            }
+        }
+    }
+
+    void notifyRouteSelected(int uid, String routeId) {
+        for (CallbackRecord record : mCallbacks) {
+            record.mExecutor.execute(() -> record.mCallback.onRouteSelected(uid, routeId));
+        }
+    }
+
+    void notifyControlCategoriesChanged(int uid, List<String> categories) {
+        for (CallbackRecord record : mCallbacks) {
+            record.mExecutor.execute(
+                    () -> record.mCallback.onControlCategoriesChanged(uid, categories));
+        }
+    }
+
+    /**
+     * Interface for receiving events about media routing changes.
+     */
+    public abstract static class Callback {
+        /**
+         * Called when a route is selected for some application uid.
+         * @param uid
+         * @param routeId
+         */
+        public abstract void onRouteSelected(int uid, String routeId);
+
+        /**
+         * Called when the control categories of an application is changed.
+         * @param uid the uid of the app that changed control categories
+         * @param categories the changed categories
+         */
+        public abstract void onControlCategoriesChanged(int uid, List<String> categories);
+    }
+
+    final class CallbackRecord {
+        public final Executor mExecutor;
+        public final Callback mCallback;
+
+        CallbackRecord(Executor executor, Callback callback) {
+            mExecutor = executor;
+            mCallback = callback;
+        }
+    }
+
+    class Client extends IMediaRouter2ManagerClient.Stub {
+        @Override
+        public void onRouteSelected(int uid, String routeId) {
+            mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifyRouteSelected,
+                    MediaRouter2Manager.this, uid, routeId));
+        }
+
+        @Override
+        public void onControlCategoriesChanged(int uid, List<String> categories) {
+            mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifyControlCategoriesChanged,
+                    MediaRouter2Manager.this, uid, categories));
+        }
+    }
+}
diff --git a/media/tests/MediaRouteProvider/Android.bp b/media/tests/MediaRouteProvider/Android.bp
new file mode 100644
index 0000000..da42824
--- /dev/null
+++ b/media/tests/MediaRouteProvider/Android.bp
@@ -0,0 +1,18 @@
+android_test {
+    name: "mediarouteprovider",
+
+    srcs: ["**/*.java"],
+
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+
+    static_libs: [
+        "android-support-test",
+        "mockito-target-minus-junit4",
+    ],
+
+    platform_apis: true,
+    certificate: "platform",
+}
\ No newline at end of file
diff --git a/media/tests/MediaRouteProvider/AndroidManifest.xml b/media/tests/MediaRouteProvider/AndroidManifest.xml
new file mode 100644
index 0000000..489a621
--- /dev/null
+++ b/media/tests/MediaRouteProvider/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.mediarouteprovider.example">
+
+    <application android:label="@string/app_name">
+        <uses-library android:name="android.test.runner" />
+        <service android:name=".SampleMediaRoute2ProviderService"
+            android:label="@string/app_name"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.media.MediaRoute2ProviderService" />
+            </intent-filter>
+       </service>
+    </application>
+</manifest>
diff --git a/media/tests/MediaRouteProvider/res/values/strings.xml b/media/tests/MediaRouteProvider/res/values/strings.xml
new file mode 100644
index 0000000..bb97064
--- /dev/null
+++ b/media/tests/MediaRouteProvider/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- name of the app [CHAR LIMIT=25]-->
+    <string name="app_name">SampleMediaRouteProvider</string>
+</resources>
\ No newline at end of file
diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
new file mode 100644
index 0000000..22fbd85
--- /dev/null
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 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.
+ */
+
+package com.android.mediarouteprovider.example;
+
+import android.content.Intent;
+import android.media.MediaRoute2ProviderService;
+import android.os.IBinder;
+
+public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService {
+    @Override
+    public IBinder onBind(Intent intent) {
+        return super.onBind(intent);
+    }
+
+    @Override
+    public void onSelect(int uid, String routeId) {
+        updateProvider(uid, routeId);
+    }
+}
diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp
new file mode 100644
index 0000000..611b25a
--- /dev/null
+++ b/media/tests/MediaRouter/Android.bp
@@ -0,0 +1,18 @@
+android_test {
+    name: "mediaroutertest",
+
+    srcs: ["**/*.java"],
+
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+
+    static_libs: [
+        "android-support-test",
+        "mockito-target-minus-junit4",
+    ],
+
+    platform_apis: true,
+    certificate: "platform",
+}
\ No newline at end of file
diff --git a/media/tests/MediaRouter/AndroidManifest.xml b/media/tests/MediaRouter/AndroidManifest.xml
new file mode 100644
index 0000000..a34a264
--- /dev/null
+++ b/media/tests/MediaRouter/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.mediaroutertest">
+
+    <uses-permission android:name="android.permission.CONTROL_MEDIA_ROUTE" />
+
+    <application android:label="@string/app_name">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.mediaroutertest"
+                     android:label="MediaRouter Tests"/>
+</manifest>
diff --git a/media/tests/MediaRouter/AndroidTest.xml b/media/tests/MediaRouter/AndroidTest.xml
new file mode 100644
index 0000000..1301062
--- /dev/null
+++ b/media/tests/MediaRouter/AndroidTest.xml
@@ -0,0 +1,16 @@
+<configuration description="Runs sample instrumentation test.">
+    <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="mediaroutertest.apk"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-tag" value="MediaRouterTest"/>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.mediaroutertest"/>
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/media/tests/MediaRouter/res/values/strings.xml b/media/tests/MediaRouter/res/values/strings.xml
new file mode 100644
index 0000000..0737020
--- /dev/null
+++ b/media/tests/MediaRouter/res/values/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- name of the app [CHAR LIMIT=25]-->
+    <string name="app_name">mediaRouterTest</string>
+</resources>
\ No newline at end of file
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
new file mode 100644
index 0000000..a4bde65
--- /dev/null
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 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.
+ */
+
+package com.android.mediaroutertest;
+
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.media.MediaRouter;
+import android.media.MediaRouter2Manager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MediaRouterManagerTest {
+    private static final String TAG = "MediaRouterManagerTest";
+
+    private static final int TARGET_UID = 109992;
+    private static final String ROUTE_1 = "MediaRoute1";
+
+    private static final int AWAIT_MS = 1000;
+    private static final int TIMEOUT_MS = 1000;
+
+    private Context mContext;
+    private MediaRouter2Manager mManager;
+    private MediaRouter mRouter;
+    private Executor mExecutor;
+
+    private static final List<String> TEST_CONTROL_CATEGORIES = new ArrayList();
+    private static final String CONTROL_CATEGORY_1 = "android.media.mediarouter.MEDIA1";
+    private static final String CONTROL_CATEGORY_2 = "android.media.mediarouter.MEDIA2";
+    static {
+        TEST_CONTROL_CATEGORIES.add(CONTROL_CATEGORY_1);
+        TEST_CONTROL_CATEGORIES.add(CONTROL_CATEGORY_2);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mManager = MediaRouter2Manager.getInstance(mContext);
+        mRouter = (MediaRouter) mContext.getSystemService(Context.MEDIA_ROUTER_SERVICE);
+        mExecutor = new ThreadPoolExecutor(
+            1, 20, 3, TimeUnit.SECONDS,
+            new SynchronousQueue<Runnable>());
+    }
+
+    @Test
+    public void transferTest() throws Exception {
+        MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class);
+
+        mManager.addCallback(mExecutor, mockCallback);
+
+        verify(mockCallback, after(AWAIT_MS).never())
+            .onRouteSelected(eq(TARGET_UID), any(String.class));
+
+        mManager.selectRoute(TARGET_UID, ROUTE_1);
+        verify(mockCallback, timeout(TIMEOUT_MS)).onRouteSelected(TARGET_UID, ROUTE_1);
+
+        mManager.removeCallback(mockCallback);
+    }
+
+    @Test
+    public void controlCategoryTest() throws Exception {
+        final int uid = android.os.Process.myUid();
+
+        MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class);
+        mManager.addCallback(mExecutor, mockCallback);
+
+        verify(mockCallback, after(AWAIT_MS).never()).onControlCategoriesChanged(eq(uid),
+                any(List.class));
+
+        mRouter.setControlCategories(TEST_CONTROL_CATEGORIES);
+        verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+            .onControlCategoriesChanged(uid, TEST_CONTROL_CATEGORIES);
+
+        mManager.removeCallback(mockCallback);
+    }
+
+}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java
index df2c571..e479e38 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/DynamicSystemInstallationService.java
@@ -49,6 +49,7 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Messenger;
+import android.os.ParcelableException;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.image.DynamicSystemClient;
@@ -170,13 +171,13 @@
     @Override
     public void onProgressUpdate(long installedSize) {
         mInstalledSize = installedSize;
-        postStatus(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED);
+        postStatus(STATUS_IN_PROGRESS, CAUSE_NOT_SPECIFIED, null);
     }
 
     @Override
-    public void onResult(int result) {
+    public void onResult(int result, Throwable detail) {
         if (result == RESULT_OK) {
-            postStatus(STATUS_READY, CAUSE_INSTALL_COMPLETED);
+            postStatus(STATUS_READY, CAUSE_INSTALL_COMPLETED, null);
             return;
         }
 
@@ -185,15 +186,15 @@
 
         switch (result) {
             case RESULT_ERROR_IO:
-                postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_IO);
+                postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_IO, detail);
                 break;
 
             case RESULT_ERROR_INVALID_URL:
-                postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_INVALID_URL);
+                postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_INVALID_URL, detail);
                 break;
 
             case RESULT_ERROR_EXCEPTION:
-                postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_EXCEPTION);
+                postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_EXCEPTION, detail);
                 break;
         }
     }
@@ -201,7 +202,7 @@
     @Override
     public void onCancelled() {
         resetTaskAndStop();
-        postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED);
+        postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
     }
 
     private void executeInstallCommand(Intent intent) {
@@ -266,7 +267,7 @@
                 Toast.LENGTH_LONG).show();
 
         resetTaskAndStop();
-        postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED);
+        postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null);
 
         mDynSystem.remove();
     }
@@ -414,7 +415,7 @@
         return VerificationActivity.isVerified(url);
     }
 
-    private void postStatus(int status, int cause) {
+    private void postStatus(int status, int cause, Throwable detail) {
         Log.d(TAG, "postStatus(): statusCode=" + status + ", causeCode=" + cause);
 
         boolean notifyOnNotificationBar = true;
@@ -433,18 +434,24 @@
 
         for (int i = mClients.size() - 1; i >= 0; i--) {
             try {
-                notifyOneClient(mClients.get(i), status, cause);
+                notifyOneClient(mClients.get(i), status, cause, detail);
             } catch (RemoteException e) {
                 mClients.remove(i);
             }
         }
     }
 
-    private void notifyOneClient(Messenger client, int status, int cause) throws RemoteException {
+    private void notifyOneClient(Messenger client, int status, int cause, Throwable detail)
+            throws RemoteException {
         Bundle bundle = new Bundle();
 
         bundle.putLong(DynamicSystemClient.KEY_INSTALLED_SIZE, mInstalledSize);
 
+        if (detail != null) {
+            bundle.putSerializable(DynamicSystemClient.KEY_EXCEPTION_DETAIL,
+                    new ParcelableException(detail));
+        }
+
         client.send(Message.obtain(null,
                   DynamicSystemClient.MSG_POST_STATUS, status, cause, bundle));
     }
@@ -496,7 +503,7 @@
                     int status = getStatus();
 
                     // tell just registered client my status, but do not specify cause
-                    notifyOneClient(client, status, CAUSE_NOT_SPECIFIED);
+                    notifyOneClient(client, status, CAUSE_NOT_SPECIFIED, null);
 
                     mClients.add(client);
                 } catch (RemoteException e) {
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java
index 052fc0a..aee5de5 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynandroid/InstallationAsyncTask.java
@@ -31,7 +31,7 @@
 import java.util.zip.GZIPInputStream;
 
 
-class InstallationAsyncTask extends AsyncTask<String, Long, Integer> {
+class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
 
     private static final String TAG = "InstallationAsyncTask";
 
@@ -43,7 +43,6 @@
         }
     }
 
-
     /** Not completed, including being cancelled */
     static final int NO_RESULT = 0;
     static final int RESULT_OK = 1;
@@ -53,7 +52,7 @@
 
     interface InstallStatusListener {
         void onProgressUpdate(long installedSize);
-        void onResult(int resultCode);
+        void onResult(int resultCode, Throwable detail);
         void onCancelled();
     }
 
@@ -84,7 +83,7 @@
     }
 
     @Override
-    protected Integer doInBackground(String... voids) {
+    protected Throwable doInBackground(String... voids) {
         Log.d(TAG, "Start doInBackground(), URL: " + mUrl);
 
         try {
@@ -108,7 +107,7 @@
                 if (isCancelled()) {
                     boolean aborted = mDynSystem.abort();
                     Log.d(TAG, "Called DynamicSystemManager.abort(), result = " + aborted);
-                    return RESULT_OK;
+                    return null;
                 }
 
                 GsiProgress progress = mDynSystem.getInstallationProgress();
@@ -124,10 +123,8 @@
 
 
             if (mInstallationSession == null) {
-                Log.e(TAG, "Failed to start installation with requested size: "
+                throw new IOException("Failed to start installation with requested size: "
                         + (mSystemSize + mUserdataSize));
-
-                return RESULT_ERROR_IO;
             }
 
             installedSize = mUserdataSize;
@@ -157,20 +154,11 @@
                 }
             }
 
-            return RESULT_OK;
-
-        } catch (IOException e) {
-            e.printStackTrace();
-            return RESULT_ERROR_IO;
-
-        } catch (InvalidImageUrlException e) {
-            e.printStackTrace();
-            return RESULT_ERROR_INVALID_URL;
+            return null;
 
         } catch (Exception e) {
             e.printStackTrace();
-            return RESULT_ERROR_EXCEPTION;
-
+            return e;
         } finally {
             close();
         }
@@ -180,19 +168,24 @@
     protected void onCancelled() {
         Log.d(TAG, "onCancelled(), URL: " + mUrl);
 
-        close();
-
         mListener.onCancelled();
     }
 
     @Override
-    protected void onPostExecute(Integer result) {
-        Log.d(TAG, "onPostExecute(), URL: " + mUrl + ", result: " + result);
+    protected void onPostExecute(Throwable detail) {
+        if (detail == null) {
+            mResult = RESULT_OK;
+        } else if (detail instanceof IOException) {
+            mResult = RESULT_ERROR_IO;
+        } else if (detail instanceof InvalidImageUrlException) {
+            mResult = RESULT_ERROR_INVALID_URL;
+        } else {
+            mResult = RESULT_ERROR_EXCEPTION;
+        }
 
-        close();
+        Log.d(TAG, "onPostExecute(), URL: " + mUrl + ", result: " + mResult);
 
-        mResult = result;
-        mListener.onResult(mResult);
+        mListener.onResult(mResult, detail);
     }
 
     @Override
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
index c1e74ef..9acfbaf 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog.xml
@@ -15,6 +15,7 @@
   -->
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:sysui="http://schemas.android.com/apk/res-auto"
     android:id="@+id/volume_dialog_container"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
@@ -116,16 +117,17 @@
             android:clipToPadding="false"
             android:translationZ="@dimen/volume_dialog_elevation"
             android:background="@drawable/rounded_bg_full">
-            <com.android.keyguard.AlphaOptimizedImageButton
+            <com.android.systemui.volume.CaptionsToggleImageButton
                 android:id="@+id/odi_captions_icon"
                 android:src="@drawable/ic_volume_odi_captions_disabled"
                 style="@style/VolumeButtons"
                 android:background="@drawable/rounded_ripple"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:tint="@color/accent_tint_color_selector"
+                android:tint="@color/caption_tint_color_selector"
                 android:layout_gravity="center"
-                android:soundEffectsEnabled="false" />
+                android:soundEffectsEnabled="false"
+                sysui:optedOut="false"/>
         </FrameLayout>
 
         <ViewStub
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index aca99e3..3aba6a0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2234,6 +2234,9 @@
     <!-- Quick settings tile secondary label format combining roaming with the mobile data type. [CHAR LIMIT=NONE] -->
     <string name="mobile_data_text_format"><xliff:g name="roaming_status" example="Roaming">%1$s</xliff:g> \u2014 <xliff:g name="mobile_data_type" example="LTE">%2$s</xliff:g></string>
 
+    <!-- Quick settings tile secondary label format combining carrier name with the mobile data tye. [CHAR LIMIT=NONE] -->
+    <string name="mobile_carrier_text_format"><xliff:g id="carrier_name" example="Test">%1$s</xliff:g>, <xliff:g id="mobile_data_type" example="LTE">%2$s</xliff:g></string>
+
     <!-- Label for when wifi is off in QS detail panel [CHAR LIMIT=NONE] -->
     <string name="wifi_is_off">Wi-Fi is off</string>
 
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 294c725..8ad5c7b 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -39,6 +39,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.colorextraction.ColorExtractor;
+import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.dock.DockManager.DockEventListener;
@@ -72,6 +73,7 @@
     private final Map<String, ClockPlugin> mClocks = new ArrayMap<>();
     @Nullable private ClockPlugin mCurrentClock;
 
+    private final Context mContext;
     private final ContentResolver mContentResolver;
     private final SettingsWrapper mSettingsWrapper;
     private final Handler mMainHandler = new Handler(Looper.getMainLooper());
@@ -117,7 +119,7 @@
                     reload();
                 }
             };
-    @Nullable private final DockManager mDockManager;
+    @Nullable private DockManager mDockManager;
     /**
      * When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face
      * to show.
@@ -132,18 +134,16 @@
 
     @Inject
     public ClockManager(Context context, InjectionInflationController injectionInflater,
-            PluginManager pluginManager, @Nullable DockManager dockManager,
-            SysuiColorExtractor colorExtractor) {
-        this(context, injectionInflater, pluginManager, dockManager, colorExtractor,
+            PluginManager pluginManager, SysuiColorExtractor colorExtractor) {
+        this(context, injectionInflater, pluginManager, colorExtractor,
                 context.getContentResolver(), new SettingsWrapper(context.getContentResolver()));
     }
 
     ClockManager(Context context, InjectionInflationController injectionInflater,
-            PluginManager pluginManager, @Nullable DockManager dockManager,
-            SysuiColorExtractor colorExtractor, ContentResolver contentResolver,
-            SettingsWrapper settingsWrapper) {
+            PluginManager pluginManager, SysuiColorExtractor colorExtractor,
+            ContentResolver contentResolver, SettingsWrapper settingsWrapper) {
+        mContext = context;
         mPluginManager = pluginManager;
-        mDockManager = dockManager;
         mColorExtractor = colorExtractor;
         mContentResolver = contentResolver;
         mSettingsWrapper = settingsWrapper;
@@ -323,6 +323,9 @@
         mContentResolver.registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE),
                 false, mContentObserver);
+        if (mDockManager == null) {
+            mDockManager = SysUiServiceProvider.getComponent(mContext, DockManager.class);
+        }
         if (mDockManager != null) {
             mDockManager.addListener(mDockEventListener);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 6bb4fb5..47ad0c1 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -31,7 +31,6 @@
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.dock.DockManager;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -220,16 +219,6 @@
         return SysUiServiceProvider.getComponent(context, StatusBar.class);
     }
 
-    /**
-     * Provides DockManager.
-     */
-    @Singleton
-    @Provides
-    @Nullable
-    public DockManager providesDockManager(Context context) {
-        return SysUiServiceProvider.getComponent(context, DockManager.class);
-    }
-
     @Module
     protected static class ContextHolder {
         private Context mContext;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 7a68be4..979d226 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -84,7 +84,6 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        updateViews();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index c587a39..db79e4d7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -25,11 +25,7 @@
 import android.content.res.Resources;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
-import android.text.SpannableString;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
 import android.text.TextUtils;
-import android.text.style.TextAppearanceSpan;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -192,8 +188,10 @@
             state.secondaryLabel = r.getString(R.string.status_bar_airplane);
         } else if (mobileDataEnabled) {
             state.state = Tile.STATE_ACTIVE;
-            state.secondaryLabel = appendMobileDataType(getMobileDataSubscriptionName(cb),
-                    cb.dataContentDescription);
+            state.secondaryLabel = appendMobileDataType(
+                    // Only show carrier name if there are more than 1 subscription
+                    cb.multipleSubs ? cb.dataSubscriptionName : "",
+                    getMobileDataContentName(cb));
         } else {
             state.state = Tile.STATE_INACTIVE;
             state.secondaryLabel = r.getString(R.string.cell_data_off);
@@ -216,24 +214,22 @@
         if (TextUtils.isEmpty(dataType)) {
             return current;
         }
-        SpannableString type = new SpannableString(dataType);
-        SpannableStringBuilder builder = new SpannableStringBuilder(current);
-        builder.append(" ");
-        builder.append(type, new TextAppearanceSpan(mContext, R.style.TextAppearance_RATBadge),
-                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-        return builder;
+        if (TextUtils.isEmpty(current)) {
+            return dataType;
+        }
+        return mContext.getString(R.string.mobile_carrier_text_format, current, dataType);
     }
 
-    private CharSequence getMobileDataSubscriptionName(CallbackInfo cb) {
-        if (cb.roaming && !TextUtils.isEmpty(cb.dataSubscriptionName)) {
+    private CharSequence getMobileDataContentName(CallbackInfo cb) {
+        if (cb.roaming && !TextUtils.isEmpty(cb.dataContentDescription)) {
             String roaming = mContext.getString(R.string.data_connection_roaming);
-            String dataDescription = cb.dataSubscriptionName.toString();
+            String dataDescription = cb.dataContentDescription.toString();
             return mContext.getString(R.string.mobile_data_text_format, roaming, dataDescription);
         }
         if (cb.roaming) {
             return mContext.getString(R.string.data_connection_roaming);
         }
-        return cb.dataSubscriptionName;
+        return cb.dataContentDescription;
     }
 
     @Override
@@ -254,6 +250,7 @@
         boolean activityOut;
         boolean noSim;
         boolean roaming;
+        boolean multipleSubs;
     }
 
     private final class CellSignalCallback implements SignalCallback {
@@ -272,6 +269,7 @@
             mInfo.activityIn = activityIn;
             mInfo.activityOut = activityOut;
             mInfo.roaming = roaming;
+            mInfo.multipleSubs = mController.getNumberSubscriptions() > 1;
             refreshState(mInfo);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
index 4c7fdb0..4897464 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
@@ -108,7 +108,7 @@
     public static NavigationBarEdgePanel create(@NonNull Context context, int width, int height,
             int gravity) {
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
-                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
                 WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                     | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                     | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index f809b62..bfbe886 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -346,8 +346,11 @@
             post(() -> {
                 // When the ime changes visibility, resize the edge panels to not cover the ime
                 final int width = mPrototypeController.getEdgeSensitivityWidth();
-                final int height = mContext.getDisplay().getHeight() - imeHeight
-                        - getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
+                int height = mContext.getDisplay().getHeight() - imeHeight;
+                if (!imeVisible) {
+                    // Hide the navigation bar area at the bottom for gestures
+                    height -= getResources().getDimensionPixelOffset(R.dimen.navigation_bar_height);
+                }
                 if (mLeftEdgePanel != null) {
                     mLeftEdgePanel.setDimensions(width, height);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 5bd394f..c5996a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -470,10 +470,16 @@
             mNetworkController.recalculateEmergency();
         }
         // Fill in the network name if we think we have it.
-        if (mCurrentState.networkName == mNetworkNameDefault && mServiceState != null
+        if (mCurrentState.networkName.equals(mNetworkNameDefault) && mServiceState != null
                 && !TextUtils.isEmpty(mServiceState.getOperatorAlphaShort())) {
             mCurrentState.networkName = mServiceState.getOperatorAlphaShort();
         }
+        // If this is the data subscription, update the currentState data name
+        if (mCurrentState.networkNameData.equals(mNetworkNameDefault) && mServiceState != null
+                && mCurrentState.dataSim
+                && !TextUtils.isEmpty(mServiceState.getDataOperatorAlphaShort())) {
+            mCurrentState.networkNameData = mServiceState.getDataOperatorAlphaShort();
+        }
 
         notifyListenersIfNecessary();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 51fef7d..71db618 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -37,6 +37,7 @@
     DataUsageController getMobileDataController();
     DataSaverController getDataSaverController();
     String getMobileDataNetworkName();
+    int getNumberSubscriptions();
 
     boolean hasVoiceCallingFeature();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index ef39912..d01430a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -376,6 +376,11 @@
         return controller != null ? controller.getState().networkNameData : "";
     }
 
+    @Override
+    public int getNumberSubscriptions() {
+        return mMobileSignalControllers.size();
+    }
+
     public boolean isEmergencyOnly() {
         if (mMobileSignalControllers.size() == 0) {
             // When there are no active subscriptions, determine emengency state from last
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
index 36265d4..46b1833 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -67,8 +67,10 @@
         when(mMockInjectionInflationController.injectable(any())).thenReturn(inflater);
 
         mFakeDockManager = new DockManagerFake();
+        getContext().putComponent(DockManager.class, mFakeDockManager);
+
         mClockManager = new ClockManager(getContext(), mMockInjectionInflationController,
-                mMockPluginManager, mFakeDockManager, mMockColorExtractor, mMockContentResolver,
+                mMockPluginManager, mMockColorExtractor, mMockContentResolver,
                 mMockSettingsWrapper);
 
         mClockManager.addOnClockChangedListener(mMockListener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 5cafc02..4fe18b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -448,7 +448,12 @@
         }
     }
 
-   protected void assertNetworkNameEquals(String expected) {
+    protected void assertNetworkNameEquals(String expected) {
        assertEquals("Network name", expected, mMobileSignalController.getState().networkName);
-   }
+    }
+
+    protected void assertDataNetworkNameEquals(String expected) {
+        assertEquals("Data network name", expected, mNetworkController.getMobileDataNetworkName());
+    }
+
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 68323c9..cd0a0441 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -285,6 +285,15 @@
         testDataActivity(TelephonyManager.DATA_ACTIVITY_INOUT, true, true);
     }
 
+    @Test
+    public void testUpdateDataNetworkName() {
+        setupDefaultSignal();
+        String newDataName = "TestDataName";
+        when(mServiceState.getDataOperatorAlphaShort()).thenReturn(newDataName);
+        updateServiceState();
+        assertDataNetworkNameEquals(newDataName);
+    }
+
     private void testDataActivity(int direction, boolean in, boolean out) {
         updateDataActivity(direction);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index bcbba8b..ac6544e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -20,6 +20,7 @@
 import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import android.content.Intent;
 import android.net.ConnectivityManager;
@@ -55,7 +56,7 @@
     @Test
     public void testNoIconWithoutMobile() {
         // Turn off mobile network support.
-        Mockito.when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
+        when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
         // Create a new NetworkController as this is currently handled in constructor.
         mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
                 mConfig, Looper.getMainLooper(), mCallbackHandler,
@@ -117,7 +118,7 @@
     @Test
     public void testNoSimlessIconWithoutMobile() {
         // Turn off mobile network support.
-        Mockito.when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
+        when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
         // Create a new NetworkController as this is currently handled in constructor.
         mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm,
                 mConfig, Looper.getMainLooper(), mCallbackHandler,
@@ -253,14 +254,14 @@
 
             // Generate a list of subscriptions we will tell the NetworkController to use.
             SubscriptionInfo mockSubInfo = Mockito.mock(SubscriptionInfo.class);
-            Mockito.when(mockSubInfo.getSubscriptionId()).thenReturn(testSubscriptions[i]);
+            when(mockSubInfo.getSubscriptionId()).thenReturn(testSubscriptions[i]);
             subscriptions.add(mockSubInfo);
         }
         assertTrue(mNetworkController.hasCorrectMobileControllers(subscriptions));
 
         // Add a subscription that the NetworkController doesn't know about.
         SubscriptionInfo mockSubInfo = Mockito.mock(SubscriptionInfo.class);
-        Mockito.when(mockSubInfo.getSubscriptionId()).thenReturn(notTestSubscription);
+        when(mockSubInfo.getSubscriptionId()).thenReturn(notTestSubscription);
         subscriptions.add(mockSubInfo);
         assertFalse(mNetworkController.hasCorrectMobileControllers(subscriptions));
     }
@@ -290,8 +291,8 @@
             if (i != indexToSkipSubscription) {
                 // Generate a list of subscriptions we will tell the NetworkController to use.
                 SubscriptionInfo mockSubInfo = Mockito.mock(SubscriptionInfo.class);
-                Mockito.when(mockSubInfo.getSubscriptionId()).thenReturn(testSubscriptions[i]);
-                Mockito.when(mockSubInfo.getSimSlotIndex()).thenReturn(testSubscriptions[i]);
+                when(mockSubInfo.getSubscriptionId()).thenReturn(testSubscriptions[i]);
+                when(mockSubInfo.getSimSlotIndex()).thenReturn(testSubscriptions[i]);
                 subscriptions.add(mockSubInfo);
             }
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
index 5385f6d..d5ba381 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
@@ -93,4 +93,9 @@
     public String getMobileDataNetworkName() {
         return "";
     }
+
+    @Override
+    public int getNumberSubscriptions() {
+        return 0;
+    }
 }
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 95abd05..f629cf6 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -7149,6 +7149,13 @@
 
     // Settings > Display > Theme
     DARK_UI_SETTINGS = 1698;
+
+    // Settings > global bubble settings
+    BUBBLE_SETTINGS = 1699;
+
+    // Settings > app > bubble settings
+    APP_BUBBLE_SETTINGS = 1700;
+
     // ---- End Q Constants, all Q constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 47cd917..904817e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -777,6 +777,7 @@
             final int removedWindowId = removeAccessibilityInteractionConnectionInternalLocked(
                     token, mGlobalWindowTokens, mGlobalInteractionConnections);
             if (removedWindowId >= 0) {
+                mSecurityPolicy.onAccessibilityClientRemovedLocked(removedWindowId);
                 if (DEBUG) {
                     Slog.i(LOG_TAG, "Removed global connection for pid:" + Binder.getCallingPid()
                             + " with windowId: " + removedWindowId + " and token: " + window.asBinder());
@@ -790,6 +791,7 @@
                         removeAccessibilityInteractionConnectionInternalLocked(
                         token, userState.mWindowTokens, userState.mInteractionConnections);
                 if (removedWindowIdForUser >= 0) {
+                    mSecurityPolicy.onAccessibilityClientRemovedLocked(removedWindowIdForUser);
                     if (DEBUG) {
                         Slog.i(LOG_TAG, "Removed user connection for pid:" + Binder.getCallingPid()
                                 + " with windowId: " + removedWindowIdForUser + " and userId:"
@@ -1332,6 +1334,7 @@
             userState.mWindowTokens.remove(windowId);
             userState.mInteractionConnections.remove(windowId);
         }
+        mSecurityPolicy.onAccessibilityClientRemovedLocked(windowId);
         if (DEBUG) {
             Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId);
         }
@@ -3266,6 +3269,18 @@
             mWindows = null;
         }
 
+        /**
+         * A callback when accessibility interaction client is removed.
+         */
+        public void onAccessibilityClientRemovedLocked(int windowId) {
+            // Active window cannot update immediately, if windows callback is unregistered.
+            // Update active window to invalid, when its a11y interaction client is removed.
+            if (mWindowsForAccessibilityCallback == null && windowId >= 0
+                    && mActiveWindowId == windowId) {
+                mActiveWindowId = INVALID_WINDOW_ID;
+            }
+        }
+
         public void updateWindowsLocked(List<WindowInfo> windows) {
             if (mWindows == null) {
                 mWindows = new ArrayList<>();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 98311fc..5b9c1f8 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2881,6 +2881,32 @@
         mVold.commitChanges();
     }
 
+    /**
+     * Check if we should be mounting with checkpointing or are checkpointing now
+     */
+    @Override
+    public boolean needsCheckpoint() throws RemoteException {
+        // Only the system process is permitted to commit checkpoints
+        if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
+            throw new SecurityException("no permission to commit checkpoint changes");
+        }
+
+        return mVold.needsCheckpoint();
+    }
+
+    /**
+     * Abort the current set of changes and either try again, or abort entirely
+     */
+    @Override
+    public void abortChanges(String message, boolean retry) throws RemoteException {
+        // Only the system process is permitted to abort checkpoints
+        if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) {
+            throw new SecurityException("no permission to commit checkpoint changes");
+        }
+
+        mVold.abortChanges(message, retry);
+    }
+
     @Override
     public String getPassword() throws RemoteException {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
@@ -3871,6 +3897,7 @@
                         case "com.jrtstudio.AnotherMusicPlayer": // b/129084562
                         case "ak.alizandro.smartaudiobookplayer": // b/129084042
                         case "com.campmobile.snow": // b/128803870
+                        case "com.qnap.qfile": // b/126374406
                             return Zygote.MOUNT_EXTERNAL_LEGACY;
                     }
                 }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 26896f5..8b10267 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -73,10 +73,12 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.OptionalInt;
 
 /**
  * Since phone process can be restarted, this class provides a centralized place
@@ -1159,17 +1161,28 @@
 
     @Override
     public void notifyCarrierNetworkChange(boolean active) {
-        enforceNotifyPermissionOrCarrierPrivilege("notifyCarrierNetworkChange()");
+        // only CarrierService with carrier privilege rule should have the permission.
+        int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        try {
+            subId = Arrays.stream(SubscriptionManager.from(mContext)
+                    .getActiveSubscriptionIdList())
+                    .filter(i -> TelephonyPermissions.checkCarrierPrivilegeForSubId(i))
+                    .findFirst().getAsInt();
+        } catch (NoSuchElementException ex) {
+            log("notifyCarrierNetworkChange without carrier privilege");
+        }
+        int phoneId = SubscriptionManager.getPhoneId(subId);
 
         if (VDBG) {
-            log("notifyCarrierNetworkChange: active=" + active);
+            log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId);
         }
 
         synchronized (mRecords) {
             mCarrierNetworkChangeState = active;
             for (Record r : mRecords) {
                 if (r.matchPhoneStateListenerEvent(
-                        PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE)) {
+                        PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE) &&
+                        idMatch(r.subId, subId, phoneId)) {
                     try {
                         r.callback.onCarrierNetworkChange(active);
                     } catch (RemoteException ex) {
@@ -1885,7 +1898,7 @@
                 pw.println("mDataConnectionState=" + mDataConnectionState[i]);
                 pw.println("mCellLocation=" + mCellLocation[i]);
                 pw.println("mCellInfo=" + mCellInfo.get(i));
-                pw.println("mImsCallDisconnectCause=" + mImsReasonInfo.get(i).toString());
+                pw.println("mImsCallDisconnectCause=" + mImsReasonInfo.get(i));
                 pw.decreaseIndent();
             }
             pw.println("mCallNetworkType=" + mCallNetworkType);
diff --git a/services/core/java/com/android/server/accounts/AccountsDb.java b/services/core/java/com/android/server/accounts/AccountsDb.java
index 712edcc..da66590 100644
--- a/services/core/java/com/android/server/accounts/AccountsDb.java
+++ b/services/core/java/com/android/server/accounts/AccountsDb.java
@@ -58,7 +58,6 @@
     private static final int CE_DATABASE_VERSION = 10;
     private static final int DE_DATABASE_VERSION = 3; // Added visibility support in O
 
-
     static final String TABLE_ACCOUNTS = "accounts";
     private static final String ACCOUNTS_ID = "_id";
     private static final String ACCOUNTS_NAME = "name";
@@ -267,6 +266,13 @@
         }
 
         @Override
+        public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            Log.e(TAG, "onDowngrade: recreate accounts CE table");
+            resetDatabase(db);
+            onCreate(db);
+        }
+
+        @Override
         public void onOpen(SQLiteDatabase db) {
             if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + CE_DATABASE_NAME);
         }
@@ -616,6 +622,13 @@
             }
         }
 
+        @Override
+        public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            Log.e(TAG, "onDowngrade: recreate accounts DE table");
+            resetDatabase(db);
+            onCreate(db);
+        }
+
         public SQLiteDatabase getReadableDatabaseUserIsUnlocked() {
             if(!mCeAttached) {
                 Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user " + mUserId
@@ -1399,4 +1412,26 @@
         return new AccountsDb(deDatabaseHelper, context, preNDatabaseFile);
     }
 
+    /**
+     * Removes all tables and triggers created by AccountManager.
+     */
+    private static void resetDatabase(SQLiteDatabase db) {
+        try (Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type ='table'", null)) {
+            while (c.moveToNext()) {
+                String name = c.getString(0);
+                // Skip tables managed by SQLiteDatabase
+                if ("android_metadata".equals(name) || "sqlite_sequence".equals(name)) {
+                    continue;
+                }
+                db.execSQL("DROP TABLE IF EXISTS " + name);
+            }
+        }
+
+        try (Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE type ='trigger'", null)) {
+            while (c.moveToNext()) {
+                String name = c.getString(0);
+                db.execSQL("DROP TRIGGER IF EXISTS " + name);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
new file mode 100644
index 0000000..d284c60
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright 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.
+ */
+
+package com.android.server.media;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.media.IMediaRoute2Callback;
+import android.media.IMediaRoute2Provider;
+import android.media.MediaRoute2ProviderService;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+
+/**
+ * Maintains a connection to a particular media route provider service.
+ */
+final class MediaRoute2ProviderProxy implements ServiceConnection {
+    private static final String TAG = "MediaRoute2ProviderProxy";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private final Context mContext;
+    private final ComponentName mComponentName;
+    private final int mUserId;
+    private final Handler mHandler;
+
+    private Callback mCallback;
+
+    // Selected Route info
+    public int mSelectedUid;
+    public String mSelectedRouteId;
+
+    // Connection state
+    private boolean mRunning;
+    private boolean mBound;
+    private Connection mActiveConnection;
+    private boolean mConnectionReady;
+
+    MediaRoute2ProviderProxy(Context context, ComponentName componentName, int userId) {
+        mContext = context;
+        mComponentName = componentName;
+        mUserId = userId;
+        mHandler = new Handler();
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        pw.println(prefix + "Proxy");
+        pw.println(prefix + "  mUserId=" + mUserId);
+        pw.println(prefix + "  mRunning=" + mRunning);
+        pw.println(prefix + "  mBound=" + mBound);
+        pw.println(prefix + "  mActiveConnection=" + mActiveConnection);
+        pw.println(prefix + "  mConnectionReady=" + mConnectionReady);
+    }
+
+    public void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
+    public void setSelectedRoute(int uid, String routeId) {
+        if (mConnectionReady) {
+            mActiveConnection.selectRoute(uid, routeId);
+            updateBinding();
+        }
+    }
+
+    public boolean hasComponentName(String packageName, String className) {
+        return mComponentName.getPackageName().equals(packageName)
+                && mComponentName.getClassName().equals(className);
+    }
+
+    public String getFlattenedComponentName() {
+        return mComponentName.flattenToShortString();
+    }
+
+    public void start() {
+        if (!mRunning) {
+            if (DEBUG) {
+                Slog.d(TAG, this + ": Starting");
+            }
+
+            mRunning = true;
+            updateBinding();
+        }
+    }
+
+    public void stop() {
+        if (mRunning) {
+            if (DEBUG) {
+                Slog.d(TAG, this + ": Stopping");
+            }
+
+            mRunning = false;
+            updateBinding();
+        }
+    }
+
+    public void rebindIfDisconnected() {
+        if (mActiveConnection == null && shouldBind()) {
+            unbind();
+            bind();
+        }
+    }
+
+    private void updateBinding() {
+        if (shouldBind()) {
+            bind();
+        } else {
+            unbind();
+        }
+    }
+
+    private boolean shouldBind() {
+        //TODO: binding could be delayed until it's necessary.
+        if (mRunning) {
+            return true;
+        }
+        return false;
+    }
+
+    private void bind() {
+        if (!mBound) {
+            if (DEBUG) {
+                Slog.d(TAG, this + ": Binding");
+            }
+
+            Intent service = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE);
+            service.setComponent(mComponentName);
+            try {
+                mBound = mContext.bindServiceAsUser(service, this,
+                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+                        new UserHandle(mUserId));
+                if (!mBound && DEBUG) {
+                    Slog.d(TAG, this + ": Bind failed");
+                }
+            } catch (SecurityException ex) {
+                if (DEBUG) {
+                    Slog.d(TAG, this + ": Bind failed", ex);
+                }
+            }
+        }
+    }
+
+    private void unbind() {
+        if (mBound) {
+            if (DEBUG) {
+                Slog.d(TAG, this + ": Unbinding");
+            }
+
+            mBound = false;
+            disconnect();
+            mContext.unbindService(this);
+        }
+    }
+
+    @Override
+    public void onServiceConnected(ComponentName name, IBinder service) {
+        if (DEBUG) {
+            Slog.d(TAG, this + ": Connected");
+        }
+
+        if (mBound) {
+            disconnect();
+
+            IMediaRoute2Provider provider = IMediaRoute2Provider.Stub.asInterface(service);
+            if (provider != null) {
+                Connection connection = new Connection(provider);
+                if (connection.register()) {
+                    mActiveConnection = connection;
+                } else {
+                    if (DEBUG) {
+                        Slog.d(TAG, this + ": Registration failed");
+                    }
+                }
+            } else {
+                Slog.e(TAG, this + ": Service returned invalid remote display provider binder");
+            }
+        }
+    }
+
+    @Override
+    public void onServiceDisconnected(ComponentName name) {
+        if (DEBUG) {
+            Slog.d(TAG, this + ": Service disconnected");
+        }
+        disconnect();
+    }
+
+    private void onConnectionReady(Connection connection) {
+        if (mActiveConnection == connection) {
+            mConnectionReady = true;
+        }
+    }
+
+    private void onConnectionDied(Connection connection) {
+        if (mActiveConnection == connection) {
+            if (DEBUG) {
+                Slog.d(TAG, this + ": Service connection died");
+            }
+            disconnect();
+        }
+    }
+
+    private void onRouteSelected(Connection connection, int uid, String routeId) {
+        mSelectedUid = uid;
+        mSelectedRouteId = routeId;
+
+        if (mActiveConnection == connection) {
+            if (DEBUG) {
+                Slog.d(TAG, this + ": State changed ");
+            }
+            mHandler.post(mStateChanged);
+        }
+    }
+
+    private void disconnect() {
+        if (mActiveConnection != null) {
+            mConnectionReady = false;
+            mActiveConnection.dispose();
+            mActiveConnection = null;
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "Service connection " + mComponentName.flattenToShortString();
+    }
+
+    private final Runnable mStateChanged = new Runnable() {
+        @Override
+        public void run() {
+            if (mCallback != null) {
+                mCallback.onProviderStateChanged(MediaRoute2ProviderProxy.this);
+            }
+        }
+    };
+
+    public interface Callback {
+        void onProviderStateChanged(MediaRoute2ProviderProxy provider);
+    }
+
+    private final class Connection implements DeathRecipient {
+        private final IMediaRoute2Provider mProvider;
+        private final ProviderCallback mCallback;
+
+        Connection(IMediaRoute2Provider provider) {
+            mProvider = provider;
+            mCallback = new ProviderCallback(this);
+        }
+
+        public boolean register() {
+            try {
+                mProvider.asBinder().linkToDeath(this, 0);
+                mProvider.setCallback(mCallback);
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        onConnectionReady(Connection.this);
+                    }
+                });
+                return true;
+            } catch (RemoteException ex) {
+                binderDied();
+            }
+            return false;
+        }
+
+        public void dispose() {
+            mProvider.asBinder().unlinkToDeath(this, 0);
+            mCallback.dispose();
+        }
+
+        public void selectRoute(int uid, String id) {
+            try {
+                mProvider.selectRoute(uid, id);
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "Failed to deliver request to set discovery mode.", ex);
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    onConnectionDied(Connection.this);
+                }
+            });
+        }
+
+        void postRouteSelected(int uid, String routeId) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    onRouteSelected(Connection.this, uid, routeId);
+                }
+            });
+        }
+    }
+
+    private static final class ProviderCallback extends IMediaRoute2Callback.Stub  {
+        private final WeakReference<Connection> mConnectionRef;
+
+        ProviderCallback(Connection connection) {
+            mConnectionRef = new WeakReference<Connection>(connection);
+        }
+
+        public void dispose() {
+            mConnectionRef.clear();
+        }
+
+        @Override
+        public void onRouteSelected(int uid, String routeId) throws RemoteException {
+            Connection connection = mConnectionRef.get();
+            if (connection != null) {
+                connection.postRouteSelected(uid, routeId);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
new file mode 100644
index 0000000..08d8c58
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 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.
+ */
+
+package com.android.server.media;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.media.MediaRoute2ProviderService;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ */
+final class MediaRoute2ProviderWatcher {
+    private static final String TAG = "MediaRouteProvider";  // max. 23 chars
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private final Context mContext;
+    private final Callback mCallback;
+    private final Handler mHandler;
+    private final int mUserId;
+    private final PackageManager mPackageManager;
+
+    private final ArrayList<MediaRoute2ProviderProxy> mProviders = new ArrayList<>();
+    private boolean mRunning;
+
+    MediaRoute2ProviderWatcher(Context context,
+            Callback callback, Handler handler, int userId) {
+        mContext = context;
+        mCallback = callback;
+        mHandler = handler;
+        mUserId = userId;
+        mPackageManager = context.getPackageManager();
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        pw.println(prefix + "Watcher");
+        pw.println(prefix + "  mUserId=" + mUserId);
+        pw.println(prefix + "  mRunning=" + mRunning);
+        pw.println(prefix + "  mProviders.size()=" + mProviders.size());
+    }
+
+    public void start() {
+        if (!mRunning) {
+            mRunning = true;
+
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+            filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+            filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+            filter.addDataScheme("package");
+            mContext.registerReceiverAsUser(mScanPackagesReceiver,
+                    new UserHandle(mUserId), filter, null, mHandler);
+
+            // Scan packages.
+            // Also has the side-effect of restarting providers if needed.
+            mHandler.post(mScanPackagesRunnable);
+        }
+    }
+
+    public void stop() {
+        if (mRunning) {
+            mRunning = false;
+
+            mContext.unregisterReceiver(mScanPackagesReceiver);
+            mHandler.removeCallbacks(mScanPackagesRunnable);
+
+            // Stop all providers.
+            for (int i = mProviders.size() - 1; i >= 0; i--) {
+                mProviders.get(i).stop();
+            }
+        }
+    }
+
+    private void scanPackages() {
+        if (!mRunning) {
+            return;
+        }
+
+        // Add providers for all new services.
+        // Reorder the list so that providers left at the end will be the ones to remove.
+        int targetIndex = 0;
+        Intent intent = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE);
+        for (ResolveInfo resolveInfo : mPackageManager.queryIntentServicesAsUser(
+                intent, 0, mUserId)) {
+            ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+            if (serviceInfo != null) {
+                int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
+                if (sourceIndex < 0) {
+                    MediaRoute2ProviderProxy provider =
+                            new MediaRoute2ProviderProxy(mContext,
+                            new ComponentName(serviceInfo.packageName, serviceInfo.name),
+                            mUserId);
+                    provider.start();
+                    mProviders.add(targetIndex++, provider);
+                    mCallback.addProvider(provider);
+                } else if (sourceIndex >= targetIndex) {
+                    MediaRoute2ProviderProxy provider = mProviders.get(sourceIndex);
+                    provider.start(); // restart the provider if needed
+                    provider.rebindIfDisconnected();
+                    Collections.swap(mProviders, sourceIndex, targetIndex++);
+                }
+            }
+        }
+
+        // Remove providers for missing services.
+        if (targetIndex < mProviders.size()) {
+            for (int i = mProviders.size() - 1; i >= targetIndex; i--) {
+                MediaRoute2ProviderProxy provider = mProviders.get(i);
+                mCallback.removeProvider(provider);
+                mProviders.remove(provider);
+                provider.stop();
+            }
+        }
+    }
+
+    private int findProvider(String packageName, String className) {
+        int count = mProviders.size();
+        for (int i = 0; i < count; i++) {
+            MediaRoute2ProviderProxy provider = mProviders.get(i);
+            if (provider.hasComponentName(packageName, className)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private final BroadcastReceiver mScanPackagesReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (DEBUG) {
+                Slog.d(TAG, "Received package manager broadcast: " + intent);
+            }
+            scanPackages();
+        }
+    };
+
+    private final Runnable mScanPackagesRunnable = new Runnable() {
+        @Override
+        public void run() {
+            scanPackages();
+        }
+    };
+
+    public interface Callback {
+        void addProvider(MediaRoute2ProviderProxy provider);
+        void removeProvider(MediaRoute2ProviderProxy provider);
+    }
+}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 3eb7321..f822e82 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -16,14 +16,10 @@
 
 package com.android.server.media;
 
-import com.android.internal.util.DumpUtils;
-import com.android.server.Watchdog;
-
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -34,6 +30,7 @@
 import android.media.AudioSystem;
 import android.media.IAudioRoutesObserver;
 import android.media.IAudioService;
+import android.media.IMediaRouter2ManagerClient;
 import android.media.IMediaRouterClient;
 import android.media.IMediaRouterService;
 import android.media.MediaRouter;
@@ -53,10 +50,14 @@
 import android.util.ArrayMap;
 import android.util.IntArray;
 import android.util.Log;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
 
+import com.android.internal.util.DumpUtils;
+import com.android.server.Watchdog;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -97,6 +98,7 @@
     private final Object mLock = new Object();
     private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
     private final ArrayMap<IBinder, ClientRecord> mAllClientRecords = new ArrayMap<>();
+    private final ArrayMap<IBinder, ManagerRecord> mAllManagerRecords = new ArrayMap<>();
     private int mCurrentUserId = -1;
     private final IAudioService mAudioService;
     private final AudioPlayerStateMonitor mAudioPlayerStateMonitor;
@@ -306,6 +308,22 @@
 
     // Binder call
     @Override
+    public void setControlCategories(IMediaRouterClient client, List<String> categories) {
+        if (client == null) {
+            throw new IllegalArgumentException("client must not be null");
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                setControlCategoriesLocked(client, categories);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    // Binder call
+    @Override
     public void setDiscoveryRequest(IMediaRouterClient client,
             int routeTypes, boolean activeScan) {
         if (client == null) {
@@ -404,6 +422,65 @@
         }
     }
 
+    // Binder call
+    @Override
+    public void registerManagerAsUser(IMediaRouter2ManagerClient client,
+            String packageName, int userId) {
+        if (client == null) {
+            throw new IllegalArgumentException("client must not be null");
+        }
+        //TODO: should check permission
+        final boolean trusted = true;
+
+        final int uid = Binder.getCallingUid();
+        if (!validatePackageName(uid, packageName)) {
+            throw new SecurityException("packageName must match the calling uid");
+        }
+
+        final int pid = Binder.getCallingPid();
+        final int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+                false /*allowAll*/, true /*requireFull*/, "registerManagerAsUser", packageName);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                registerManagerLocked(client, uid, pid, packageName, resolvedUserId, trusted);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    // Binder call
+    @Override
+    public void unregisterManager(IMediaRouter2ManagerClient client) {
+        if (client == null) {
+            throw new IllegalArgumentException("client must not be null");
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                unregisterManagerLocked(client, false);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    // Binder call
+    @Override
+    public void setRemoteRoute(IMediaRouter2ManagerClient client,
+            int uid, String routeId, boolean explicit) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                setRemoteRouteLocked(client, uid, routeId, explicit);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     void restoreBluetoothA2dp() {
         try {
             boolean a2dpOn;
@@ -475,6 +552,12 @@
         }
     }
 
+    void clientDied(ManagerRecord managerRecord) {
+        synchronized (mLock) {
+            unregisterManagerLocked(managerRecord.mClient, true);
+        }
+    }
+
     private void registerClientLocked(IMediaRouterClient client,
             int uid, int pid, String packageName, int userId, boolean trusted) {
         final IBinder binder = client.asBinder();
@@ -522,6 +605,17 @@
         return null;
     }
 
+    private void setControlCategoriesLocked(IMediaRouterClient client, List<String> categories) {
+        final IBinder binder = client.asBinder();
+        ClientRecord clientRecord = mAllClientRecords.get(binder);
+
+        if (clientRecord != null) {
+            clientRecord.mControlCategories = categories;
+            clientRecord.mUserRecord.mHandler.obtainMessage(
+                    UserHandler.MSG_UPDATE_CLIENT_USAGE, clientRecord).sendToTarget();
+        }
+    }
+
     private void setDiscoveryRequestLocked(IMediaRouterClient client,
             int routeTypes, boolean activeScan) {
         final IBinder binder = client.asBinder();
@@ -575,6 +669,63 @@
         }
     }
 
+    private void registerManagerLocked(IMediaRouter2ManagerClient client,
+            int uid, int pid, String packageName, int userId, boolean trusted) {
+        final IBinder binder = client.asBinder();
+        ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+        if (managerRecord == null) {
+            boolean newUser = false;
+            UserRecord userRecord = mUserRecords.get(userId);
+            if (userRecord == null) {
+                userRecord = new UserRecord(userId);
+                newUser = true;
+            }
+            managerRecord = new ManagerRecord(userRecord, client, uid, pid, packageName, trusted);
+            try {
+                binder.linkToDeath(managerRecord, 0);
+            } catch (RemoteException ex) {
+                throw new RuntimeException("Media router client died prematurely.", ex);
+            }
+
+            if (newUser) {
+                mUserRecords.put(userId, userRecord);
+                initializeUserLocked(userRecord);
+            }
+
+            userRecord.mManagerRecords.add(managerRecord);
+            mAllManagerRecords.put(binder, managerRecord);
+
+            // send client usage to manager
+            final int clientCount = userRecord.mClientRecords.size();
+            for (int i = 0; i < clientCount; i++) {
+                userRecord.mHandler.obtainMessage(UserHandler.MSG_UPDATE_CLIENT_USAGE,
+                        userRecord.mClientRecords.get(i)).sendToTarget();
+            }
+        }
+    }
+
+    private void unregisterManagerLocked(IMediaRouter2ManagerClient client, boolean died) {
+        ManagerRecord clientRecord = mAllManagerRecords.remove(client.asBinder());
+        if (clientRecord != null) {
+            UserRecord userRecord = clientRecord.mUserRecord;
+            userRecord.mManagerRecords.remove(clientRecord);
+            clientRecord.dispose();
+            disposeUserIfNeededLocked(userRecord); // since client removed from user
+        }
+    }
+
+    private void setRemoteRouteLocked(IMediaRouter2ManagerClient client,
+            int uid, String routeId, boolean explicit) {
+        ManagerRecord managerRecord = mAllManagerRecords.get(client.asBinder());
+        if (managerRecord != null) {
+            if (explicit && managerRecord.mTrusted) {
+                Pair<Integer, String> obj = new Pair<>(uid, routeId);
+                managerRecord.mUserRecord.mHandler.obtainMessage(
+                        UserHandler.MSG_SELECT_REMOTE_ROUTE, obj).sendToTarget();
+            }
+        }
+    }
+
     private void requestSetVolumeLocked(IMediaRouterClient client,
             String routeId, int volume) {
         final IBinder binder = client.asBinder();
@@ -667,6 +818,46 @@
         }
     }
 
+    final class ManagerRecord implements DeathRecipient {
+        public final UserRecord mUserRecord;
+        public final IMediaRouter2ManagerClient mClient;
+        public final int mUid;
+        public final int mPid;
+        public final String mPackageName;
+        public final boolean mTrusted;
+
+        ManagerRecord(UserRecord userRecord, IMediaRouter2ManagerClient client,
+                int uid, int pid, String packageName, boolean trusted) {
+            mUserRecord = userRecord;
+            mClient = client;
+            mUid = uid;
+            mPid = pid;
+            mPackageName = packageName;
+            mTrusted = trusted;
+        }
+
+        public void dispose() {
+            mClient.asBinder().unlinkToDeath(this, 0);
+        }
+
+        @Override
+        public void binderDied() {
+            clientDied(this);
+        }
+
+        public void dump(PrintWriter pw, String prefix) {
+            pw.println(prefix + this);
+
+            final String indent = prefix + "  ";
+            pw.println(indent + "mTrusted=" + mTrusted);
+        }
+
+        @Override
+        public String toString() {
+            return "Client " + mPackageName + " (pid " + mPid + ")";
+        }
+    }
+
     /**
      * Information about a particular client of the media router.
      * The contents of this object is guarded by mLock.
@@ -678,6 +869,7 @@
         public final int mPid;
         public final String mPackageName;
         public final boolean mTrusted;
+        public List<String> mControlCategories;
 
         public int mRouteTypes;
         public boolean mActiveScan;
@@ -728,7 +920,8 @@
      */
     final class UserRecord {
         public final int mUserId;
-        public final ArrayList<ClientRecord> mClientRecords = new ArrayList<ClientRecord>();
+        public final ArrayList<ClientRecord> mClientRecords = new ArrayList<>();
+        public final ArrayList<ManagerRecord> mManagerRecords = new ArrayList<>();
         public final UserHandler mHandler;
         public MediaRouterClientState mRouterState;
 
@@ -783,7 +976,9 @@
      */
     static final class UserHandler extends Handler
             implements RemoteDisplayProviderWatcher.Callback,
-            RemoteDisplayProviderProxy.Callback {
+            RemoteDisplayProviderProxy.Callback,
+            MediaRoute2ProviderWatcher.Callback,
+            MediaRoute2ProviderProxy.Callback {
         public static final int MSG_START = 1;
         public static final int MSG_STOP = 2;
         public static final int MSG_UPDATE_DISCOVERY_REQUEST = 3;
@@ -794,6 +989,9 @@
         private static final int MSG_UPDATE_CLIENT_STATE = 8;
         private static final int MSG_CONNECTION_TIMED_OUT = 9;
 
+        private static final int MSG_SELECT_REMOTE_ROUTE = 10;
+        private static final int MSG_UPDATE_CLIENT_USAGE = 11;
+
         private static final int TIMEOUT_REASON_NOT_AVAILABLE = 1;
         private static final int TIMEOUT_REASON_CONNECTION_LOST = 2;
         private static final int TIMEOUT_REASON_WAITING_FOR_CONNECTING = 3;
@@ -809,11 +1007,17 @@
         private final MediaRouterService mService;
         private final UserRecord mUserRecord;
         private final RemoteDisplayProviderWatcher mWatcher;
+        private final MediaRoute2ProviderWatcher mMediaWatcher;
+
         private final ArrayList<ProviderRecord> mProviderRecords =
                 new ArrayList<ProviderRecord>();
         private final ArrayList<IMediaRouterClient> mTempClients =
                 new ArrayList<IMediaRouterClient>();
 
+        private final ArrayList<MediaRoute2ProviderProxy> mMediaProviders =
+                new ArrayList<>();
+        private final ArrayList<IMediaRouter2ManagerClient> mTempManagers = new ArrayList<>();
+
         private boolean mRunning;
         private int mDiscoveryMode = RemoteDisplayState.DISCOVERY_MODE_NONE;
         private RouteRecord mSelectedRouteRecord;
@@ -828,6 +1032,8 @@
             mUserRecord = userRecord;
             mWatcher = new RemoteDisplayProviderWatcher(service.mContext, this,
                     this, mUserRecord.mUserId);
+            mMediaWatcher = new MediaRoute2ProviderWatcher(service.mContext, this,
+                    this, mUserRecord.mUserId);
         }
 
         @Override
@@ -869,6 +1075,15 @@
                     connectionTimedOut();
                     break;
                 }
+                case MSG_SELECT_REMOTE_ROUTE: {
+                    Pair<Integer, String> obj = (Pair<Integer, String>) msg.obj;
+                    selectRemoteRoute(obj.first, obj.second);
+                    break;
+                }
+                case MSG_UPDATE_CLIENT_USAGE: {
+                    updateClientUsage((ClientRecord) msg.obj);
+                    break;
+                }
             }
         }
 
@@ -900,6 +1115,7 @@
             if (!mRunning) {
                 mRunning = true;
                 mWatcher.start(); // also starts all providers
+                mMediaWatcher.start();
             }
         }
 
@@ -908,6 +1124,7 @@
                 mRunning = false;
                 unselectSelectedRoute();
                 mWatcher.stop(); // also stops all providers
+                mMediaWatcher.stop();
             }
         }
 
@@ -1039,6 +1256,26 @@
             }
         }
 
+        @Override
+        public void addProvider(MediaRoute2ProviderProxy provider) {
+            provider.setCallback(this);
+            mMediaProviders.add(provider);
+        }
+
+        @Override
+        public void removeProvider(MediaRoute2ProviderProxy provider) {
+            mMediaProviders.remove(provider);
+        }
+
+        @Override
+        public void onProviderStateChanged(MediaRoute2ProviderProxy provider) {
+            updateProvider(provider);
+        }
+
+        private void updateProvider(MediaRoute2ProviderProxy provider) {
+            scheduleUpdateClientState();
+        }
+
         /**
          * This function is called whenever the state of the selected route may have changed.
          * It checks the state and updates timeouts or unselects the route as appropriate.
@@ -1149,6 +1386,17 @@
             unselectSelectedRoute();
         }
 
+        private void selectRemoteRoute(int uid, String routeId) {
+            if (routeId != null) {
+                final int providerCount = mMediaProviders.size();
+
+                //TODO: should find proper provider (currently assumes a single provider)
+                for (int i = 0; i < providerCount; ++i) {
+                    mMediaProviders.get(i).setSelectedRoute(uid, routeId);
+                }
+            }
+        }
+
         private void scheduleUpdateClientState() {
             if (!mClientStateUpdateScheduled) {
                 mClientStateUpdateScheduled = true;
@@ -1166,6 +1414,15 @@
                 mProviderRecords.get(i).appendClientState(routerState);
             }
 
+            //TODO: send provider info
+            int selectedUid = 0;
+            String selectedRouteId = null;
+            final int mediaCount = mMediaProviders.size();
+            for (int i = 0; i < mediaCount; i++) {
+                selectedUid = mMediaProviders.get(i).mSelectedUid;
+                selectedRouteId = mMediaProviders.get(i).mSelectedRouteId;
+            }
+
             try {
                 synchronized (mService.mLock) {
                     // Update the UserRecord.
@@ -1176,6 +1433,11 @@
                     for (int i = 0; i < count; i++) {
                         mTempClients.add(mUserRecord.mClientRecords.get(i).mClient);
                     }
+
+                    final int count2 = mUserRecord.mManagerRecords.size();
+                    for (int i = 0; i < count2; i++) {
+                        mTempManagers.add(mUserRecord.mManagerRecords.get(i).mClient);
+                    }
                 }
 
                 // Notify all clients (outside of the lock).
@@ -1187,9 +1449,39 @@
                         Slog.w(TAG, "Failed to call onStateChanged. Client probably died.");
                     }
                 }
+                //TODO: Call proper callbacks when provider descriptor is implemented.
+                final int count2 = mTempManagers.size();
+                for (int i = 0; i < count2; i++) {
+                    try {
+                        mTempManagers.get(i).onRouteSelected(selectedUid, selectedRouteId);
+                    } catch (RemoteException ex) {
+                        Slog.w(TAG, "Failed to call onStateChanged. Manager probably died.", ex);
+                    }
+                }
             } finally {
                 // Clear the list in preparation for the next time.
                 mTempClients.clear();
+                mTempManagers.clear();
+            }
+        }
+
+        private void updateClientUsage(ClientRecord clientRecord) {
+            List<IMediaRouter2ManagerClient> managers = new ArrayList<>();
+            synchronized (mService.mLock) {
+                final int count = mUserRecord.mManagerRecords.size();
+                for (int i = 0; i < count; i++) {
+                    managers.add(mUserRecord.mManagerRecords.get(i).mClient);
+                }
+            }
+            final int count = managers.size();
+            for (int i = 0; i < count; i++) {
+                try {
+                    managers.get(i).onControlCategoriesChanged(clientRecord.mUid,
+                            clientRecord.mControlCategories);
+                } catch (RemoteException ex) {
+                    Slog.w(TAG, "Failed to call onControlCategoriesChanged. "
+                            + "Manager probably died.", ex);
+                }
             }
         }
 
@@ -1576,4 +1868,5 @@
             }
         }
     }
+
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e43fc1f..dddb7ef 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4106,11 +4106,19 @@
         if (r == null) {
             return;
         }
-        if (mAssistants.isAdjustmentAllowed(adjustment.getKey())) {
-            if (adjustment.getSignals() != null) {
-                Bundle.setDefusable(adjustment.getSignals(), true);
-                r.addAdjustment(adjustment);
+        if (adjustment.getSignals() != null) {
+            final Bundle adjustments = adjustment.getSignals();
+            Bundle.setDefusable(adjustments, true);
+            List<String> toRemove = new ArrayList<>();
+            for (String potentialKey : adjustments.keySet()) {
+                if (!mAssistants.isAdjustmentAllowed(potentialKey)) {
+                    toRemove.add(potentialKey);
+                }
             }
+            for (String removeKey : toRemove) {
+                adjustments.remove(removeKey);
+            }
+            r.addAdjustment(adjustment);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 66b530f..f1d4524 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -476,6 +476,7 @@
                     mResolvedBaseFile.getAbsolutePath() : null;
             info.progress = mProgress;
             info.sealed = mSealed;
+            info.isCommitted = mCommitted;
             info.active = mActiveCount.get() > 0;
 
             info.mode = params.mode;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 73b000e..1a33b16 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1141,11 +1141,22 @@
                     switch (userStatus) {
                         case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
                             if (!verified) {
-                                // updatedStatus is already UNDEFINED
-                                needUpdate = true;
+                                // Don't demote if sysconfig says 'always'
+                                SystemConfig systemConfig = SystemConfig.getInstance();
+                                ArraySet<String> packages = systemConfig.getLinkedApps();
+                                if (!packages.contains(packageName)) {
+                                    // updatedStatus is already UNDEFINED
+                                    needUpdate = true;
 
-                                if (DEBUG_DOMAIN_VERIFICATION) {
-                                    Slog.d(TAG, "Formerly validated but now failing; demoting");
+                                    if (DEBUG_DOMAIN_VERIFICATION) {
+                                        Slog.d(TAG, "Formerly validated but now failing; demoting");
+                                    }
+                                } else {
+                                    if (DEBUG_DOMAIN_VERIFICATION) {
+                                        Slog.d(TAG, "Updating bundled package " + packageName
+                                                + " failed autoVerify, but sysconfig supersedes");
+                                    }
+                                    // leave needUpdate == false here intentionally
                                 }
                             }
                             break;
@@ -17789,6 +17800,12 @@
     @Override
     public boolean isPackageDeviceAdminOnAnyUser(String packageName) {
         final int callingUid = Binder.getCallingUid();
+        if (checkUidPermission(android.Manifest.permission.MANAGE_USERS, callingUid)
+                != PERMISSION_GRANTED) {
+            EventLog.writeEvent(0x534e4554, "128599183", -1, "");
+            throw new SecurityException(android.Manifest.permission.MANAGE_USERS
+                    + " permission is required to call this API");
+        }
         if (getInstantAppPackageName(callingUid) != null
                 && !isCallerSameApp(packageName, callingUid)) {
             return false;
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 0a17e130..212df43 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -189,11 +189,8 @@
     @Deprecated
     private static final Set<String> STORAGE_PERMISSIONS = new ArraySet<>();
     static {
-        // STOPSHIP(b/112545973): remove once feature enabled by default
-        if (!StorageManager.hasIsolatedStorage()) {
-            STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
-            STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
-        }
+        STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+        STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
     }
 
     private static final Set<String> MEDIA_AURAL_PERMISSIONS = new ArraySet<>();
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 55af357..1c5c7a3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3807,6 +3807,9 @@
             }
 
             case KeyEvent.KEYCODE_POWER: {
+                Slog.d(TAG, "interceptKeyBeforeQueueing: KEYCODE_POWER "
+                        + KeyEvent.actionToString(event.getAction())
+                        + " mPowerKeyHandled=" + mPowerKeyHandled + " b/128933363");
                 // Any activity on the power button stops the accessibility shortcut
                 cancelPendingAccessibilityShortcutAction();
                 result &= ~ACTION_PASS_TO_USER;
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 1800433..28a853f 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1724,7 +1724,7 @@
             throw new IllegalStateException("mKernelCpuThreadReader is null");
         }
         ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsages =
-                this.mKernelCpuThreadReader.getProcessCpuUsageByUids();
+                this.mKernelCpuThreadReader.getProcessCpuUsage();
         if (processCpuUsages == null) {
             throw new IllegalStateException("processCpuUsages is null");
         }
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 426122a..987d46a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -40,6 +40,8 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Build.VERSION_CODES.O_MR1;
 import static android.os.Build.VERSION_CODES.P;
+import static android.service.notification.Adjustment.KEY_IMPORTANCE;
+import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
 
@@ -492,6 +494,19 @@
         return answers;
     }
 
+    private void clearDeviceConfig() {
+        DeviceConfig.resetToDefaults(
+                Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI);
+    }
+
+    private void setDefaultAssistantInDeviceConfig(String componentName) {
+        DeviceConfig.setProperty(
+                DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE,
+                componentName,
+                false);
+    }
+
     @Test
     public void testCreateNotificationChannels_SingleChannel() throws Exception {
         final NotificationChannel channel =
@@ -831,7 +846,7 @@
         mService.addEnqueuedNotification(r);
 
         Bundle bundle = new Bundle();
-        bundle.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_NONE);
+        bundle.putInt(KEY_IMPORTANCE, IMPORTANCE_NONE);
         Adjustment adjustment = new Adjustment(
                 r.sbn.getPackageName(), r.getKey(), bundle, "", r.getUser().getIdentifier());
         mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
@@ -2826,7 +2841,7 @@
         mService.setHandler(handler);
 
         Bundle signals = new Bundle();
-        signals.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_NONE);
+        signals.putInt(KEY_IMPORTANCE, IMPORTANCE_NONE);
         Adjustment adjustment = new Adjustment(
                 r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
         when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
@@ -2867,7 +2882,7 @@
         when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
 
         Bundle signals = new Bundle();
-        signals.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_LOW);
+        signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
         Adjustment adjustment = new Adjustment(
                 r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
         mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
@@ -2885,13 +2900,13 @@
         when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
 
         Bundle signals = new Bundle();
-        signals.putInt(Adjustment.KEY_IMPORTANCE, IMPORTANCE_LOW);
+        signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
         Adjustment adjustment = new Adjustment(
                 r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
         mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
 
         assertEquals(IMPORTANCE_DEFAULT, r.getImportance());
-        assertFalse(r.hasAdjustment(Adjustment.KEY_IMPORTANCE));
+        assertFalse(r.hasAdjustment(KEY_IMPORTANCE));
     }
 
     @Test
@@ -4275,18 +4290,7 @@
                 .onGranted(eq(xmlConfig), eq(0), eq(true));
     }
 
-    private void clearDeviceConfig() {
-        DeviceConfig.resetToDefaults(
-                Settings.RESET_MODE_PACKAGE_DEFAULTS, DeviceConfig.NAMESPACE_SYSTEMUI);
-    }
 
-    private void setDefaultAssistantInDeviceConfig(String componentName) {
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE,
-                componentName,
-                false);
-    }
 
     public void testGetAllowedAssistantCapabilities() throws Exception {
         List<String> capabilities = mBinderService.getAllowedAssistantCapabilities(null);
@@ -4301,4 +4305,23 @@
             assertFalse(currentCapabilities.contains(capability));
         }
     }
+
+    public void testAdjustRestrictedKey() throws Exception {
+        NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+
+        when(mAssistants.isAdjustmentAllowed(KEY_IMPORTANCE)).thenReturn(true);
+        when(mAssistants.isAdjustmentAllowed(KEY_USER_SENTIMENT)).thenReturn(false);
+
+        Bundle signals = new Bundle();
+        signals.putInt(KEY_IMPORTANCE, IMPORTANCE_LOW);
+        signals.putInt(KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
+        Adjustment adjustment = new Adjustment(r.sbn.getPackageName(), r.getKey(), signals,
+               "", r.getUser().getIdentifier());
+
+        mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+        r.applyAdjustments();
+
+        assertEquals(IMPORTANCE_LOW, r.getAssistantImportance());
+        assertEquals(USER_SENTIMENT_NEUTRAL, r.getUserSentiment());
+    }
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 5df7bf7..d5245e3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1321,7 +1321,7 @@
 
     /**
      * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} to indicate the
-     * subscription which has changed.
+     * subscription which has changed; or in general whenever a subscription ID needs specified.
      */
     public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
 
@@ -1439,13 +1439,24 @@
 
     /**
      * Integer intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED}
-     * to indicate whether a SIM selection is needed to choose default subscription.
+     * to indicate what type of SIM selection is needed.
      *
      * @hide
      */
     public static final String EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE =
             "android.telephony.extra.DEFAULT_SUBSCRIPTION_SELECT_TYPE";
 
+    /** @hide */
+    @IntDef({
+            EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE,
+            EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA,
+            EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_VOICE,
+            EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_SMS,
+            EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DefaultSubscriptionSelectType{}
+
     /**
      * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
      * to indicate there's no need to re-select any default subscription.
@@ -1477,20 +1488,11 @@
     /**
      * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE}
      * to indicate user to decide whether current SIM should be preferred for all
-     * data / voice / sms.
+     * data / voice / sms. {@link #EXTRA_SUBSCRIPTION_ID} will specified to indicate
+     * which subscription should be the default subscription.
      * @hide
      */
-    public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_FOR_ALL_TYPES = 4;
-
-    /**
-     * Integer intent extra to be used with
-     * {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_FOR_ALL_TYPES}
-     * to indicate which SIM is being selected.
-     *
-     * @hide
-     */
-    public static final String EXTRA_DEFAULT_SUBSCRIPTION_ID =
-            "android.telephony.extra.DEFAULT_SUBSCRIPTION_ID";
+    public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4;
 
     //
     //
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index 4886a3f..d93e582 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -123,6 +123,19 @@
                 context, TELEPHONY_SUPPLIER, subId, pid, uid, callingPackage, message);
     }
 
+    /**
+     * Check whether the calling packages has carrier privileges for the passing subscription.
+     * @return {@code true} if the caller has carrier privileges, {@false} otherwise.
+     */
+    public static boolean checkCarrierPrivilegeForSubId(int subId) {
+        if (SubscriptionManager.isValidSubscriptionId(subId)
+                && getCarrierPrivilegeStatus(TELEPHONY_SUPPLIER, subId, Binder.getCallingUid())
+                == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+            return true;
+        }
+        return false;
+    }
+
     @VisibleForTesting
     public static boolean checkReadPhoneState(
             Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
@@ -204,9 +217,7 @@
         }
         // Calling packages with carrier privileges will also have access to device identifiers, but
         // this may be removed in a future release.
-        if (SubscriptionManager.isValidSubscriptionId(subId) && getCarrierPrivilegeStatus(
-                TELEPHONY_SUPPLIER, subId, uid)
-                == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+        if (checkCarrierPrivilegeForSubId(subId)) {
             return true;
         }
         // else the calling package is not authorized to access the device identifiers; call
@@ -243,9 +254,7 @@
         }
         // If the calling package has carrier privileges then allow access to the subscriber
         // identifiers.
-        if (SubscriptionManager.isValidSubscriptionId(subId) && getCarrierPrivilegeStatus(
-                TELEPHONY_SUPPLIER, subId, uid)
-                == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+        if (checkCarrierPrivilegeForSubId(subId)) {
             return true;
         }
         return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage,
@@ -365,9 +374,7 @@
                         uid) == PackageManager.PERMISSION_GRANTED) {
                     return false;
                 }
-                if (SubscriptionManager.isValidSubscriptionId(subId)
-                        && getCarrierPrivilegeStatus(TELEPHONY_SUPPLIER, subId, uid)
-                        == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+                if (checkCarrierPrivilegeForSubId(subId)) {
                     return false;
                 }
             }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1151214..e881ad4 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -212,7 +212,6 @@
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
@@ -4022,8 +4021,13 @@
         callback3.expectStopped();
     }
 
+    @FunctionalInterface
+    private interface ThrowingConsumer<T> {
+        void accept(T t) throws Exception;
+    }
+
     // Helper method to prepare the executor and run test
-    private void runTestWithSerialExecutors(Consumer<Executor> functor) {
+    private void runTestWithSerialExecutors(ThrowingConsumer<Executor> functor) throws Exception {
         final ExecutorService executorSingleThread = Executors.newSingleThreadExecutor();
         final Executor executorInline = (Runnable r) -> r.run();
         functor.accept(executorSingleThread);
@@ -4032,15 +4036,9 @@
     }
 
     @Test
-    public void testNattSocketKeepalives() {
-        runTestWithSerialExecutors(executor -> {
-            try {
-                doTestNattSocketKeepalivesWithExecutor(executor);
-                doTestNattSocketKeepalivesFdWithExecutor(executor);
-            } catch (Exception e) {
-                fail(e.getMessage());
-            }
-        });
+    public void testNattSocketKeepalives() throws Exception {
+        runTestWithSerialExecutors(executor -> doTestNattSocketKeepalivesWithExecutor(executor));
+        runTestWithSerialExecutors(executor -> doTestNattSocketKeepalivesFdWithExecutor(executor));
     }
 
     private void doTestNattSocketKeepalivesWithExecutor(Executor executor) throws Exception {
@@ -4210,14 +4208,8 @@
     }
 
     @Test
-    public void testTcpSocketKeepalives() {
-        runTestWithSerialExecutors(executor -> {
-            try {
-                doTestTcpSocketKeepalivesWithExecutor(executor);
-            } catch (Exception e) {
-                fail(e.getMessage());
-            }
-        });
+    public void testTcpSocketKeepalives() throws Exception {
+        runTestWithSerialExecutors(executor -> doTestTcpSocketKeepalivesWithExecutor(executor));
     }
 
     private void doTestTcpSocketKeepalivesWithExecutor(Executor executor) throws Exception {
diff --git a/tools/bit/main.cpp b/tools/bit/main.cpp
index 0f555b1..1a91f52 100644
--- a/tools/bit/main.cpp
+++ b/tools/bit/main.cpp
@@ -856,7 +856,7 @@
             // TODO: if (!apk.file.fileInfo.exists || apk.file.HasChanged())
             err = run_adb("shell", "mkdir", "-p", dir.c_str(), NULL);
             check_error(err);
-            err = run_adb("push", pushed.file.filename.c_str(), pushed.dest.c_str());
+            err = run_adb("push", pushed.file.filename.c_str(), pushed.dest.c_str(), NULL);
             check_error(err);
             // pushed.installed = true;
         }