Snap for 5033059 from 238c5b95e54d5dc13a777c4c2ddd4cead2355ab1 to qt-release

Change-Id: I25c89ca50cfa8979d4bbb0507921eb3cb469ba37
diff --git a/Android.bp b/Android.bp
index 3e471b6..a64cc81 100644
--- a/Android.bp
+++ b/Android.bp
@@ -706,7 +706,6 @@
 
     // Loaded with System.loadLibrary by android.view.textclassifier
     required: [
-        "libtextclassifier",
         "libmedia2_jni",
     ],
 
@@ -810,12 +809,16 @@
 java_library {
     name: "ext",
     installable: true,
-    no_framework_libs: true,
+    sdk_version: "core_current",
     static_libs: [
         "libphonenumber-platform",
         "nist-sip",
         "tagsoup",
         "rappor",
+        "libtextclassifier-java",
+    ],
+    required: [
+        "libtextclassifier",
     ],
     dxflags: ["--core-library"],
 }
diff --git a/api/current.txt b/api/current.txt
index 52257c0..f915bb3 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -42920,6 +42920,7 @@
     method public static int getSlotIndex(int);
     method public static int[] getSubscriptionIds(int);
     method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
+    method public boolean isActiveSubscriptionId(int);
     method public boolean isNetworkRoaming(int);
     method public static boolean isValidSubscriptionId(int);
     method public void removeOnOpportunisticSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
@@ -51407,7 +51408,7 @@
   }
 
   public abstract class CookieManager {
-    ctor public CookieManager();
+    ctor public deprecated CookieManager();
     method public abstract boolean acceptCookie();
     method public abstract boolean acceptThirdPartyCookies(android.webkit.WebView);
     method public static boolean allowFileSchemeCookies();
@@ -51507,13 +51508,13 @@
   }
 
   public abstract class RenderProcessGoneDetail {
-    ctor public RenderProcessGoneDetail();
+    ctor public deprecated RenderProcessGoneDetail();
     method public abstract boolean didCrash();
     method public abstract int rendererPriorityAtExit();
   }
 
   public abstract class SafeBrowsingResponse {
-    ctor public SafeBrowsingResponse();
+    ctor public deprecated SafeBrowsingResponse();
     method public abstract void backToSafety(boolean);
     method public abstract void proceed(boolean);
     method public abstract void showInterstitial(boolean);
@@ -51525,7 +51526,7 @@
   }
 
   public abstract class ServiceWorkerController {
-    ctor public ServiceWorkerController();
+    ctor public deprecated ServiceWorkerController();
     method public static android.webkit.ServiceWorkerController getInstance();
     method public abstract android.webkit.ServiceWorkerWebSettings getServiceWorkerWebSettings();
     method public abstract void setServiceWorkerClient(android.webkit.ServiceWorkerClient);
@@ -51574,7 +51575,7 @@
   }
 
   public abstract class TracingController {
-    ctor public TracingController();
+    ctor public deprecated TracingController();
     method public static android.webkit.TracingController getInstance();
     method public abstract boolean isTracing();
     method public abstract void start(android.webkit.TracingConfig);
@@ -52114,7 +52115,7 @@
   }
 
   public abstract class WebViewDatabase {
-    ctor public WebViewDatabase();
+    ctor public deprecated WebViewDatabase();
     method public abstract deprecated void clearFormData();
     method public abstract void clearHttpAuthUsernamePassword();
     method public abstract deprecated void clearUsernamePassword();
diff --git a/api/system-current.txt b/api/system-current.txt
index 1f74cbf..5785e4a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5439,6 +5439,7 @@
     method public boolean disableDataConnectivity();
     method public boolean enableDataConnectivity();
     method public void enableVideoCalling(boolean);
+    method public java.lang.String getAidForAppType(int);
     method public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
@@ -5454,6 +5455,7 @@
     method public deprecated boolean getDataEnabled(int);
     method public boolean getEmergencyCallbackMode();
     method public java.lang.String getIsimDomain();
+    method public int getPreferredNetworkType(int);
     method public int getSimApplicationState();
     method public int getSimCardState();
     method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
@@ -5463,10 +5465,7 @@
     method public boolean handlePinMmi(java.lang.String);
     method public boolean handlePinMmiForSubscriber(int, java.lang.String);
     method public boolean isDataConnectivityPossible();
-    method public deprecated boolean isIdle();
-    method public deprecated boolean isOffhook();
     method public deprecated boolean isRadioOn();
-    method public deprecated boolean isRinging();
     method public boolean isVideoCallingEnabled();
     method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
     method public boolean needsOtaServiceProvisioning();
@@ -5480,7 +5479,6 @@
     method public void setSimPowerStateForSlot(int, int);
     method public deprecated void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean);
     method public void setVoiceActivationState(int);
-    method public deprecated void silenceRinger();
     method public boolean supplyPin(java.lang.String);
     method public int[] supplyPinReportResult(java.lang.String);
     method public boolean supplyPuk(java.lang.String, java.lang.String);
@@ -5498,6 +5496,29 @@
     field public static final java.lang.String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
     field public static final java.lang.String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
     field public static final java.lang.String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
+    field public static final int NETWORK_MODE_CDMA_EVDO = 4; // 0x4
+    field public static final int NETWORK_MODE_CDMA_NO_EVDO = 5; // 0x5
+    field public static final int NETWORK_MODE_EVDO_NO_CDMA = 6; // 0x6
+    field public static final int NETWORK_MODE_GLOBAL = 7; // 0x7
+    field public static final int NETWORK_MODE_GSM_ONLY = 1; // 0x1
+    field public static final int NETWORK_MODE_GSM_UMTS = 3; // 0x3
+    field public static final int NETWORK_MODE_LTE_CDMA_EVDO = 8; // 0x8
+    field public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10; // 0xa
+    field public static final int NETWORK_MODE_LTE_GSM_WCDMA = 9; // 0x9
+    field public static final int NETWORK_MODE_LTE_ONLY = 11; // 0xb
+    field public static final int NETWORK_MODE_LTE_TDSCDMA = 15; // 0xf
+    field public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22; // 0x16
+    field public static final int NETWORK_MODE_LTE_TDSCDMA_GSM = 17; // 0x11
+    field public static final int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = 20; // 0x14
+    field public static final int NETWORK_MODE_LTE_TDSCDMA_WCDMA = 19; // 0x13
+    field public static final int NETWORK_MODE_LTE_WCDMA = 12; // 0xc
+    field public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 21; // 0x15
+    field public static final int NETWORK_MODE_TDSCDMA_GSM = 16; // 0x10
+    field public static final int NETWORK_MODE_TDSCDMA_GSM_WCDMA = 18; // 0x12
+    field public static final int NETWORK_MODE_TDSCDMA_ONLY = 13; // 0xd
+    field public static final int NETWORK_MODE_TDSCDMA_WCDMA = 14; // 0xe
+    field public static final int NETWORK_MODE_WCDMA_ONLY = 2; // 0x2
+    field public static final int NETWORK_MODE_WCDMA_PREF = 0; // 0x0
     field public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2; // 0x2
     field public static final int SIM_ACTIVATION_STATE_ACTIVATING = 1; // 0x1
     field public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3; // 0x3
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 9012c33..2246562 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -148,6 +148,10 @@
   public class TelephonyManager {
     method public deprecated void answerRingingCall();
     method public deprecated boolean endCall();
+    method public deprecated boolean isIdle();
+    method public deprecated boolean isOffhook();
+    method public deprecated boolean isRinging();
+    method public deprecated void silenceRinger();
   }
 
 }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index ffd7d31..1e9c354 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -80,7 +80,8 @@
         BatteryLevelChanged battery_level_changed = 30;
         ChargingStateChanged charging_state_changed = 31;
         PluggedStateChanged plugged_state_changed = 32;
-        // 33 - 34 are available
+        InteractiveStateChanged interactive_state_changed = 33;
+        // 34 is available
         WakeupAlarmOccurred wakeup_alarm_occurred = 35;
         KernelWakeupReported kernel_wakeup_reported = 36;
         WifiLockStateChanged wifi_lock_state_changed = 37;
@@ -665,6 +666,20 @@
 }
 
 /**
+ * Logs when the device is interactive, according to the PowerManager Notifier.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/power/Notifier.java
+ */
+message InteractiveStateChanged {
+    enum State {
+        OFF = 0;
+        ON = 1;
+    }
+    optional State state = 1;
+}
+
+/**
  * Logs Battery Saver state change.
  *
  * Logged from:
@@ -2220,6 +2235,11 @@
 
     // SWAP
     optional int64 swap_in_bytes = 8;
+
+    // RSS high watermark.
+    // Peak RSS usage of the process. Value is read from the VmHWM field in /proc/PID/status or
+    // from memory.max_usage_in_bytes under /dev/memcg if the device uses per-app memory cgroups.
+    optional int64 rss_high_watermark_in_bytes = 9;
 }
 
 /*
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 5a0172b..66392f8 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -169,7 +169,7 @@
           new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}},
         // process_memory_state
         {android::util::PROCESS_MEMORY_STATE,
-         {{4, 5, 6, 7, 8},
+         {{4, 5, 6, 7, 8, 9},
           {2, 3},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 0472461..ac16fd3 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -2076,27 +2076,20 @@
 Lcom/android/internal/telephony/ISub;->getDefaultSubId()I
 Lcom/android/internal/telephony/ISub;->setDefaultDataSubId(I)V
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->endCall()Z
-Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->endCallForSubscriber(I)Z
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String;
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->isRadioOn(Ljava/lang/String;)Z
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Lcom/android/internal/telephony/ITelephony$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephony;
 Lcom/android/internal/telephony/ITelephony$Stub;->DESCRIPTOR:Ljava/lang/String;
-Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_answerRingingCall:I
 Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_call:I
 Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_dial:I
-Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_endCall:I
 Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_getDeviceId:I
-Lcom/android/internal/telephony/ITelephony;->answerRingingCall()V
 Lcom/android/internal/telephony/ITelephony;->call(Ljava/lang/String;Ljava/lang/String;)V
 Lcom/android/internal/telephony/ITelephony;->dial(Ljava/lang/String;)V
 Lcom/android/internal/telephony/ITelephony;->disableDataConnectivity()Z
 Lcom/android/internal/telephony/ITelephony;->disableLocationUpdates()V
 Lcom/android/internal/telephony/ITelephony;->enableDataConnectivity()Z
 Lcom/android/internal/telephony/ITelephony;->enableLocationUpdates()V
-Lcom/android/internal/telephony/ITelephony;->endCall()Z
-Lcom/android/internal/telephony/ITelephony;->endCallForSubscriber(I)Z
 Lcom/android/internal/telephony/ITelephony;->getActivePhoneType()I
 Lcom/android/internal/telephony/ITelephony;->getCallState()I
 Lcom/android/internal/telephony/ITelephony;->getDataActivity()I
@@ -2108,12 +2101,8 @@
 Lcom/android/internal/telephony/ITelephony;->hasIccCard()Z
 Lcom/android/internal/telephony/ITelephony;->iccCloseLogicalChannel(II)Z
 Lcom/android/internal/telephony/ITelephony;->iccTransmitApduLogicalChannel(IIIIIIILjava/lang/String;)Ljava/lang/String;
-Lcom/android/internal/telephony/ITelephony;->isIdle(Ljava/lang/String;)Z
-Lcom/android/internal/telephony/ITelephony;->isIdleForSubscriber(ILjava/lang/String;)Z
 Lcom/android/internal/telephony/ITelephony;->isRadioOnForSubscriber(ILjava/lang/String;)Z
-Lcom/android/internal/telephony/ITelephony;->isRinging(Ljava/lang/String;)Z
 Lcom/android/internal/telephony/ITelephony;->setRadio(Z)Z
-Lcom/android/internal/telephony/ITelephony;->silenceRinger()V
 Lcom/android/internal/telephony/ITelephony;->supplyPin(Ljava/lang/String;)Z
 Lcom/android/internal/telephony/ITelephony;->toggleRadioOnOff()V
 Lcom/android/internal/telephony/ITelephony;->updateServiceLocation()V
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index be1f2db..294a3ec 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -19,11 +19,14 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
+import android.content.IIntentReceiver;
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.UserInfo;
+import android.os.Bundle;
 import android.os.IBinder;
+import android.os.TransactionTooLargeException;
 import android.view.RemoteAnimationAdapter;
 
 import java.util.ArrayList;
@@ -232,4 +235,14 @@
     public abstract void setBooted(boolean booted);
     public abstract boolean isBooted();
     public abstract void finishBooting();
+
+    public abstract void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid,
+            long duration, String tag);
+    public abstract int broadcastIntentInPackage(String packageName, int uid, Intent intent,
+            String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData,
+            Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized,
+            boolean sticky, int userId);
+    public abstract ComponentName startServiceInPackage(int uid, Intent service,
+            String resolvedType, boolean fgRequired, String callingPackage, int userId)
+            throws TransactionTooLargeException;
 }
diff --git a/core/java/android/app/ProcessMemoryState.java b/core/java/android/app/ProcessMemoryState.java
index e0aed16..9bfdae0 100644
--- a/core/java/android/app/ProcessMemoryState.java
+++ b/core/java/android/app/ProcessMemoryState.java
@@ -32,10 +32,11 @@
     public final long rssInBytes;
     public final long cacheInBytes;
     public final long swapInBytes;
+    public final long rssHighWatermarkInBytes;
 
     public ProcessMemoryState(int uid, String processName, int oomScore, long pgfault,
                               long pgmajfault, long rssInBytes, long cacheInBytes,
-                              long swapInBytes) {
+                              long swapInBytes, long rssHighWatermarkInBytes) {
         this.uid = uid;
         this.processName = processName;
         this.oomScore = oomScore;
@@ -44,6 +45,7 @@
         this.rssInBytes = rssInBytes;
         this.cacheInBytes = cacheInBytes;
         this.swapInBytes = swapInBytes;
+        this.rssHighWatermarkInBytes = rssHighWatermarkInBytes;
     }
 
     private ProcessMemoryState(Parcel in) {
@@ -55,6 +57,7 @@
         rssInBytes = in.readLong();
         cacheInBytes = in.readLong();
         swapInBytes = in.readLong();
+        rssHighWatermarkInBytes = in.readLong();
     }
 
     public static final Creator<ProcessMemoryState> CREATOR = new Creator<ProcessMemoryState>() {
@@ -84,5 +87,6 @@
         parcel.writeLong(rssInBytes);
         parcel.writeLong(cacheInBytes);
         parcel.writeLong(swapInBytes);
+        parcel.writeLong(rssHighWatermarkInBytes);
     }
 }
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index a64eead..8d9533e 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -331,7 +331,6 @@
             for (int i = 0; i < numOperations; i++) {
                 ContentProviderOperation operation = operations.get(i);
                 Uri uri = operation.getUri();
-                userIds[i] = getUserIdFromUri(uri);
                 uri = validateIncomingUri(uri);
                 uri = maybeGetUriWithoutUserId(uri);
                 // Rebuild operation if we changed the Uri above
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 553511d..edd09f8 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -40,7 +40,7 @@
  *
  * @hide
  */
-abstract class ApkVerityBuilder {
+public abstract class ApkVerityBuilder {
     private ApkVerityBuilder() {}
 
     private static final int CHUNK_SIZE_BYTES = 4096;  // Typical Linux block size
@@ -51,12 +51,18 @@
     private static final String JCA_DIGEST_ALGORITHM = "SHA-256";
     private static final byte[] DEFAULT_SALT = new byte[8];
 
-    static class ApkVerityResult {
+    /** Result generated by the builder. */
+    public static class ApkVerityResult {
+        /** Raw fs-verity metadata and Merkle tree ready to be deployed on disk. */
         public final ByteBuffer verityData;
+
+        /** Size of the Merkle tree in {@code verityData}. */
         public final int merkleTreeSize;
+
+        /** Root hash of the Merkle tree. */
         public final byte[] rootHash;
 
-        ApkVerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) {
+        private ApkVerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) {
             this.verityData = verityData;
             this.merkleTreeSize = merkleTreeSize;
             this.rootHash = rootHash;
@@ -65,19 +71,47 @@
 
     /**
      * Generates the 4k, SHA-256 based Merkle tree for the given APK and stores in the {@link
-     * ByteBuffer} created by the {@link ByteBufferFactory}.  The Merkle tree is suitable to be used
-     * as the on-disk format for apk-verity.
+     * ByteBuffer} created by the {@link ByteBufferFactory}.  The output is suitable to be used as
+     * the on-disk format for fs-verity to use.
      *
      * @return ApkVerityResult containing a buffer with the generated Merkle tree stored at the
      *         front, the tree size, and the calculated root hash.
      */
     @NonNull
-    static ApkVerityResult generateApkVerityTree(@NonNull RandomAccessFile apk,
+    public static ApkVerityResult generateFsVerityTree(@NonNull RandomAccessFile apk,
+            @NonNull ByteBufferFactory bufferFactory)
+            throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
+        return generateVerityTree(apk, bufferFactory, null /* signatureInfo */,
+                false /* skipSigningBlock */);
+    }
+
+    /**
+     * Generates the 4k, SHA-256 based Merkle tree for the given APK and stores in the {@link
+     * ByteBuffer} created by the {@link ByteBufferFactory}.  The Merkle tree does not cover Signing
+     * Block specificed in {@code signatureInfo}.  The output is suitable to be used as the on-disk
+     * format for fs-verity to use (with elide and patch extensions).
+     *
+     * @return ApkVerityResult containing a buffer with the generated Merkle tree stored at the
+     *         front, the tree size, and the calculated root hash.
+     */
+    @NonNull
+    public static ApkVerityResult generateApkVerityTree(@NonNull RandomAccessFile apk,
             @Nullable SignatureInfo signatureInfo, @NonNull ByteBufferFactory bufferFactory)
             throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
-        long signingBlockSize =
-                signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
-        long dataSize = apk.length() - signingBlockSize;
+        return generateVerityTree(apk, bufferFactory, signatureInfo, true /* skipSigningBlock */);
+    }
+
+    @NonNull
+    private static ApkVerityResult generateVerityTree(@NonNull RandomAccessFile apk,
+            @NonNull ByteBufferFactory bufferFactory, @Nullable SignatureInfo signatureInfo,
+            boolean skipSigningBlock)
+            throws IOException, SecurityException, NoSuchAlgorithmException, DigestException {
+        long dataSize = apk.length();
+        if (skipSigningBlock) {
+            long signingBlockSize =
+                    signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
+            dataSize -= signingBlockSize;
+        }
         int[] levelOffset = calculateVerityLevelOffset(dataSize);
         int merkleTreeSize = levelOffset[levelOffset.length - 1];
 
@@ -85,10 +119,11 @@
                 merkleTreeSize
                 + CHUNK_SIZE_BYTES);  // maximum size of apk-verity metadata
         output.order(ByteOrder.LITTLE_ENDIAN);
-
         ByteBuffer tree = slice(output, 0, merkleTreeSize);
-        byte[] apkRootHash = generateApkVerityTreeInternal(apk, signatureInfo, DEFAULT_SALT,
-                levelOffset, tree);
+        // Only use default salt in legacy case.
+        byte[] salt = skipSigningBlock ? DEFAULT_SALT : null;
+        byte[] apkRootHash = generateVerityTreeInternal(apk, signatureInfo, salt, levelOffset,
+                tree, skipSigningBlock);
         return new ApkVerityResult(output, merkleTreeSize, apkRootHash);
     }
 
@@ -138,7 +173,8 @@
             throws IOException, SignatureNotFoundException, SecurityException, DigestException,
                    NoSuchAlgorithmException {
         try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
-            ApkVerityResult result = generateApkVerityTree(apk, signatureInfo, bufferFactory);
+            ApkVerityResult result = generateVerityTree(apk, bufferFactory, signatureInfo,
+                    true /* skipSigningBlock */);
             ByteBuffer footer = slice(result.verityData, result.merkleTreeSize,
                     result.verityData.limit());
             generateApkVerityFooter(apk, signatureInfo, footer);
@@ -170,11 +206,14 @@
         private final byte[] mDigestBuffer = new byte[DIGEST_SIZE_BYTES];
         private final byte[] mSalt;
 
-        private BufferedDigester(byte[] salt, ByteBuffer output) throws NoSuchAlgorithmException {
+        private BufferedDigester(@Nullable byte[] salt, @NonNull ByteBuffer output)
+                throws NoSuchAlgorithmException {
             mSalt = salt;
             mOutput = output.slice();
             mMd = MessageDigest.getInstance(JCA_DIGEST_ALGORITHM);
-            mMd.update(mSalt);
+            if (mSalt != null) {
+                mMd.update(mSalt);
+            }
             mBytesDigestedSinceReset = 0;
         }
 
@@ -201,7 +240,9 @@
                     mMd.digest(mDigestBuffer, 0, mDigestBuffer.length);
                     mOutput.put(mDigestBuffer);
                     // After digest, MessageDigest resets automatically, so no need to reset again.
-                    mMd.update(mSalt);
+                    if (mSalt != null) {
+                        mMd.update(mSalt);
+                    }
                     mBytesDigestedSinceReset = 0;
                 }
             }
@@ -242,6 +283,26 @@
     // thus the syscall overhead is not too big.
     private static final int MMAP_REGION_SIZE_BYTES = 1024 * 1024;
 
+    private static void generateFsVerityDigestAtLeafLevel(RandomAccessFile file, ByteBuffer output)
+            throws IOException, NoSuchAlgorithmException, DigestException {
+        BufferedDigester digester = new BufferedDigester(null /* salt */, output);
+
+        // 1. Digest the whole file by chunks.
+        consumeByChunk(digester,
+                new MemoryMappedFileDataSource(file.getFD(), 0, file.length()),
+                MMAP_REGION_SIZE_BYTES);
+
+        // 2. Pad 0s up to the nearest 4096-byte block before hashing.
+        int lastIncompleteChunkSize = (int) (file.length() % CHUNK_SIZE_BYTES);
+        if (lastIncompleteChunkSize != 0) {
+            digester.consume(ByteBuffer.allocate(CHUNK_SIZE_BYTES - lastIncompleteChunkSize));
+        }
+        digester.assertEmptyBuffer();
+
+        // 3. Fill up the rest of buffer with 0s.
+        digester.fillUpLastOutputChunk();
+    }
+
     private static void generateApkVerityDigestAtLeafLevel(RandomAccessFile apk,
             SignatureInfo signatureInfo, byte[] salt, ByteBuffer output)
             throws IOException, NoSuchAlgorithmException, DigestException {
@@ -288,15 +349,19 @@
     }
 
     @NonNull
-    private static byte[] generateApkVerityTreeInternal(@NonNull RandomAccessFile apk,
-            @Nullable SignatureInfo signatureInfo, @NonNull byte[] salt,
-            @NonNull int[] levelOffset, @NonNull ByteBuffer output)
+    private static byte[] generateVerityTreeInternal(@NonNull RandomAccessFile apk,
+            @Nullable SignatureInfo signatureInfo, @Nullable byte[] salt,
+            @NonNull int[] levelOffset, @NonNull ByteBuffer output, boolean skipSigningBlock)
             throws IOException, NoSuchAlgorithmException, DigestException {
-        assertSigningBlockAlignedAndHasFullPages(signatureInfo);
-
         // 1. Digest the apk to generate the leaf level hashes.
-        generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output,
-                    levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
+        if (skipSigningBlock) {
+            assertSigningBlockAlignedAndHasFullPages(signatureInfo);
+            generateApkVerityDigestAtLeafLevel(apk, signatureInfo, salt, slice(output,
+                        levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
+        } else {
+            generateFsVerityDigestAtLeafLevel(apk, slice(output,
+                        levelOffset[levelOffset.length - 2], levelOffset[levelOffset.length - 1]));
+        }
 
         // 2. Digest the lower level hashes bottom up.
         for (int level = levelOffset.length - 3; level >= 0; level--) {
@@ -434,10 +499,11 @@
         return levelOffset;
     }
 
-    private static void assertSigningBlockAlignedAndHasFullPages(SignatureInfo signatureInfo) {
+    private static void assertSigningBlockAlignedAndHasFullPages(
+            @NonNull SignatureInfo signatureInfo) {
         if (signatureInfo.apkSigningBlockOffset % CHUNK_SIZE_BYTES != 0) {
             throw new IllegalArgumentException(
-                    "APK Signing Block does not start at the page  boundary: "
+                    "APK Signing Block does not start at the page boundary: "
                     + signatureInfo.apkSigningBlockOffset);
         }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2ee7ab9..9be9ed0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -239,6 +239,12 @@
     final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList<>();
     @UnsupportedAppUsage
     final Context mContext;
+    /**
+     * TODO(b/116349163): Check if we can merge this into {@link #mContext}.
+     */
+    @NonNull
+    private Context mDisplayContext;
+
     @UnsupportedAppUsage
     final IWindowSession mWindowSession;
     @NonNull Display mDisplay;
@@ -532,6 +538,7 @@
 
     public ViewRootImpl(Context context, Display display) {
         mContext = context;
+        mDisplayContext = context.createDisplayContext(display);
         mWindowSession = WindowManagerGlobal.getWindowSession();
         mDisplay = display;
         mBasePackageName = context.getBasePackageName();
@@ -1249,6 +1256,7 @@
         } else {
             mDisplay = preferredDisplay;
         }
+        mDisplayContext = mContext.createDisplayContext(mDisplay);
     }
 
     void pokeDrawLockIfNeeded() {
@@ -2579,7 +2587,7 @@
                     .mayUseInputMethod(mWindowAttributes.flags);
             if (imTarget != mLastWasImTarget) {
                 mLastWasImTarget = imTarget;
-                InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+                InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class);
                 if (imm != null && imTarget) {
                     imm.onPreWindowFocus(mView, hasWindowFocus);
                     imm.onPostWindowFocus(mView, mView.findFocus(),
@@ -2695,7 +2703,7 @@
             mLastWasImTarget = WindowManager.LayoutParams
                     .mayUseInputMethod(mWindowAttributes.flags);
 
-            InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+            InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class);
             if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
                 imm.onPreWindowFocus(mView, hasWindowFocus);
             }
@@ -4329,7 +4337,8 @@
                     enqueueInputEvent(event, null, 0, true);
                 } break;
                 case MSG_CHECK_FOCUS: {
-                    InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+                    InputMethodManager imm =
+                            mDisplayContext.getSystemService(InputMethodManager.class);
                     if (imm != null) {
                         imm.checkFocus();
                     }
@@ -4871,7 +4880,7 @@
         @Override
         protected int onProcess(QueuedInputEvent q) {
             if (mLastWasImTarget && !isInLocalFocusMode()) {
-                InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+                InputMethodManager imm = mDisplayContext.getSystemService(InputMethodManager.class);
                 if (imm != null) {
                     final InputEvent event = q.mEvent;
                     if (DEBUG_IMF) Log.v(mTag, "Sending input event to IME: " + event);
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 29339e9..3e240cf 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -44,6 +44,8 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 
+import com.google.android.textclassifier.AnnotatorModel;
+
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -92,7 +94,7 @@
     @GuardedBy("mLock") // Do not access outside this lock.
     private ModelFile mModel;
     @GuardedBy("mLock") // Do not access outside this lock.
-    private TextClassifierImplNative mNative;
+    private AnnotatorModel mNative;
 
     private final Object mLoggerLock = new Object();
     @GuardedBy("mLoggerLock") // Do not access outside this lock.
@@ -125,7 +127,7 @@
                     && rangeLength <= mSettings.getSuggestSelectionMaxRangeLength()) {
                 final String localesString = concatenateLocales(request.getDefaultLocales());
                 final ZonedDateTime refTime = ZonedDateTime.now();
-                final TextClassifierImplNative nativeImpl = getNative(request.getDefaultLocales());
+                final AnnotatorModel nativeImpl = getNative(request.getDefaultLocales());
                 final int start;
                 final int end;
                 if (mSettings.isModelDarkLaunchEnabled() && !request.isDarkLaunchAllowed()) {
@@ -134,7 +136,7 @@
                 } else {
                     final int[] startEnd = nativeImpl.suggestSelection(
                             string, request.getStartIndex(), request.getEndIndex(),
-                            new TextClassifierImplNative.SelectionOptions(localesString));
+                            new AnnotatorModel.SelectionOptions(localesString));
                     start = startEnd[0];
                     end = startEnd[1];
                 }
@@ -142,10 +144,10 @@
                         && start >= 0 && end <= string.length()
                         && start <= request.getStartIndex() && end >= request.getEndIndex()) {
                     final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end);
-                    final TextClassifierImplNative.ClassificationResult[] results =
+                    final AnnotatorModel.ClassificationResult[] results =
                             nativeImpl.classifyText(
                                     string, start, end,
-                                    new TextClassifierImplNative.ClassificationOptions(
+                                    new AnnotatorModel.ClassificationOptions(
                                             refTime.toInstant().toEpochMilli(),
                                             refTime.getZone().getId(),
                                             localesString));
@@ -184,11 +186,11 @@
                 final String localesString = concatenateLocales(request.getDefaultLocales());
                 final ZonedDateTime refTime = request.getReferenceTime() != null
                         ? request.getReferenceTime() : ZonedDateTime.now();
-                final TextClassifierImplNative.ClassificationResult[] results =
+                final AnnotatorModel.ClassificationResult[] results =
                         getNative(request.getDefaultLocales())
                                 .classifyText(
                                         string, request.getStartIndex(), request.getEndIndex(),
-                                        new TextClassifierImplNative.ClassificationOptions(
+                                        new AnnotatorModel.ClassificationOptions(
                                                 refTime.toInstant().toEpochMilli(),
                                                 refTime.getZone().getId(),
                                                 localesString));
@@ -228,17 +230,17 @@
                     ? request.getEntityConfig().resolveEntityListModifications(
                             getEntitiesForHints(request.getEntityConfig().getHints()))
                     : mSettings.getEntityListDefault();
-            final TextClassifierImplNative nativeImpl =
+            final AnnotatorModel nativeImpl =
                     getNative(request.getDefaultLocales());
-            final TextClassifierImplNative.AnnotatedSpan[] annotations =
+            final AnnotatorModel.AnnotatedSpan[] annotations =
                     nativeImpl.annotate(
                         textString,
-                        new TextClassifierImplNative.AnnotationOptions(
+                        new AnnotatorModel.AnnotationOptions(
                                 refTime.toInstant().toEpochMilli(),
                                         refTime.getZone().getId(),
                                 concatenateLocales(request.getDefaultLocales())));
-            for (TextClassifierImplNative.AnnotatedSpan span : annotations) {
-                final TextClassifierImplNative.ClassificationResult[] results =
+            for (AnnotatorModel.AnnotatedSpan span : annotations) {
+                final AnnotatorModel.ClassificationResult[] results =
                         span.getClassification();
                 if (results.length == 0
                         || !entitiesToIdentify.contains(results[0].getCollection())) {
@@ -297,7 +299,7 @@
         }
     }
 
-    private TextClassifierImplNative getNative(LocaleList localeList)
+    private AnnotatorModel getNative(LocaleList localeList)
             throws FileNotFoundException {
         synchronized (mLock) {
             localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
@@ -310,7 +312,7 @@
                 destroyNativeIfExistsLocked();
                 final ParcelFileDescriptor fd = ParcelFileDescriptor.open(
                         new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
-                mNative = new TextClassifierImplNative(fd.getFd());
+                mNative = new AnnotatorModel(fd.getFd());
                 closeAndLogError(fd);
                 mModel = bestModel;
             }
@@ -398,14 +400,14 @@
     }
 
     private TextClassification createClassificationResult(
-            TextClassifierImplNative.ClassificationResult[] classifications,
+            AnnotatorModel.ClassificationResult[] classifications,
             String text, int start, int end, @Nullable Instant referenceTime) {
         final String classifiedText = text.substring(start, end);
         final TextClassification.Builder builder = new TextClassification.Builder()
                 .setText(classifiedText);
 
         final int size = classifications.length;
-        TextClassifierImplNative.ClassificationResult highestScoringResult = null;
+        AnnotatorModel.ClassificationResult highestScoringResult = null;
         float highestScore = Float.MIN_VALUE;
         for (int i = 0; i < size; i++) {
             builder.setEntityType(classifications[i].getCollection(),
@@ -486,9 +488,9 @@
             try {
                 final ParcelFileDescriptor modelFd = ParcelFileDescriptor.open(
                         file, ParcelFileDescriptor.MODE_READ_ONLY);
-                final int version = TextClassifierImplNative.getVersion(modelFd.getFd());
+                final int version = AnnotatorModel.getVersion(modelFd.getFd());
                 final String supportedLocalesStr =
-                        TextClassifierImplNative.getLocales(modelFd.getFd());
+                        AnnotatorModel.getLocales(modelFd.getFd());
                 if (supportedLocalesStr.isEmpty()) {
                     Log.d(DEFAULT_LOG_TAG, "Ignoring " + file.getAbsolutePath());
                     return null;
@@ -676,7 +678,7 @@
         public static List<LabeledIntent> create(
                 Context context,
                 @Nullable Instant referenceTime,
-                TextClassifierImplNative.ClassificationResult classification,
+                AnnotatorModel.ClassificationResult classification,
                 String text) {
             final String type = classification.getCollection().trim().toLowerCase(Locale.ENGLISH);
             text = text.trim();
diff --git a/core/java/android/view/textclassifier/TextClassifierImplNative.java b/core/java/android/view/textclassifier/TextClassifierImplNative.java
deleted file mode 100644
index 3d4c8f2..0000000
--- a/core/java/android/view/textclassifier/TextClassifierImplNative.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright (C) 2017 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.view.textclassifier;
-
-import android.content.res.AssetFileDescriptor;
-
-/**
- * Java wrapper for TextClassifier native library interface. This library is used for detecting
- * entities in text.
- */
-final class TextClassifierImplNative {
-
-    static {
-        System.loadLibrary("textclassifier");
-    }
-
-    private final long mModelPtr;
-
-    /**
-     * Creates a new instance of TextClassifierImplNative, using the provided model image, given as
-     * a file descriptor.
-     */
-    TextClassifierImplNative(int fd) {
-        mModelPtr = nativeNew(fd);
-        if (mModelPtr == 0L) {
-            throw new IllegalArgumentException("Couldn't initialize TC from file descriptor.");
-        }
-    }
-
-    /**
-     * Creates a new instance of TextClassifierImplNative, using the provided model image, given as
-     * a file path.
-     */
-    TextClassifierImplNative(String path) {
-        mModelPtr = nativeNewFromPath(path);
-        if (mModelPtr == 0L) {
-            throw new IllegalArgumentException("Couldn't initialize TC from given file.");
-        }
-    }
-
-    /**
-     * Creates a new instance of TextClassifierImplNative, using the provided model image, given as
-     * an AssetFileDescriptor.
-     */
-    TextClassifierImplNative(AssetFileDescriptor afd) {
-        mModelPtr = nativeNewFromAssetFileDescriptor(afd, afd.getStartOffset(), afd.getLength());
-        if (mModelPtr == 0L) {
-            throw new IllegalArgumentException(
-                    "Couldn't initialize TC from given AssetFileDescriptor");
-        }
-    }
-
-    /**
-     * Given a string context and current selection, computes the SmartSelection suggestion.
-     *
-     * <p>The begin and end are character indices into the context UTF8 string. selectionBegin is
-     * the character index where the selection begins, and selectionEnd is the index of one
-     * character past the selection span.
-     *
-     * <p>The return value is an array of two ints: suggested selection beginning and end, with the
-     * same semantics as the input selectionBeginning and selectionEnd.
-     */
-    public int[] suggestSelection(
-            String context, int selectionBegin, int selectionEnd, SelectionOptions options) {
-        return nativeSuggestSelection(mModelPtr, context, selectionBegin, selectionEnd, options);
-    }
-
-    /**
-     * Given a string context and current selection, classifies the type of the selected text.
-     *
-     * <p>The begin and end params are character indices in the context string.
-     *
-     * <p>Returns an array of ClassificationResult objects with the probability scores for different
-     * collections.
-     */
-    public ClassificationResult[] classifyText(
-            String context, int selectionBegin, int selectionEnd, ClassificationOptions options) {
-        return nativeClassifyText(mModelPtr, context, selectionBegin, selectionEnd, options);
-    }
-
-    /**
-     * Annotates given input text. The annotations should cover the whole input context except for
-     * whitespaces, and are sorted by their position in the context string.
-     */
-    public AnnotatedSpan[] annotate(String text, AnnotationOptions options) {
-        return nativeAnnotate(mModelPtr, text, options);
-    }
-
-    /** Frees up the allocated memory. */
-    public void close() {
-        nativeClose(mModelPtr);
-    }
-
-    /** Returns a comma separated list of locales supported by the model as BCP 47 tags. */
-    public static String getLocales(int fd) {
-        return nativeGetLocales(fd);
-    }
-
-    /** Returns the version of the model. */
-    public static int getVersion(int fd) {
-        return nativeGetVersion(fd);
-    }
-
-    /** Represents a datetime parsing result from classifyText calls. */
-    public static final class DatetimeResult {
-        static final int GRANULARITY_YEAR = 0;
-        static final int GRANULARITY_MONTH = 1;
-        static final int GRANULARITY_WEEK = 2;
-        static final int GRANULARITY_DAY = 3;
-        static final int GRANULARITY_HOUR = 4;
-        static final int GRANULARITY_MINUTE = 5;
-        static final int GRANULARITY_SECOND = 6;
-
-        private final long mTimeMsUtc;
-        private final int mGranularity;
-
-        DatetimeResult(long timeMsUtc, int granularity) {
-            mGranularity = granularity;
-            mTimeMsUtc = timeMsUtc;
-        }
-
-        public long getTimeMsUtc() {
-            return mTimeMsUtc;
-        }
-
-        public int getGranularity() {
-            return mGranularity;
-        }
-    }
-
-    /** Represents a result of classifyText method call. */
-    public static final class ClassificationResult {
-        private final String mCollection;
-        private final float mScore;
-        private final DatetimeResult mDatetimeResult;
-
-        ClassificationResult(
-                String collection, float score, DatetimeResult datetimeResult) {
-            mCollection = collection;
-            mScore = score;
-            mDatetimeResult = datetimeResult;
-        }
-
-        public String getCollection() {
-            if (mCollection.equals(TextClassifier.TYPE_DATE) && mDatetimeResult != null) {
-                switch (mDatetimeResult.getGranularity()) {
-                    case DatetimeResult.GRANULARITY_HOUR:
-                        // fall through
-                    case DatetimeResult.GRANULARITY_MINUTE:
-                        // fall through
-                    case DatetimeResult.GRANULARITY_SECOND:
-                        return TextClassifier.TYPE_DATE_TIME;
-                    default:
-                        return TextClassifier.TYPE_DATE;
-                }
-            }
-            return mCollection;
-        }
-
-        public float getScore() {
-            return mScore;
-        }
-
-        public DatetimeResult getDatetimeResult() {
-            return mDatetimeResult;
-        }
-    }
-
-    /** Represents a result of Annotate call. */
-    public static final class AnnotatedSpan {
-        private final int mStartIndex;
-        private final int mEndIndex;
-        private final ClassificationResult[] mClassification;
-
-        AnnotatedSpan(
-                int startIndex, int endIndex, ClassificationResult[] classification) {
-            mStartIndex = startIndex;
-            mEndIndex = endIndex;
-            mClassification = classification;
-        }
-
-        public int getStartIndex() {
-            return mStartIndex;
-        }
-
-        public int getEndIndex() {
-            return mEndIndex;
-        }
-
-        public ClassificationResult[] getClassification() {
-            return mClassification;
-        }
-    }
-
-    /** Represents options for the suggestSelection call. */
-    public static final class SelectionOptions {
-        private final String mLocales;
-
-        SelectionOptions(String locales) {
-            mLocales = locales;
-        }
-
-        public String getLocales() {
-            return mLocales;
-        }
-    }
-
-    /** Represents options for the classifyText call. */
-    public static final class ClassificationOptions {
-        private final long mReferenceTimeMsUtc;
-        private final String mReferenceTimezone;
-        private final String mLocales;
-
-        ClassificationOptions(long referenceTimeMsUtc, String referenceTimezone, String locale) {
-            mReferenceTimeMsUtc = referenceTimeMsUtc;
-            mReferenceTimezone = referenceTimezone;
-            mLocales = locale;
-        }
-
-        public long getReferenceTimeMsUtc() {
-            return mReferenceTimeMsUtc;
-        }
-
-        public String getReferenceTimezone() {
-            return mReferenceTimezone;
-        }
-
-        public String getLocale() {
-            return mLocales;
-        }
-    }
-
-    /** Represents options for the Annotate call. */
-    public static final class AnnotationOptions {
-        private final long mReferenceTimeMsUtc;
-        private final String mReferenceTimezone;
-        private final String mLocales;
-
-        AnnotationOptions(long referenceTimeMsUtc, String referenceTimezone, String locale) {
-            mReferenceTimeMsUtc = referenceTimeMsUtc;
-            mReferenceTimezone = referenceTimezone;
-            mLocales = locale;
-        }
-
-        public long getReferenceTimeMsUtc() {
-            return mReferenceTimeMsUtc;
-        }
-
-        public String getReferenceTimezone() {
-            return mReferenceTimezone;
-        }
-
-        public String getLocale() {
-            return mLocales;
-        }
-    }
-
-    private static native long nativeNew(int fd);
-
-    private static native long nativeNewFromPath(String path);
-
-    private static native long nativeNewFromAssetFileDescriptor(
-            AssetFileDescriptor afd, long offset, long size);
-
-    private static native int[] nativeSuggestSelection(
-            long context,
-            String text,
-            int selectionBegin,
-            int selectionEnd,
-            SelectionOptions options);
-
-    private static native ClassificationResult[] nativeClassifyText(
-            long context,
-            String text,
-            int selectionBegin,
-            int selectionEnd,
-            ClassificationOptions options);
-
-    private static native AnnotatedSpan[] nativeAnnotate(
-            long context, String text, AnnotationOptions options);
-
-    private static native void nativeClose(long context);
-
-    private static native String nativeGetLocales(int fd);
-
-    private static native int nativeGetVersion(int fd);
-}
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index ae6a2fd..23d1237 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -25,6 +25,13 @@
  * Cookies are manipulated according to RFC2109.
  */
 public abstract class CookieManager {
+    /**
+     * @deprecated This class should not be constructed by applications, use {@link #getInstance}
+     * instead to fetch the singleton instance.
+     */
+    // TODO(ntfschr): mark this as @SystemApi after a year.
+    @Deprecated
+    public CookieManager() {}
 
     @Override
     protected Object clone() throws CloneNotSupportedException {
diff --git a/core/java/android/webkit/RenderProcessGoneDetail.java b/core/java/android/webkit/RenderProcessGoneDetail.java
index 0843e26..9beeaa5 100644
--- a/core/java/android/webkit/RenderProcessGoneDetail.java
+++ b/core/java/android/webkit/RenderProcessGoneDetail.java
@@ -22,6 +22,13 @@
  **/
 public abstract class RenderProcessGoneDetail {
     /**
+     * @deprecated This class should not be constructed by applications.
+     */
+    // TODO(ntfschr): mark this as @SystemApi after a year.
+    @Deprecated
+    public RenderProcessGoneDetail() {}
+
+    /**
      * Indicates whether the render process was observed to crash, or whether
      * it was killed by the system.
      *
diff --git a/core/java/android/webkit/SafeBrowsingResponse.java b/core/java/android/webkit/SafeBrowsingResponse.java
index 7839a00..ca33a0c 100644
--- a/core/java/android/webkit/SafeBrowsingResponse.java
+++ b/core/java/android/webkit/SafeBrowsingResponse.java
@@ -27,6 +27,12 @@
  * {@link android.webkit.WebView#getSafeBrowsingPrivacyPolicyUrl()}.
  */
 public abstract class SafeBrowsingResponse {
+    /**
+     * @deprecated This class should not be constructed by applications.
+     */
+    // TODO(ntfschr): mark this as @SystemApi after a year.
+    @Deprecated
+    public SafeBrowsingResponse() {}
 
     /**
      * Display the default interstitial.
diff --git a/core/java/android/webkit/ServiceWorkerController.java b/core/java/android/webkit/ServiceWorkerController.java
index 3517c74..d7e0715 100644
--- a/core/java/android/webkit/ServiceWorkerController.java
+++ b/core/java/android/webkit/ServiceWorkerController.java
@@ -38,6 +38,14 @@
 public abstract class ServiceWorkerController {
 
     /**
+     * @deprecated This class should not be constructed by applications, use {@link #getInstance()}
+     * instead to fetch the singleton instance.
+     */
+    // TODO(ntfschr): mark this as @SystemApi after a year.
+    @Deprecated
+    public ServiceWorkerController() {}
+
+    /**
      * Returns the default ServiceWorkerController instance. At present there is
      * only one ServiceWorkerController instance for all WebView instances,
      * however this restriction may be relaxed in the future.
diff --git a/core/java/android/webkit/TracingController.java b/core/java/android/webkit/TracingController.java
index 30f465c..9908182 100644
--- a/core/java/android/webkit/TracingController.java
+++ b/core/java/android/webkit/TracingController.java
@@ -43,6 +43,13 @@
  * </pre></p>
  */
 public abstract class TracingController {
+    /**
+     * @deprecated This class should not be constructed by applications, use {@link #getInstance}
+     * instead to fetch the singleton instance.
+     */
+    // TODO(ntfschr): mark this as @SystemApi after a year.
+    @Deprecated
+    public TracingController() {}
 
     /**
      * Returns the default TracingController instance. At present there is
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index f6166c5..f346c60 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -31,6 +31,14 @@
  */
 public abstract class WebViewDatabase {
     /**
+     * @deprecated This class should not be constructed by applications, use {@link
+     * #getInstance(Context)} instead to fetch the singleton instance.
+     */
+    // TODO(ntfschr): mark this as @SystemApi after a year.
+    @Deprecated
+    public WebViewDatabase() {}
+
+    /**
      * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
      */
     protected static final String LOGTAG = "webviewdatabase";
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index bbfac44..f95b3ce 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -321,6 +321,12 @@
  * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize
  * @attr ref android.R.styleable#TextView_autoSizeStepGranularity
  * @attr ref android.R.styleable#TextView_autoSizePresetSizes
+ * @attr ref android.R.styleable#TextView_textCursorDrawable
+ * @attr ref android.R.styleable#TextView_textSelectHandle
+ * @attr ref android.R.styleable#TextView_textSelectHandleLeft
+ * @attr ref android.R.styleable#TextView_textSelectHandleRight
+ * @attr ref android.R.styleable#TextView_allowUndo
+ * @attr ref android.R.styleable#TextView_enabled
  */
 @RemoteView
 public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
diff --git a/core/java/com/android/internal/app/procstats/IProcessStats.aidl b/core/java/com/android/internal/app/procstats/IProcessStats.aidl
index 44867c7..0c203ab 100644
--- a/core/java/com/android/internal/app/procstats/IProcessStats.aidl
+++ b/core/java/com/android/internal/app/procstats/IProcessStats.aidl
@@ -24,4 +24,14 @@
     byte[] getCurrentStats(out List<ParcelFileDescriptor> historic);
     ParcelFileDescriptor getStatsOverTime(long minTime);
     int getCurrentMemoryState();
+
+    /**
+     * Get stats committed after highWaterMarkMs
+     * @param highWaterMarkMs Report stats committed after this time.
+     * @param section Integer mask to indicate which sections to include in the stats.
+     * @param doAggregate Whether to aggregate the stats or keep them separated.
+     * @param List of Files of individual commits in protobuf binary or one that is merged from them.
+     */
+     long getCommittedStats(long highWaterMarkMs, int section, boolean doAggregate,
+        out List<ParcelFileDescriptor> committedStats);
 }
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index f6e99ba..e7ac566 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -158,6 +158,25 @@
             STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY
     };
 
+    // Should report process stats.
+    public static final int REPORT_PROC_STATS = 0x01;
+    // Should report package process stats.
+    public static final int REPORT_PKG_PROC_STATS = 0x02;
+    // Should report package service stats.
+    public static final int REPORT_PKG_SVC_STATS = 0x04;
+    // Should report package association stats.
+    public static final int REPORT_PKG_ASC_STATS = 0x08;
+    // Should report package stats.
+    public static final int REPORT_PKG_STATS = 0x0E;
+    // Should report all stats.
+    public static final int REPORT_ALL = 0x0F;
+
+    public static final int[] OPTIONS =
+            {REPORT_PROC_STATS, REPORT_PKG_PROC_STATS, REPORT_PKG_SVC_STATS, REPORT_PKG_ASC_STATS,
+                    REPORT_PKG_STATS, REPORT_ALL};
+    public static final String[] OPTIONS_STR =
+            {"proc", "pkg-proc", "pkg-svc", "pkg-asc", "pkg-all", "all"};
+
     // Current version of the parcel format.
     private static final int PARCEL_VERSION = 34;
     // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
@@ -1412,7 +1431,7 @@
     }
 
     public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary,
-            boolean dumpDetails, boolean dumpAll, boolean activeOnly) {
+            boolean dumpDetails, boolean dumpAll, boolean activeOnly, int section) {
         long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
                 mStartTime, now);
         boolean sepNeeded = false;
@@ -1421,176 +1440,205 @@
             mSysMemUsage.dump(pw, "  ", ALL_SCREEN_ADJ, ALL_MEM_ADJ);
             sepNeeded = true;
         }
-        ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap = mPackages.getMap();
         boolean printedHeader = false;
-        for (int ip=0; ip<pkgMap.size(); ip++) {
-            final String pkgName = pkgMap.keyAt(ip);
-            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
-            for (int iu=0; iu<uids.size(); iu++) {
-                final int uid = uids.keyAt(iu);
-                final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
-                for (int iv=0; iv<vpkgs.size(); iv++) {
-                    final long vers = vpkgs.keyAt(iv);
-                    final PackageState pkgState = vpkgs.valueAt(iv);
-                    final int NPROCS = pkgState.mProcesses.size();
-                    final int NSRVS = pkgState.mServices.size();
-                    final int NASCS = pkgState.mAssociations.size();
-                    final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
-                    if (!pkgMatch) {
-                        boolean procMatch = false;
-                        for (int iproc=0; iproc<NPROCS; iproc++) {
-                            ProcessState proc = pkgState.mProcesses.valueAt(iproc);
-                            if (reqPackage.equals(proc.getName())) {
-                                procMatch = true;
-                                break;
+        if ((section & REPORT_PKG_STATS) != 0) {
+            ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+                    mPackages.getMap();
+            for (int ip = 0; ip < pkgMap.size(); ip++) {
+                final String pkgName = pkgMap.keyAt(ip);
+                final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+                for (int iu = 0; iu < uids.size(); iu++) {
+                    final int uid = uids.keyAt(iu);
+                    final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
+                    for (int iv = 0; iv < vpkgs.size(); iv++) {
+                        final long vers = vpkgs.keyAt(iv);
+                        final PackageState pkgState = vpkgs.valueAt(iv);
+                        final int NPROCS = pkgState.mProcesses.size();
+                        final int NSRVS = pkgState.mServices.size();
+                        final int NASCS = pkgState.mAssociations.size();
+                        final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
+                        if (!pkgMatch) {
+                            boolean procMatch = false;
+                            for (int iproc = 0; iproc < NPROCS; iproc++) {
+                                ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+                                if (reqPackage.equals(proc.getName())) {
+                                    procMatch = true;
+                                    break;
+                                }
                             }
-                        }
-                        if (!procMatch) {
-                            continue;
-                        }
-                    }
-                    if (NPROCS > 0 || NSRVS > 0 || NASCS > 0) {
-                        if (!printedHeader) {
-                            if (sepNeeded) pw.println();
-                            pw.println("Per-Package Stats:");
-                            printedHeader = true;
-                            sepNeeded = true;
-                        }
-                        pw.print("  * "); pw.print(pkgName); pw.print(" / ");
-                                UserHandle.formatUid(pw, uid); pw.print(" / v");
-                                pw.print(vers); pw.println(":");
-                    }
-                    if (!dumpSummary || dumpAll) {
-                        for (int iproc=0; iproc<NPROCS; iproc++) {
-                            ProcessState proc = pkgState.mProcesses.valueAt(iproc);
-                            if (!pkgMatch && !reqPackage.equals(proc.getName())) {
+                            if (!procMatch) {
                                 continue;
                             }
-                            if (activeOnly && !proc.isInUse()) {
-                                pw.print("      (Not active: ");
-                                        pw.print(pkgState.mProcesses.keyAt(iproc)); pw.println(")");
-                                continue;
+                        }
+                        if (NPROCS > 0 || NSRVS > 0 || NASCS > 0) {
+                            if (!printedHeader) {
+                                if (sepNeeded) pw.println();
+                                pw.println("Per-Package Stats:");
+                                printedHeader = true;
+                                sepNeeded = true;
                             }
-                            pw.print("      Process ");
-                            pw.print(pkgState.mProcesses.keyAt(iproc));
-                            if (proc.getCommonProcess().isMultiPackage()) {
-                                pw.print(" (multi, ");
-                            } else {
-                                pw.print(" (unique, ");
-                            }
-                            pw.print(proc.getDurationsBucketCount());
-                            pw.print(" entries)");
+                            pw.print("  * ");
+                            pw.print(pkgName);
+                            pw.print(" / ");
+                            UserHandle.formatUid(pw, uid);
+                            pw.print(" / v");
+                            pw.print(vers);
                             pw.println(":");
-                            proc.dumpProcessState(pw, "        ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
-                                    ALL_PROC_STATES, now);
-                            proc.dumpPss(pw, "        ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
-                                    ALL_PROC_STATES, now);
-                            proc.dumpInternalLocked(pw, "        ", dumpAll);
                         }
-                    } else {
-                        ArrayList<ProcessState> procs = new ArrayList<ProcessState>();
-                        for (int iproc=0; iproc<NPROCS; iproc++) {
-                            ProcessState proc = pkgState.mProcesses.valueAt(iproc);
-                            if (!pkgMatch && !reqPackage.equals(proc.getName())) {
-                                continue;
+                        if ((section & REPORT_PKG_PROC_STATS) != 0) {
+                            if (!dumpSummary || dumpAll) {
+                                for (int iproc = 0; iproc < NPROCS; iproc++) {
+                                    ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+                                    if (!pkgMatch && !reqPackage.equals(proc.getName())) {
+                                        continue;
+                                    }
+                                    if (activeOnly && !proc.isInUse()) {
+                                        pw.print("      (Not active: ");
+                                        pw.print(pkgState.mProcesses.keyAt(iproc));
+                                        pw.println(")");
+                                        continue;
+                                    }
+                                    pw.print("      Process ");
+                                    pw.print(pkgState.mProcesses.keyAt(iproc));
+                                    if (proc.getCommonProcess().isMultiPackage()) {
+                                        pw.print(" (multi, ");
+                                    } else {
+                                        pw.print(" (unique, ");
+                                    }
+                                    pw.print(proc.getDurationsBucketCount());
+                                    pw.print(" entries)");
+                                    pw.println(":");
+                                    proc.dumpProcessState(pw, "        ", ALL_SCREEN_ADJ,
+                                            ALL_MEM_ADJ,
+                                            ALL_PROC_STATES, now);
+                                    proc.dumpPss(pw, "        ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+                                            ALL_PROC_STATES, now);
+                                    proc.dumpInternalLocked(pw, "        ", dumpAll);
+                                }
+                            } else {
+                                ArrayList<ProcessState> procs = new ArrayList<ProcessState>();
+                                for (int iproc = 0; iproc < NPROCS; iproc++) {
+                                    ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+                                    if (!pkgMatch && !reqPackage.equals(proc.getName())) {
+                                        continue;
+                                    }
+                                    if (activeOnly && !proc.isInUse()) {
+                                        continue;
+                                    }
+                                    procs.add(proc);
+                                }
+                                DumpUtils.dumpProcessSummaryLocked(pw, "      ", "Prc ", procs,
+                                        ALL_SCREEN_ADJ, ALL_MEM_ADJ, NON_CACHED_PROC_STATES,
+                                        now, totalTime);
                             }
-                            if (activeOnly && !proc.isInUse()) {
-                                continue;
+                        }
+                        if ((section & REPORT_PKG_SVC_STATS) != 0) {
+                            for (int isvc = 0; isvc < NSRVS; isvc++) {
+                                ServiceState svc = pkgState.mServices.valueAt(isvc);
+                                if (!pkgMatch && !reqPackage.equals(svc.getProcessName())) {
+                                    continue;
+                                }
+                                if (activeOnly && !svc.isInUse()) {
+                                    pw.print("      (Not active service: ");
+                                    pw.print(pkgState.mServices.keyAt(isvc));
+                                    pw.println(")");
+                                    continue;
+                                }
+                                if (dumpAll) {
+                                    pw.print("      Service ");
+                                } else {
+                                    pw.print("      * Svc ");
+                                }
+                                pw.print(pkgState.mServices.keyAt(isvc));
+                                pw.println(":");
+                                pw.print("        Process: ");
+                                pw.println(svc.getProcessName());
+                                svc.dumpStats(pw, "        ", "          ", "    ",
+                                        now, totalTime, dumpSummary, dumpAll);
                             }
-                            procs.add(proc);
                         }
-                        DumpUtils.dumpProcessSummaryLocked(pw, "      ", "Prc ", procs,
-                                ALL_SCREEN_ADJ, ALL_MEM_ADJ, NON_CACHED_PROC_STATES,
-                                now, totalTime);
-                    }
-                    for (int isvc=0; isvc<NSRVS; isvc++) {
-                        ServiceState svc = pkgState.mServices.valueAt(isvc);
-                        if (!pkgMatch && !reqPackage.equals(svc.getProcessName())) {
-                            continue;
+                        if ((section & REPORT_PKG_ASC_STATS) != 0) {
+                            for (int iasc = 0; iasc < NASCS; iasc++) {
+                                AssociationState asc = pkgState.mAssociations.valueAt(iasc);
+                                if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
+                                    continue;
+                                }
+                                if (activeOnly && !asc.isInUse()) {
+                                    pw.print("      (Not active association: ");
+                                    pw.print(pkgState.mAssociations.keyAt(iasc));
+                                    pw.println(")");
+                                    continue;
+                                }
+                                if (dumpAll) {
+                                    pw.print("      Association ");
+                                } else {
+                                    pw.print("      * Asc ");
+                                }
+                                pw.print(pkgState.mAssociations.keyAt(iasc));
+                                pw.println(":");
+                                pw.print("        Process: ");
+                                pw.println(asc.getProcessName());
+                                asc.dumpStats(pw, "        ", "          ", "    ",
+                                        now, totalTime, dumpDetails, dumpAll);
+                            }
                         }
-                        if (activeOnly && !svc.isInUse()) {
-                            pw.print("      (Not active service: ");
-                                    pw.print(pkgState.mServices.keyAt(isvc)); pw.println(")");
-                            continue;
-                        }
-                        if (dumpAll) {
-                            pw.print("      Service ");
-                        } else {
-                            pw.print("      * Svc ");
-                        }
-                        pw.print(pkgState.mServices.keyAt(isvc));
-                        pw.println(":");
-                        pw.print("        Process: "); pw.println(svc.getProcessName());
-                        svc.dumpStats(pw, "        ", "          ", "    ",
-                                now, totalTime, dumpSummary, dumpAll);
-                    }
-                    for (int iasc=0; iasc<NASCS; iasc++) {
-                        AssociationState asc = pkgState.mAssociations.valueAt(iasc);
-                        if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
-                            continue;
-                        }
-                        if (activeOnly && !asc.isInUse()) {
-                            pw.print("      (Not active association: ");
-                            pw.print(pkgState.mAssociations.keyAt(iasc)); pw.println(")");
-                            continue;
-                        }
-                        if (dumpAll) {
-                            pw.print("      Association ");
-                        } else {
-                            pw.print("      * Asc ");
-                        }
-                        pw.print(pkgState.mAssociations.keyAt(iasc));
-                        pw.println(":");
-                        pw.print("        Process: "); pw.println(asc.getProcessName());
-                        asc.dumpStats(pw, "        ", "          ", "    ",
-                                now, totalTime, dumpDetails, dumpAll);
                     }
                 }
             }
         }
 
-        ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
-        printedHeader = false;
-        int numShownProcs = 0, numTotalProcs = 0;
-        for (int ip=0; ip<procMap.size(); ip++) {
-            String procName = procMap.keyAt(ip);
-            SparseArray<ProcessState> uids = procMap.valueAt(ip);
-            for (int iu=0; iu<uids.size(); iu++) {
-                int uid = uids.keyAt(iu);
-                numTotalProcs++;
-                final ProcessState proc = uids.valueAt(iu);
-                if (!proc.hasAnyData()) {
-                    continue;
+        if ((section & REPORT_PROC_STATS) != 0) {
+            ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+            printedHeader = false;
+            int numShownProcs = 0, numTotalProcs = 0;
+            for (int ip = 0; ip < procMap.size(); ip++) {
+                String procName = procMap.keyAt(ip);
+                SparseArray<ProcessState> uids = procMap.valueAt(ip);
+                for (int iu = 0; iu < uids.size(); iu++) {
+                    int uid = uids.keyAt(iu);
+                    numTotalProcs++;
+                    final ProcessState proc = uids.valueAt(iu);
+                    if (!proc.hasAnyData()) {
+                        continue;
+                    }
+                    if (!proc.isMultiPackage()) {
+                        continue;
+                    }
+                    if (reqPackage != null && !reqPackage.equals(procName)
+                            && !reqPackage.equals(proc.getPackage())) {
+                        continue;
+                    }
+                    numShownProcs++;
+                    if (sepNeeded) {
+                        pw.println();
+                    }
+                    sepNeeded = true;
+                    if (!printedHeader) {
+                        pw.println("Multi-Package Common Processes:");
+                        printedHeader = true;
+                    }
+                    if (activeOnly && !proc.isInUse()) {
+                        pw.print("      (Not active: ");
+                        pw.print(procName);
+                        pw.println(")");
+                        continue;
+                    }
+                    pw.print("  * ");
+                    pw.print(procName);
+                    pw.print(" / ");
+                    UserHandle.formatUid(pw, uid);
+                    pw.print(" (");
+                    pw.print(proc.getDurationsBucketCount());
+                    pw.print(" entries)");
+                    pw.println(":");
+                    proc.dumpProcessState(pw, "        ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+                            ALL_PROC_STATES, now);
+                    proc.dumpPss(pw, "        ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES, now);
+                    proc.dumpInternalLocked(pw, "        ", dumpAll);
                 }
-                if (!proc.isMultiPackage()) {
-                    continue;
-                }
-                if (reqPackage != null && !reqPackage.equals(procName)
-                        && !reqPackage.equals(proc.getPackage())) {
-                    continue;
-                }
-                numShownProcs++;
-                if (sepNeeded) {
-                    pw.println();
-                }
-                sepNeeded = true;
-                if (!printedHeader) {
-                    pw.println("Multi-Package Common Processes:");
-                    printedHeader = true;
-                }
-                if (activeOnly && !proc.isInUse()) {
-                    pw.print("      (Not active: "); pw.print(procName); pw.println(")");
-                    continue;
-                }
-                pw.print("  * "); pw.print(procName); pw.print(" / ");
-                        UserHandle.formatUid(pw, uid);
-                        pw.print(" ("); pw.print(proc.getDurationsBucketCount());
-                        pw.print(" entries)"); pw.println(":");
-                proc.dumpProcessState(pw, "        ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
-                        ALL_PROC_STATES, now);
-                proc.dumpPss(pw, "        ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES, now);
-                proc.dumpInternalLocked(pw, "        ", dumpAll);
             }
+            pw.print("  Total procs: "); pw.print(numShownProcs);
+            pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total");
         }
 
         if (dumpAll) {
@@ -1598,8 +1646,7 @@
                 pw.println();
             }
             sepNeeded = true;
-            pw.print("  Total procs: "); pw.print(numShownProcs);
-                    pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total");
+
             if (mTrackingAssociations.size() > 0) {
                 pw.println();
                 pw.println("Tracking associations:");
@@ -1866,7 +1913,10 @@
         return outProcs;
     }
 
-    public void dumpCheckinLocked(PrintWriter pw, String reqPackage) {
+    /**
+     * Prints checkin style stats dump.
+     */
+    public void dumpCheckinLocked(PrintWriter pw, String reqPackage, int section) {
         final long now = SystemClock.uptimeMillis();
         final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
                 mPackages.getMap();
@@ -1895,50 +1945,61 @@
         }
         pw.println();
         pw.print("config,"); pw.println(mRuntime);
-        for (int ip=0; ip<pkgMap.size(); ip++) {
-            final String pkgName = pkgMap.keyAt(ip);
-            if (reqPackage != null && !reqPackage.equals(pkgName)) {
-                continue;
-            }
-            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
-            for (int iu=0; iu<uids.size(); iu++) {
-                final int uid = uids.keyAt(iu);
-                final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
-                for (int iv=0; iv<vpkgs.size(); iv++) {
-                    final long vers = vpkgs.keyAt(iv);
-                    final PackageState pkgState = vpkgs.valueAt(iv);
-                    final int NPROCS = pkgState.mProcesses.size();
-                    final int NSRVS = pkgState.mServices.size();
-                    final int NASCS = pkgState.mAssociations.size();
-                    for (int iproc=0; iproc<NPROCS; iproc++) {
-                        ProcessState proc = pkgState.mProcesses.valueAt(iproc);
-                        proc.dumpPackageProcCheckin(pw, pkgName, uid, vers,
-                                pkgState.mProcesses.keyAt(iproc), now);
-                    }
-                    for (int isvc=0; isvc<NSRVS; isvc++) {
-                        final String serviceName = DumpUtils.collapseString(pkgName,
-                                pkgState.mServices.keyAt(isvc));
-                        final ServiceState svc = pkgState.mServices.valueAt(isvc);
-                        svc.dumpTimesCheckin(pw, pkgName, uid, vers, serviceName, now);
-                    }
-                    for (int iasc=0; iasc<NASCS; iasc++) {
-                        final String associationName = DumpUtils.collapseString(pkgName,
-                                pkgState.mAssociations.keyAt(iasc));
-                        final AssociationState asc = pkgState.mAssociations.valueAt(iasc);
-                        asc.dumpTimesCheckin(pw, pkgName, uid, vers, associationName, now);
+
+        if ((section & REPORT_PKG_STATS) != 0) {
+            for (int ip = 0; ip < pkgMap.size(); ip++) {
+                final String pkgName = pkgMap.keyAt(ip);
+                if (reqPackage != null && !reqPackage.equals(pkgName)) {
+                    continue;
+                }
+                final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+                for (int iu = 0; iu < uids.size(); iu++) {
+                    final int uid = uids.keyAt(iu);
+                    final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
+                    for (int iv = 0; iv < vpkgs.size(); iv++) {
+                        final long vers = vpkgs.keyAt(iv);
+                        final PackageState pkgState = vpkgs.valueAt(iv);
+                        final int NPROCS = pkgState.mProcesses.size();
+                        final int NSRVS = pkgState.mServices.size();
+                        final int NASCS = pkgState.mAssociations.size();
+                        if ((section & REPORT_PKG_PROC_STATS) != 0) {
+                            for (int iproc = 0; iproc < NPROCS; iproc++) {
+                                ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+                                proc.dumpPackageProcCheckin(pw, pkgName, uid, vers,
+                                        pkgState.mProcesses.keyAt(iproc), now);
+                            }
+                        }
+                        if ((section & REPORT_PKG_SVC_STATS) != 0) {
+                            for (int isvc = 0; isvc < NSRVS; isvc++) {
+                                final String serviceName = DumpUtils.collapseString(pkgName,
+                                        pkgState.mServices.keyAt(isvc));
+                                final ServiceState svc = pkgState.mServices.valueAt(isvc);
+                                svc.dumpTimesCheckin(pw, pkgName, uid, vers, serviceName, now);
+                            }
+                        }
+                        if ((section & REPORT_PKG_ASC_STATS) != 0) {
+                            for (int iasc = 0; iasc < NASCS; iasc++) {
+                                final String associationName = DumpUtils.collapseString(pkgName,
+                                        pkgState.mAssociations.keyAt(iasc));
+                                final AssociationState asc = pkgState.mAssociations.valueAt(iasc);
+                                asc.dumpTimesCheckin(pw, pkgName, uid, vers, associationName, now);
+                            }
+                        }
                     }
                 }
             }
         }
 
-        ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
-        for (int ip=0; ip<procMap.size(); ip++) {
-            String procName = procMap.keyAt(ip);
-            SparseArray<ProcessState> uids = procMap.valueAt(ip);
-            for (int iu=0; iu<uids.size(); iu++) {
-                final int uid = uids.keyAt(iu);
-                final ProcessState procState = uids.valueAt(iu);
-                procState.dumpProcCheckin(pw, procName, uid, now);
+        if ((section & REPORT_PROC_STATS) != 0) {
+            ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+            for (int ip = 0; ip < procMap.size(); ip++) {
+                String procName = procMap.keyAt(ip);
+                SparseArray<ProcessState> uids = procMap.valueAt(ip);
+                for (int iu = 0; iu < uids.size(); iu++) {
+                    final int uid = uids.keyAt(iu);
+                    final ProcessState procState = uids.valueAt(iu);
+                    procState.dumpProcCheckin(pw, procName, uid, now);
+                }
             }
         }
         pw.print("total");
@@ -2013,9 +2074,10 @@
         }
     }
 
-    public void writeToProto(ProtoOutputStream proto, long fieldId, long now) {
-        final long token = proto.start(fieldId);
-
+    /**
+     * Writes to ProtoOutputStream.
+     */
+    public void writeToProto(ProtoOutputStream proto, long now, int section) {
         proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime);
         proto.write(ProcessStatsSectionProto.END_REALTIME_MS,
                 mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
@@ -2024,15 +2086,15 @@
         proto.write(ProcessStatsSectionProto.RUNTIME, mRuntime);
         proto.write(ProcessStatsSectionProto.HAS_SWAPPED_PSS, mHasSwappedOutPss);
         boolean partial = true;
-        if ((mFlags&FLAG_SHUTDOWN) != 0) {
+        if ((mFlags & FLAG_SHUTDOWN) != 0) {
             proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SHUTDOWN);
             partial = false;
         }
-        if ((mFlags&FLAG_SYSPROPS) != 0) {
+        if ((mFlags & FLAG_SYSPROPS) != 0) {
             proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SYSPROPS);
             partial = false;
         }
-        if ((mFlags&FLAG_COMPLETE) != 0) {
+        if ((mFlags & FLAG_COMPLETE) != 0) {
             proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_COMPLETE);
             partial = false;
         }
@@ -2041,31 +2103,34 @@
         }
 
         final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
-        for (int ip=0; ip<procMap.size(); ip++) {
-            final String procName = procMap.keyAt(ip);
-            final SparseArray<ProcessState> uids = procMap.valueAt(ip);
-            for (int iu=0; iu<uids.size(); iu++) {
-                final int uid = uids.keyAt(iu);
-                final ProcessState procState = uids.valueAt(iu);
-                procState.writeToProto(proto, ProcessStatsSectionProto.PROCESS_STATS, procName,
-                        uid, now);
-            }
-        }
-
-        final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
-                mPackages.getMap();
-        for (int ip = 0; ip < pkgMap.size(); ip++) {
-            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
-            for (int iu = 0; iu < uids.size(); iu++) {
-                final LongSparseArray<PackageState> vers = uids.valueAt(iu);
-                for (int iv = 0; iv < vers.size(); iv++) {
-                    final PackageState pkgState = vers.valueAt(iv);
-                    pkgState.writeToProto(proto, ProcessStatsSectionProto.PACKAGE_STATS, now);
+        if ((section & REPORT_PROC_STATS) != 0) {
+            for (int ip = 0; ip < procMap.size(); ip++) {
+                final String procName = procMap.keyAt(ip);
+                final SparseArray<ProcessState> uids = procMap.valueAt(ip);
+                for (int iu = 0; iu < uids.size(); iu++) {
+                    final int uid = uids.keyAt(iu);
+                    final ProcessState procState = uids.valueAt(iu);
+                    procState.writeToProto(proto, ProcessStatsSectionProto.PROCESS_STATS, procName,
+                            uid, now);
                 }
             }
         }
 
-        proto.end(token);
+        if ((section & REPORT_PKG_STATS) != 0) {
+            final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+                    mPackages.getMap();
+            for (int ip = 0; ip < pkgMap.size(); ip++) {
+                final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+                for (int iu = 0; iu < uids.size(); iu++) {
+                    final LongSparseArray<PackageState> vers = uids.valueAt(iu);
+                    for (int iv = 0; iv < vers.size(); iv++) {
+                        final PackageState pkgState = vers.valueAt(iv);
+                        pkgState.writeToProto(proto, ProcessStatsSectionProto.PACKAGE_STATS, now,
+                                section);
+                    }
+                }
+            }
+        }
     }
 
     final public static class ProcessStateHolder {
@@ -2110,30 +2175,39 @@
             return as;
         }
 
-        public void writeToProto(ProtoOutputStream proto, long fieldId, long now) {
+        /**
+         * Writes the containing stats into proto, with options to choose smaller sections.
+         */
+        public void writeToProto(ProtoOutputStream proto, long fieldId, long now, int section) {
             final long token = proto.start(fieldId);
 
             proto.write(ProcessStatsPackageProto.PACKAGE, mPackageName);
             proto.write(ProcessStatsPackageProto.UID, mUid);
             proto.write(ProcessStatsPackageProto.VERSION, mVersionCode);
 
-            for (int ip = 0; ip < mProcesses.size(); ip++) {
-                final String procName = mProcesses.keyAt(ip);
-                final ProcessState procState = mProcesses.valueAt(ip);
-                procState.writeToProto(proto, ProcessStatsPackageProto.PROCESS_STATS, procName,
-                        mUid, now);
+            if ((section & ProcessStats.REPORT_PKG_PROC_STATS) != 0) {
+                for (int ip = 0; ip < mProcesses.size(); ip++) {
+                    final String procName = mProcesses.keyAt(ip);
+                    final ProcessState procState = mProcesses.valueAt(ip);
+                    procState.writeToProto(proto, ProcessStatsPackageProto.PROCESS_STATS, procName,
+                            mUid, now);
+                }
             }
 
-            for (int is = 0; is < mServices.size(); is++) {
-                final ServiceState serviceState = mServices.valueAt(is);
-                serviceState.writeToProto(proto, ProcessStatsPackageProto.SERVICE_STATS,
-                        now);
+            if ((section & ProcessStats.REPORT_PKG_SVC_STATS) != 0) {
+                for (int is = 0; is < mServices.size(); is++) {
+                    final ServiceState serviceState = mServices.valueAt(is);
+                    serviceState.writeToProto(proto, ProcessStatsPackageProto.SERVICE_STATS,
+                            now);
+                }
             }
 
-            for (int ia=0; ia<mAssociations.size(); ia++) {
-                final AssociationState ascState = mAssociations.valueAt(ia);
-                ascState.writeToProto(proto, ProcessStatsPackageProto.ASSOCIATION_STATS,
-                        now);
+            if ((section & ProcessStats.REPORT_PKG_ASC_STATS) != 0) {
+                for (int ia = 0; ia < mAssociations.size(); ia++) {
+                    final AssociationState ascState = mAssociations.valueAt(ia);
+                    ascState.writeToProto(proto, ProcessStatsPackageProto.ASSOCIATION_STATS,
+                            now);
+                }
             }
 
             proto.end(token);
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 9d5f0bc..ab50ad1 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -130,6 +130,13 @@
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
     optional bool keyguard_showing = 1;
+    repeated KeyguardOccludedProto keyguard_occluded_states= 2;
+}
+
+message KeyguardOccludedProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional int32 display_id = 1;
     optional bool keyguard_occluded = 2;
 }
 
diff --git a/core/res/res/values-television/themes.xml b/core/res/res/values-television/themes.xml
index 0712cbc..48b59c7 100644
--- a/core/res/res/values-television/themes.xml
+++ b/core/res/res/values-television/themes.xml
@@ -15,7 +15,6 @@
 -->
 <resources>
     <style name="Theme.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
-    <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.Leanback.Dialog.AppError" />
     <style name="Theme.Holo.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
     <style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
     <style name="Theme.Material.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
diff --git a/core/res/res/values-television/themes_device_defaults.xml b/core/res/res/values-television/themes_device_defaults.xml
index b92c44e..fdd0624 100644
--- a/core/res/res/values-television/themes_device_defaults.xml
+++ b/core/res/res/values-television/themes_device_defaults.xml
@@ -15,6 +15,7 @@
 -->
 <resources>
     <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
+    <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.Leanback.Dialog.AppError" />
     <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
 
     <!-- TODO(b/116457731): remove colorBackground from colors_material.xml if not used anymore -->
diff --git a/core/res/res/values-watch/themes.xml b/core/res/res/values-watch/themes.xml
deleted file mode 100644
index 1be47ba..0000000
--- a/core/res/res/values-watch/themes.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <!-- Theme for the dialog shown when an app crashes or ANRs. Override to make it dark. -->
-    <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert">
-        <item name="windowContentTransitions">false</item>
-        <item name="windowActivityTransitions">false</item>
-        <item name="windowCloseOnTouchOutside">false</item>
-    </style>
-</resources>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index a7736e7c..bfba312 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -422,6 +422,13 @@
         <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
+    <!-- Theme for the dialog shown when an app crashes or ANRs. Override to make it dark. -->
+    <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert">
+        <item name="windowContentTransitions">false</item>
+        <item name="windowActivityTransitions">false</item>
+        <item name="windowCloseOnTouchOutside">false</item>
+    </style>
+
     <style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar">
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1404383..9aebf6c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3541,4 +3541,13 @@
 
     <!-- Brand value for attestation of misprovisioned device. -->
     <string name="config_misprovisionedBrandValue" translatable="false"></string>
+
+    <!-- Pre-scale volume at volume step 1 for Absolute Volume -->
+    <fraction name="config_prescaleAbsoluteVolume_index1">50%</fraction>
+
+    <!-- Pre-scale volume at volume step 2 for Absolute Volume -->
+    <fraction name="config_prescaleAbsoluteVolume_index2">70%</fraction>
+
+    <!-- Pre-scale volume at volume step 3 for Absolute Volume -->
+    <fraction name="config_prescaleAbsoluteVolume_index3">85%</fraction>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 21f0dad..9f2256a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3470,4 +3470,9 @@
   <java-symbol type="integer" name="db_wal_truncate_size" />
   <java-symbol type="integer" name="config_wakeUpDelayDoze" />
   <java-symbol type="string" name="config_dozeWakeScreenSensorType" />
+
+  <!-- For Bluetooth AbsoluteVolume -->
+  <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index1" />
+  <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index2" />
+  <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index3" />
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index a710410..58feef5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -102,7 +102,7 @@
                 BluetoothProfile.A2DP);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index e13e566..988062d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -96,7 +96,7 @@
                 BluetoothProfile.A2DP_SINK);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index ecaeaf2..750a843 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -34,7 +34,6 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 
 import androidx.annotation.VisibleForTesting;
@@ -59,7 +58,6 @@
     private long mHiSyncId;
     // Need this since there is no method for getting RSSI
     private short mRssi;
-    private HashMap<LocalBluetoothProfile, Integer> mProfileConnectionState;
 
     private final List<LocalBluetoothProfile> mProfiles =
             new ArrayList<LocalBluetoothProfile>();
@@ -101,7 +99,6 @@
         mLocalAdapter = BluetoothAdapter.getDefaultAdapter();
         mProfileManager = profileManager;
         mDevice = device;
-        mProfileConnectionState = new HashMap<>();
         fillData();
         mHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID;
     }
@@ -134,7 +131,6 @@
             }
             return;
         }
-        mProfileConnectionState.put(profile, newProfileState);
         if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
             if (profile instanceof MapProfile) {
                 profile.setPreferred(mDevice, true);
@@ -226,7 +222,7 @@
 
         int preferredProfiles = 0;
         for (LocalBluetoothProfile profile : mProfiles) {
-            if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
+            if (connectAllProfiles ? profile.accessProfileEnabled() : profile.isAutoConnectable()) {
                 if (profile.isPreferred(mDevice)) {
                     ++preferredProfiles;
                     connectInt(profile);
@@ -324,22 +320,9 @@
     }
 
     public int getProfileConnectionState(LocalBluetoothProfile profile) {
-        if (mProfileConnectionState.get(profile) == null) {
-            // If cache is empty make the binder call to get the state
-            int state = profile.getConnectionStatus(mDevice);
-            mProfileConnectionState.put(profile, state);
-        }
-        return mProfileConnectionState.get(profile);
-    }
-
-    public void clearProfileConnectionState ()
-    {
-        if (BluetoothUtils.D) {
-            Log.d(TAG," Clearing all connection state for dev:" + mDevice.getName());
-        }
-        for (LocalBluetoothProfile profile :getProfiles()) {
-            mProfileConnectionState.put(profile, BluetoothProfile.STATE_DISCONNECTED);
-        }
+        return profile != null
+                ? profile.getConnectionStatus(mDevice)
+                : BluetoothProfile.STATE_DISCONNECTED;
     }
 
     // TODO: do any of these need to run async on a background thread?
@@ -661,7 +644,7 @@
         List<LocalBluetoothProfile> connectableProfiles =
                 new ArrayList<LocalBluetoothProfile>();
         for (LocalBluetoothProfile profile : mProfiles) {
-            if (profile.isConnectable()) {
+            if (profile.accessProfileEnabled()) {
                 connectableProfiles.add(profile);
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 5a64e02..21cf0c2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -285,11 +285,6 @@
                     {
                         mCachedDevicesMapForHearingAids.remove(cachedDevice.getHiSyncId());
                     }
-                } else {
-                    // For bonded devices, we need to clear the connection status so that
-                    // when BT is enabled next time, device connection status shall be retrieved
-                    // by making a binder call.
-                    cachedDevice.clearProfileConnectionState();
                 }
             }
             for (int i = mHearingAidDevicesNotAddedInCache.size() - 1; i >= 0; i--) {
@@ -297,11 +292,6 @@
                 if (notCachedDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
                     notCachedDevice.setJustDiscovered(false);
                     mHearingAidDevicesNotAddedInCache.remove(i);
-                } else {
-                    // For bonded devices, we need to clear the connection status so that
-                    // when BT is enabled next time, device connection status shall be retrieved
-                    // by making a binder call.
-                    notCachedDevice.clearProfileConnectionState();
                 }
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 2dd8eaf..62507f5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -103,7 +103,7 @@
                 BluetoothProfile.HEADSET);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index 1eeb4f0..8bc0acf5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -100,7 +100,7 @@
                 new HearingAidServiceListener(), BluetoothProfile.HEARING_AID);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return false;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 4b6a22c..4879144 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -104,7 +104,7 @@
     }
 
     @Override
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
index c8d4fc8..61e5b6b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
@@ -94,7 +94,7 @@
     }
 
     @Override
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index fe6b222..75d16db 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -93,7 +93,7 @@
                 BluetoothProfile.HID_HOST);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
index 0447f37..4b0ca74 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
@@ -26,9 +26,9 @@
 public interface LocalBluetoothProfile {
 
     /**
-     * Returns true if the user can initiate a connection, false otherwise.
+     * Return {@code true} if the user can initiate a connection for this profile in UI.
      */
-    boolean isConnectable();
+    boolean accessProfileEnabled();
 
     /**
      * Returns true if the user can enable auto connection for this profile.
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 7000f9d..0c29f43 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -290,10 +290,11 @@
                 }
             }
 
-            mEventManager.dispatchProfileConnectionStateChanged(cachedDevice, newState,
-                    mProfile.getProfileId());
             cachedDevice.onProfileStateChanged(mProfile, newState);
             cachedDevice.refresh();
+            // Dispatch profile changed after device update
+            mEventManager.dispatchProfileConnectionStateChanged(cachedDevice, newState,
+                    mProfile.getProfileId());
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
index 7ad2e28c..1e22f44 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -105,7 +105,7 @@
                 new MapClientServiceListener(), BluetoothProfile.MAP_CLIENT);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
index caea04f..7582024 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -104,7 +104,7 @@
                 BluetoothProfile.MAP);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
index dfd1622..e1e5dbe 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
@@ -32,7 +32,7 @@
     // Order of this profile in device profiles list
     private static final int ORDINAL = 2;
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return false;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
index 02afe8d..7b81162 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
@@ -78,7 +78,7 @@
             BluetoothProfile.PAN);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index 8fefb2f..1f15601f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -107,7 +107,7 @@
                 new PbapClientServiceListener(), BluetoothProfile.PBAP_CLIENT);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index e9d8cb5..adef0841 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -80,7 +80,7 @@
         BluetoothPbap pbap = new BluetoothPbap(context, new PbapServiceListener());
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
index 61602c6..9a6f104 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java
@@ -106,7 +106,7 @@
                 BluetoothProfile.SAP);
     }
 
-    public boolean isConnectable() {
+    public boolean accessProfileEnabled() {
         return true;
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 7baded8..62b5688 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -118,7 +118,7 @@
      * Test to verify addDevice().
      */
     @Test
-    public void testAddDevice_validCachedDevices_devicesAdded() {
+    public void addDevice_validCachedDevices_devicesAdded() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -136,7 +136,7 @@
      * Test to verify getName().
      */
     @Test
-    public void testGetName_validCachedDevice_nameFound() {
+    public void getName_validCachedDevice_nameFound() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
         assertThat(mCachedDeviceManager.getName(mDevice1)).isEqualTo(DEVICE_ALIAS_1);
@@ -146,7 +146,7 @@
      * Test to verify onDeviceNameUpdated().
      */
     @Test
-    public void testOnDeviceNameUpdated_validName_nameUpdated() {
+    public void onDeviceNameUpdated_validName_nameUpdated() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
         assertThat(cachedDevice1.getName()).isEqualTo(DEVICE_ALIAS_1);
@@ -161,7 +161,7 @@
      * Test to verify clearNonBondedDevices().
      */
     @Test
-    public void testClearNonBondedDevices_bondedAndNonBondedDevices_nonBondedDevicesCleared() {
+    public void clearNonBondedDevices_bondedAndNonBondedDevices_nonBondedDevicesCleared() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -193,7 +193,7 @@
      * Test to verify clearNonBondedDevices() for hearing aids.
      */
     @Test
-    public void testClearNonBondedDevices_HearingAids_nonBondedHAsClearedFromCachedDevicesMap() {
+    public void clearNonBondedDevices_HearingAids_nonBondedHAsClearedFromCachedDevicesMap() {
         when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
         when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
 
@@ -214,7 +214,7 @@
      * Test to verify onHiSyncIdChanged() for hearing aid devices with same HiSyncId.
      */
     @Test
-    public void testOnHiSyncIdChanged_sameHiSyncId_populateInDifferentLists() {
+    public void onHiSyncIdChanged_sameHiSyncId_populateInDifferentLists() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -247,7 +247,7 @@
      * device is connected and other is disconnected. The connected device should be chosen.
      */
     @Test
-    public void testOnHiSyncIdChanged_sameHiSyncIdAndOneConnected_chooseConnectedDevice() {
+    public void onHiSyncIdChanged_sameHiSyncIdAndOneConnected_chooseConnectedDevice() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -282,7 +282,7 @@
      * Test to verify onHiSyncIdChanged() for hearing aid devices with different HiSyncId.
      */
     @Test
-    public void testOnHiSyncIdChanged_differentHiSyncId_populateInSameList() {
+    public void onHiSyncIdChanged_differentHiSyncId_populateInSameList() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -316,7 +316,7 @@
      * Test to verify onProfileConnectionStateChanged() for single hearing aid device connection.
      */
     @Test
-    public void testOnProfileConnectionStateChanged_singleDeviceConnected_visible() {
+    public void onProfileConnectionStateChanged_singleDeviceConnected_visible() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
         cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
@@ -353,7 +353,7 @@
      * devices are disconnected and they get connected.
      */
     @Test
-    public void testOnProfileConnectionStateChanged_twoDevicesConnected_oneDeviceVisible() {
+    public void onProfileConnectionStateChanged_twoDevicesConnected_oneDeviceVisible() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -405,7 +405,7 @@
      * devices are connected and they get disconnected.
      */
     @Test
-    public void testOnProfileConnectionStateChanged_twoDevicesDisconnected_oneDeviceVisible() {
+    public void onProfileConnectionStateChanged_twoDevicesDisconnected_oneDeviceVisible() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -458,7 +458,7 @@
      * Test to verify OnDeviceUnpaired() for a paired hearing Aid device pair.
      */
     @Test
-    public void testOnDeviceUnpaired_bothHearingAidsPaired_removesItsPairFromList() {
+    public void onDeviceUnpaired_bothHearingAidsPaired_removesItsPairFromList() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -488,7 +488,7 @@
      * Test to verify OnDeviceUnpaired() for paired hearing Aid devices which are not a pair.
      */
     @Test
-    public void testOnDeviceUnpaired_bothHearingAidsNotPaired_doesNotRemoveAnyDeviceFromList() {
+    public void onDeviceUnpaired_bothHearingAidsNotPaired_doesNotRemoveAnyDeviceFromList() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -532,7 +532,7 @@
      * Test to verify addDevice() for hearing aid devices with same HiSyncId.
      */
     @Test
-    public void testAddDevice_hearingAidDevicesWithSameHiSyncId_populateInDifferentLists() {
+    public void addDevice_hearingAidDevicesWithSameHiSyncId_populateInDifferentLists() {
         doAnswer((invocation) -> mHearingAidProfile).when(mLocalProfileManager)
             .getHearingAidProfile();
         doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
@@ -560,7 +560,7 @@
      * Test to verify addDevice() for hearing aid devices with different HiSyncId.
      */
     @Test
-    public void testAddDevice_hearingAidDevicesWithDifferentHiSyncId_populateInSameList() {
+    public void addDevice_hearingAidDevicesWithDifferentHiSyncId_populateInSameList() {
         doAnswer((invocation) -> mHearingAidProfile).when(mLocalProfileManager)
             .getHearingAidProfile();
         doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
@@ -592,7 +592,7 @@
      * Test to verify getHearingAidPairDeviceSummary() for hearing aid devices with same HiSyncId.
      */
     @Test
-    public void testGetHearingAidPairDeviceSummary_bothHearingAidsPaired_returnsSummaryOfPair() {
+    public void getHearingAidPairDeviceSummary_bothHearingAidsPaired_returnsSummaryOfPair() {
         mCachedDevice1.setHiSyncId(HISYNCID1);
         mCachedDevice2.setHiSyncId(HISYNCID1);
         mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
@@ -609,7 +609,7 @@
      * HiSyncId.
      */
     @Test
-    public void testGetHearingAidPairDeviceSummary_bothHearingAidsNotPaired_returnsNull() {
+    public void getHearingAidPairDeviceSummary_bothHearingAidsNotPaired_returnsNull() {
         mCachedDevice1.setHiSyncId(HISYNCID1);
         mCachedDevice2.setHiSyncId(HISYNCID2);
         mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
@@ -625,7 +625,7 @@
      * Test to verify updateHearingAidsDevices().
      */
     @Test
-    public void testUpdateHearingAidDevices_hiSyncIdAvailable_setsHiSyncId() {
+    public void updateHearingAidDevices_hiSyncIdAvailable_setsHiSyncId() {
         doAnswer((invocation) -> mHearingAidProfile).when(mLocalProfileManager)
             .getHearingAidProfile();
         doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
@@ -643,7 +643,7 @@
      * Test to verify onBtClassChanged().
      */
     @Test
-    public void testOnBtClassChanged_validBtClass_classChanged() {
+    public void onBtClassChanged_validBtClass_classChanged() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
         assertThat(cachedDevice1.getBtClass()).isEqualTo(DEVICE_CLASS_1);
@@ -658,7 +658,7 @@
      * Test to verify onDeviceDisappeared().
      */
     @Test
-    public void testOnDeviceDisappeared_deviceBondedUnbonded_unbondedDeviceDisappeared() {
+    public void onDeviceDisappeared_deviceBondedUnbonded_unbondedDeviceDisappeared() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
 
@@ -673,7 +673,7 @@
      * Test to verify onActiveDeviceChanged().
      */
     @Test
-    public void testOnActiveDeviceChanged_connectedDevices_activeDeviceChanged() {
+    public void onActiveDeviceChanged_connectedDevices_activeDeviceChanged() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
@@ -736,7 +736,7 @@
      * Test to verify onActiveDeviceChanged() with A2DP and Hearing Aid.
      */
     @Test
-    public void testOnActiveDeviceChanged_withA2dpAndHearingAid() {
+    public void onActiveDeviceChanged_withA2dpAndHearingAid() {
         CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
         assertThat(cachedDevice1).isNotNull();
         CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index c18db11..f6201dd 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -20,6 +20,7 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -79,74 +80,74 @@
     }
 
     @Test
-    public void testGetConnectionSummary_testSingleProfileConnectDisconnect() {
+    public void getConnectionSummary_testSingleProfileConnectDisconnect() {
         // Test without battery level
         // Set PAN profile to be connected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isNull();
 
         // Set PAN profile to be disconnected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isNull();
 
         // Test with battery level
         mBatteryLevel = 10;
         // Set PAN profile to be connected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("10% battery");
 
         // Set PAN profile to be disconnected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isNull();
 
         // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
         mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
 
         // Set PAN profile to be connected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isNull();
 
         // Set PAN profile to be disconnected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isNull();
     }
 
     @Test
-    public void testGetConnectionSummary_testMultipleProfileConnectDisconnect() {
+    public void getConnectionSummary_testMultipleProfileConnectDisconnect() {
         mBatteryLevel = 10;
 
         // Set HFP, A2DP and PAN profile to be connected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
-        mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("10% battery");
 
         // Disconnect HFP only and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
                 "10% battery");
 
         // Disconnect A2DP only and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
                 "10% battery");
 
         // Disconnect both HFP and A2DP and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
                 "10% battery");
 
         // Disconnect all profiles and test connection state summary
-        mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isNull();
     }
 
     @Test
-    public void testGetConnectionSummary_testSingleProfileActiveDeviceA2dp() {
+    public void getConnectionSummary_testSingleProfileActiveDeviceA2dp() {
         // Test without battery level
         // Set A2DP profile to be connected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isNull();
 
         // Set device as Active for A2DP and test connection state summary
@@ -159,26 +160,26 @@
                 "Active, 10% battery");
 
         // Set A2DP profile to be disconnected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isNull();
 
         // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
         mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
         // Set A2DP profile to be connected, Active and test connection state summary
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
 
         // Set A2DP profile to be disconnected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isNull();
     }
 
     @Test
-    public void testGetConnectionSummary_testSingleProfileActiveDeviceHfp() {
+    public void getConnectionSummary_testSingleProfileActiveDeviceHfp() {
         // Test without battery level
         // Set HFP profile to be connected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isNull();
 
         // Set device as Active for HFP and test connection state summary
@@ -193,26 +194,26 @@
                 "Active, 10% battery");
 
         // Set HFP profile to be disconnected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isNull();
 
         // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
         mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
         // Set HFP profile to be connected, Active and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
 
         // Set HFP profile to be disconnected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isNull();
     }
 
     @Test
-    public void testGetConnectionSummary_testSingleProfileActiveDeviceHearingAid() {
+    public void getConnectionSummary_testSingleProfileActiveDeviceHearingAid() {
         // Test without battery level
         // Set Hearing Aid profile to be connected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isNull();
 
         // Set device as Active for Hearing Aid and test connection state summary
@@ -227,11 +228,11 @@
     }
 
     @Test
-    public void testGetConnectionSummary_testMultipleProfilesActiveDevice() {
+    public void getConnectionSummary_testMultipleProfilesActiveDevice() {
         // Test without battery level
         // Set A2DP and HFP profiles to be connected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isNull();
 
         // Set device as Active for A2DP and HFP and test connection state summary
@@ -246,14 +247,14 @@
 
         // Disconnect A2DP only and test connection state summary
         mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.A2DP);
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
                 "10% battery");
 
         // Disconnect HFP only and test connection state summary
         mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEADSET);
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(
                 "Active, 10% battery");
@@ -261,15 +262,15 @@
         // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
         mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
         // Set A2DP and HFP profiles to be connected, Active and test connection state summary
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
         assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
 
         // Set A2DP and HFP profiles to be disconnected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getConnectionSummary()).isNull();
     }
 
@@ -277,32 +278,32 @@
     public void getCarConnectionSummary_singleProfileConnectDisconnect() {
         // Test without battery level
         // Set PAN profile to be connected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
 
         // Set PAN profile to be disconnected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
 
         // Test with battery level
         mBatteryLevel = 10;
         // Set PAN profile to be connected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, battery 10%");
 
         // Set PAN profile to be disconnected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
 
         // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
         mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
 
         // Set PAN profile to be connected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
 
         // Set PAN profile to be disconnected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
     }
 
@@ -311,29 +312,29 @@
         mBatteryLevel = 10;
 
         // Set HFP, A2DP and PAN profile to be connected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
-        mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, battery 10%");
 
         // Disconnect HFP only and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
                 "Connected (no phone), battery 10%");
 
         // Disconnect A2DP only and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
                 "Connected (no media), battery 10%");
 
         // Disconnect both HFP and A2DP and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
                 "Connected (no phone or media), battery 10%");
 
         // Disconnect all profiles and test connection state summary
-        mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
     }
 
@@ -341,7 +342,7 @@
     public void getCarConnectionSummary_singleProfileActiveDeviceA2dp() {
         // Test without battery level
         // Set A2DP profile to be connected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
 
         // Set device as Active for A2DP and test connection state summary
@@ -354,18 +355,18 @@
                 "Connected, battery 10%, active (media)");
 
         // Set A2DP profile to be disconnected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
 
         // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
         mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
         // Set A2DP profile to be connected, Active and test connection state summary
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
         assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, active (media)");
 
         // Set A2DP profile to be disconnected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
     }
 
@@ -373,7 +374,7 @@
     public void getCarConnectionSummary_singleProfileActiveDeviceHfp() {
         // Test without battery level
         // Set HFP profile to be connected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
 
         // Set device as Active for HFP and test connection state summary
@@ -386,18 +387,18 @@
                 "Connected, battery 10%, active (phone)");
 
         // Set HFP profile to be disconnected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
 
         // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
         mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
         // Set HFP profile to be connected, Active and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
         assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, active (phone)");
 
         // Set HFP profile to be disconnected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
     }
 
@@ -405,7 +406,7 @@
     public void getCarConnectionSummary_singleProfileActiveDeviceHearingAid() {
         // Test without battery level
         // Set Hearing Aid profile to be connected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
 
         // Set device as Active for Hearing Aid and test connection state summary
@@ -414,8 +415,7 @@
 
         // Set Hearing Aid profile to be disconnected and test connection state summary
         mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEARING_AID);
-        mCachedDevice.onProfileStateChanged(mHearingAidProfile,
-                BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
     }
 
@@ -423,8 +423,8 @@
     public void getCarConnectionSummary_multipleProfilesActiveDevice() {
         // Test without battery level
         // Set A2DP and HFP profiles to be connected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected");
 
         // Set device as Active for A2DP and HFP and test connection state summary
@@ -439,14 +439,14 @@
 
         // Disconnect A2DP only and test connection state summary
         mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.A2DP);
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
                 "Connected (no media), battery 10%, active (phone)");
 
         // Disconnect HFP only and test connection state summary
         mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEADSET);
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
         assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo(
                 "Connected (no phone), battery 10%, active (media)");
@@ -454,21 +454,21 @@
         // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
         mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
         // Set A2DP and HFP profiles to be connected, Active and test connection state summary
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.A2DP);
         mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEADSET);
         assertThat(mCachedDevice.getCarConnectionSummary()).isEqualTo("Connected, active");
 
         // Set A2DP and HFP profiles to be disconnected and test connection state summary
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.getCarConnectionSummary()).isNull();
     }
 
 
     @Test
-    public void testDeviceName_testAliasNameAvailable() {
+    public void deviceName_testAliasNameAvailable() {
         when(mDevice.getAliasName()).thenReturn(DEVICE_ALIAS);
         when(mDevice.getName()).thenReturn(DEVICE_NAME);
         CachedBluetoothDevice cachedBluetoothDevice =
@@ -480,7 +480,7 @@
     }
 
     @Test
-    public void testDeviceName_testNameNotAvailable() {
+    public void deviceName_testNameNotAvailable() {
         CachedBluetoothDevice cachedBluetoothDevice =
                 new CachedBluetoothDevice(mContext, mProfileManager, mDevice);
         // Verify device address is returned on getName
@@ -490,7 +490,7 @@
     }
 
     @Test
-    public void testDeviceName_testRenameDevice() {
+    public void deviceName_testRenameDevice() {
         final String[] alias = {DEVICE_ALIAS};
         doAnswer(invocation -> alias[0]).when(mDevice).getAliasName();
         doAnswer(invocation -> {
@@ -513,7 +513,7 @@
     }
 
     @Test
-    public void testSetActive() {
+    public void setActive() {
         when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
         when(mProfileManager.getHeadsetProfile()).thenReturn(mHfpProfile);
         when(mA2dpProfile.setActiveDevice(any(BluetoothDevice.class))).thenReturn(true);
@@ -521,19 +521,19 @@
 
         assertThat(mCachedDevice.setActive()).isFalse();
 
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.setActive()).isTrue();
 
-        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
         assertThat(mCachedDevice.setActive()).isTrue();
 
-        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mCachedDevice.setActive()).isFalse();
     }
 
     @Test
-    public void testIsA2dpDevice_isA2dpDevice() {
+    public void isA2dpDevice_isA2dpDevice() {
         when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
         when(mA2dpProfile.getConnectionStatus(mDevice)).
                 thenReturn(BluetoothProfile.STATE_CONNECTED);
@@ -542,7 +542,7 @@
     }
 
     @Test
-    public void testIsA2dpDevice_isNotA2dpDevice() {
+    public void isA2dpDevice_isNotA2dpDevice() {
         when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
         when(mA2dpProfile.getConnectionStatus(mDevice)).
                 thenReturn(BluetoothProfile.STATE_DISCONNECTING);
@@ -551,7 +551,7 @@
     }
 
     @Test
-    public void testIsHfpDevice_isHfpDevice() {
+    public void isHfpDevice_isHfpDevice() {
         when(mProfileManager.getHeadsetProfile()).thenReturn(mHfpProfile);
         when(mHfpProfile.getConnectionStatus(mDevice)).
                 thenReturn(BluetoothProfile.STATE_CONNECTED);
@@ -637,4 +637,24 @@
 
         verify(mDevice, never()).setAlias(any());
     }
+
+    @Test
+    public void getProfileConnectionState_nullProfile_returnDisconnected() {
+        assertThat(mCachedDevice.getProfileConnectionState(null)).isEqualTo(
+                BluetoothProfile.STATE_DISCONNECTED);
+    }
+
+    @Test
+    public void getProfileConnectionState_profileConnected_returnConnected() {
+        doReturn(BluetoothProfile.STATE_CONNECTED).when(mA2dpProfile).getConnectionStatus(
+                any(BluetoothDevice.class));
+
+        assertThat(mCachedDevice.getProfileConnectionState(mA2dpProfile)).isEqualTo(
+                BluetoothProfile.STATE_CONNECTED);
+    }
+
+    private void updateProfileStatus(LocalBluetoothProfile profile, int status) {
+        doReturn(status).when(profile).getConnectionStatus(mDevice);
+        mCachedDevice.onProfileStateChanged(profile, status);
+    }
 }
diff --git a/packages/SystemUI/res/layout/qs_detail_buttons.xml b/packages/SystemUI/res/layout/qs_detail_buttons.xml
index 03ed62b..75f43f9 100644
--- a/packages/SystemUI/res/layout/qs_detail_buttons.xml
+++ b/packages/SystemUI/res/layout/qs_detail_buttons.xml
@@ -26,6 +26,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginEnd="8dp"
+        android:minHeight="48dp"
         android:minWidth="132dp"
         android:textAppearance="@style/TextAppearance.QS.DetailButton"
         android:focusable="true" />
@@ -35,6 +36,7 @@
         style="@style/QSBorderlessButton"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:minHeight="48dp"
         android:minWidth="88dp"
         android:textAppearance="@style/TextAppearance.QS.DetailButton"
         android:focusable="true"/>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index b159b39..b8df3c06 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -342,12 +342,11 @@
                 case SimPuk:
                     // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
                     SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
-                    if (securityMode != SecurityMode.None
-                            || !mLockPatternUtils.isLockScreenDisabled(
+                    if (securityMode == SecurityMode.None || mLockPatternUtils.isLockScreenDisabled(
                             KeyguardUpdateMonitor.getCurrentUser())) {
-                        showSecurityScreen(securityMode);
-                    } else {
                         finish = true;
+                    } else {
+                        showSecurityScreen(securityMode);
                     }
                     break;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index 12daff1..9edd65e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -25,6 +25,7 @@
 import android.graphics.drawable.Drawable;
 import android.nfc.NfcAdapter;
 import android.provider.Settings;
+import android.service.quicksettings.Tile;
 import android.widget.Switch;
 
 import com.android.internal.logging.MetricsLogger;
@@ -77,6 +78,9 @@
 
     @Override
     protected void handleClick() {
+        if (getAdapter() == null) {
+            return;
+        }
         if (!getAdapter().isEnabled()) {
             getAdapter().enable();
         } else {
@@ -96,13 +100,13 @@
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
-        final Drawable mEnable = mContext.getDrawable(R.drawable.ic_qs_nfc_enabled);
-        final Drawable mDisable = mContext.getDrawable(R.drawable.ic_qs_nfc_disabled);
-
-        if (getAdapter() == null) return;
-        state.value = getAdapter().isEnabled();
+        state.value = getAdapter() != null && getAdapter().isEnabled();
+        state.state = getAdapter() == null
+                ? Tile.STATE_UNAVAILABLE
+                : state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+        state.icon = ResourceIcon.get(
+                state.value ? R.drawable.ic_qs_nfc_enabled : R.drawable.ic_qs_nfc_disabled);
         state.label = mContext.getString(R.string.quick_settings_nfc_label);
-        state.icon = new DrawableIcon(state.value ? mEnable : mDisable);
         state.expandedAccessibilityClassName = Switch.class.getName();
         state.contentDescription = state.label;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 304a00f..2d03ed0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.car;
 
 import android.app.ActivityTaskManager;
-import android.car.user.CarUserManagerHelper;
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
 import android.util.Log;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index 67e512c..618a4c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -22,7 +22,7 @@
 import android.app.AlertDialog;
 import android.app.AlertDialog.Builder;
 import android.app.Dialog;
-import android.car.user.CarUserManagerHelper;
+import android.car.userlib.CarUserManagerHelper;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.pm.UserInfo;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
index 4f957bf..c7ab27b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ContextualButton.java
@@ -67,9 +67,15 @@
     }
 
     protected KeyButtonDrawable getNewDrawable() {
-        return KeyButtonDrawable.create(getContext(), mIconResId, false /* shadow */);
+        return KeyButtonDrawable.create(getContext().getApplicationContext(), mIconResId,
+                false /* shadow */);
     }
 
+    /**
+     * This context is from the view that could be stale after rotation or config change. To get
+     * correct resources use getApplicationContext() as well.
+     * @return current view context
+     */
     protected Context getContext() {
         return getCurrentView().getContext();
     }
diff --git a/services/backup/java/com/android/server/backup/keyvalue/AgentException.java b/services/backup/java/com/android/server/backup/keyvalue/AgentException.java
new file mode 100644
index 0000000..e2ca351
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/keyvalue/AgentException.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 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.backup.keyvalue;
+
+/**
+ * This represents something wrong with a specific package. For example:
+ * <ul>
+ *     <li>Package unknown.
+ *     <li>Package is not eligible for backup anymore.
+ *     <li>Backup agent timed out.
+ *     <li>Backup agent wrote protected keys.
+ *     <li>...
+ * </ul>
+ *
+ * @see KeyValueBackupTask
+ * @see TaskException
+ */
+class AgentException extends BackupException {
+    static AgentException transitory() {
+        return new AgentException(/* transitory */ true);
+    }
+
+    static AgentException transitory(Exception cause) {
+        return new AgentException(/* transitory */ true, cause);
+    }
+
+    static AgentException permanent() {
+        return new AgentException(/* transitory */ false);
+    }
+
+    static AgentException permanent(Exception cause) {
+        return new AgentException(/* transitory */ false, cause);
+    }
+
+    private final boolean mTransitory;
+
+    private AgentException(boolean transitory) {
+        mTransitory = transitory;
+    }
+
+    private AgentException(boolean transitory, Exception cause) {
+        super(cause);
+        mTransitory = transitory;
+    }
+
+    boolean isTransitory() {
+        return mTransitory;
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/BackupException.java b/services/backup/java/com/android/server/backup/keyvalue/BackupException.java
new file mode 100644
index 0000000..27b2d35
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/keyvalue/BackupException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 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.backup.keyvalue;
+
+import android.util.AndroidException;
+
+/**
+ * Key-value backup task exception.
+ *
+ * @see AgentException
+ * @see TaskException
+ */
+class BackupException extends AndroidException {
+    BackupException() {}
+
+    BackupException(Exception cause) {
+        super(cause);
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
index a92070c..bb8a1d1 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupReporter.java
@@ -153,16 +153,18 @@
                 mObserver, packageName, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
     }
 
-    void onBindAgentError(SecurityException e) {
-        Slog.d(TAG, "Error in bind/backup", e);
-    }
-
     void onAgentUnknown(String packageName) {
         Slog.d(TAG, "Package does not exist, skipping");
         BackupObserverUtils.sendBackupOnPackageResult(
                 mObserver, packageName, BackupManager.ERROR_PACKAGE_NOT_FOUND);
     }
 
+    void onBindAgentError(String packageName, SecurityException e) {
+        Slog.d(TAG, "Error in bind/backup", e);
+        BackupObserverUtils.sendBackupOnPackageResult(
+                mObserver, packageName, BackupManager.ERROR_AGENT_FAILURE);
+    }
+
     void onAgentError(String packageName) {
         if (MORE_DEBUG) {
             Slog.i(TAG, "Agent failure for " + packageName + ", re-staging");
@@ -190,6 +192,8 @@
     void onCallAgentDoBackupError(String packageName, boolean callingAgent, Exception e) {
         if (callingAgent) {
             Slog.e(TAG, "Error invoking agent on " + packageName + ": " + e);
+            BackupObserverUtils.sendBackupOnPackageResult(
+                    mObserver, packageName, BackupManager.ERROR_AGENT_FAILURE);
         } else {
             Slog.e(TAG, "Error before invoking agent on " + packageName + ": " + e);
         }
@@ -220,12 +224,8 @@
         }
     }
 
-    void onReadAgentDataError(String packageName, IOException e) {
-        Slog.w(TAG, "Unable read backup data for " + packageName + ": " + e);
-    }
-
-    void onWriteWidgetDataError(String packageName, IOException e) {
-        Slog.w(TAG, "Unable to save widget data for " + packageName + ": " + e);
+    void onAgentDataError(String packageName, IOException e) {
+        Slog.w(TAG, "Unable to read/write agent data for " + packageName + ": " + e);
     }
 
     void onDigestError(NoSuchAlgorithmException e) {
@@ -243,16 +243,12 @@
         }
     }
 
-    void onSendDataToTransport(String packageName) {
+    void onTransportPerformBackup(String packageName) {
         if (MORE_DEBUG) {
             Slog.v(TAG, "Sending non-empty data to transport for " + packageName);
         }
     }
 
-    void onNonIncrementalAndNonIncrementalRequired() {
-        Slog.e(TAG, "Transport requested non-incremental but already the case");
-    }
-
     void onEmptyData(PackageInfo packageInfo) {
         if (MORE_DEBUG) {
             Slog.i(TAG, "No backup data written, not calling transport");
@@ -302,13 +298,20 @@
                 /* extras */ null);
     }
 
+    void onPackageBackupNonIncrementalAndNonIncrementalRequired(String packageName) {
+        Slog.e(TAG, "Transport requested non-incremental but already the case");
+        BackupObserverUtils.sendBackupOnPackageResult(
+                mObserver, packageName, BackupManager.ERROR_TRANSPORT_ABORTED);
+        EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
+    }
+
     void onPackageBackupTransportFailure(String packageName) {
         BackupObserverUtils.sendBackupOnPackageResult(
                 mObserver, packageName, BackupManager.ERROR_TRANSPORT_ABORTED);
         EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, packageName);
     }
 
-    void onPackageBackupError(String packageName, Exception e) {
+    void onPackageBackupTransportError(String packageName, Exception e) {
         Slog.e(TAG, "Transport error backing up " + packageName, e);
         BackupObserverUtils.sendBackupOnPackageResult(
                 mObserver, packageName, BackupManager.ERROR_TRANSPORT_ABORTED);
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index e915ce1..6904b3f 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -16,6 +16,7 @@
 
 package com.android.server.backup.keyvalue;
 
+import static android.app.ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL;
 import static android.os.ParcelFileDescriptor.MODE_CREATE;
 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
@@ -25,8 +26,8 @@
 import static com.android.server.backup.BackupManagerService.OP_PENDING;
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
-import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
 import android.app.backup.BackupAgent;
 import android.app.backup.BackupDataInput;
@@ -47,7 +48,6 @@
 import android.os.SELinux;
 import android.os.UserHandle;
 import android.os.WorkSource;
-import android.util.Pair;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -77,6 +77,8 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
@@ -173,10 +175,8 @@
     private static final AtomicInteger THREAD_COUNT = new AtomicInteger();
     private static final String BLANK_STATE_FILE_NAME = "blank_state";
     private static final String PM_PACKAGE = BackupManagerService.PACKAGE_MANAGER_SENTINEL;
-    @VisibleForTesting
-    public static final String STAGING_FILE_SUFFIX = ".data";
-    @VisibleForTesting
-    public static final String NEW_STATE_FILE_SUFFIX = ".new";
+    @VisibleForTesting public static final String STAGING_FILE_SUFFIX = ".data";
+    @VisibleForTesting public static final String NEW_STATE_FILE_SUFFIX = ".new";
 
     /**
      * Creates a new {@link KeyValueBackupTask} for key-value backup operation, spins up a new
@@ -244,13 +244,13 @@
     private final int mCurrentOpToken;
     private final File mStateDirectory;
     private final File mDataDirectory;
+    private final File mBlankStateFile;
     private final List<String> mOriginalQueue;
     private final List<String> mQueue;
     private final List<String> mPendingFullBackups;
     private final Object mQueueLock;
     @Nullable private final DataChangedJournal mJournal;
 
-    private int mStatus;
     @Nullable private PerformFullTransportBackupTask mFullBackupTask;
     @Nullable private IBackupAgent mAgent;
     @Nullable private PackageInfo mCurrentPackage;
@@ -316,6 +316,7 @@
         mDataDirectory = mBackupManagerService.getDataDir();
         mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
         mQueueLock = mBackupManagerService.getQueueLock();
+        mBlankStateFile = new File(mStateDirectory, BLANK_STATE_FILE_NAME);
     }
 
     private void registerTask() {
@@ -331,45 +332,43 @@
     public void run() {
         Process.setThreadPriority(THREAD_PRIORITY);
 
-        boolean processQueue = startTask();
-        while (processQueue && !mQueue.isEmpty() && !mCancelled) {
-            String packageName = mQueue.remove(0);
-            if (PM_PACKAGE.equals(packageName)) {
-                processQueue = backupPm();
-            } else {
-                processQueue = backupPackage(packageName);
+        int status = BackupTransport.TRANSPORT_OK;
+        try {
+            startTask();
+            while (!mQueue.isEmpty() && !mCancelled) {
+                String packageName = mQueue.remove(0);
+                try {
+                    if (PM_PACKAGE.equals(packageName)) {
+                        backupPm();
+                    } else {
+                        backupPackage(packageName);
+                    }
+                } catch (AgentException e) {
+                    if (e.isTransitory()) {
+                        // We try again this package in the next backup pass.
+                        mBackupManagerService.dataChangedImpl(packageName);
+                    }
+                }
             }
+        } catch (TaskException e) {
+            if (e.isStateCompromised()) {
+                mBackupManagerService.resetBackupState(mStateDirectory);
+            }
+            revertTask();
+            status = e.getStatus();
         }
-        finishTask();
+        finishTask(status);
     }
 
-    /** Returns whether to consume next queue package. */
-    private boolean handleAgentResult(@Nullable PackageInfo packageInfo, RemoteResult result) {
-        if (result == RemoteResult.FAILED_THREAD_INTERRUPTED) {
-            // Not an explicit cancel, we need to flag it.
-            mCancelled = true;
-            mReporter.onAgentCancelled(packageInfo);
-            cleanUpAgentForAgentError();
-            return false;
+    /** Returns transport status. */
+    private int sendDataToTransport(@Nullable PackageInfo packageInfo)
+            throws AgentException, TaskException {
+        try {
+            return sendDataToTransport();
+        } catch (IOException e) {
+            mReporter.onAgentDataError(packageInfo.packageName, e);
+            throw TaskException.causedBy(e);
         }
-        if (result == RemoteResult.FAILED_CANCELLED) {
-            mReporter.onAgentCancelled(packageInfo);
-            cleanUpAgentForAgentError();
-            return false;
-        }
-        if (result == RemoteResult.FAILED_TIMED_OUT) {
-            mReporter.onAgentTimedOut(packageInfo);
-            cleanUpAgentForAgentError();
-            return true;
-        }
-        Preconditions.checkState(result.isPresent());
-        long agentResult = result.get();
-        if (agentResult == BackupAgent.RESULT_ERROR) {
-            mReporter.onAgentResultError(packageInfo);
-            cleanUpAgentForAgentError();
-            return true;
-        }
-        return sendDataToTransport();
     }
 
     @Override
@@ -378,11 +377,10 @@
     @Override
     public void operationComplete(long unusedResult) {}
 
-    /** Returns whether to consume next queue package. */
-    private boolean startTask() {
+    private void startTask() throws TaskException {
         if (mBackupManagerService.isBackupOperationInProgress()) {
             mReporter.onSkipBackup();
-            return false;
+            throw TaskException.create();
         }
 
         // Unfortunately full backup task constructor registers the task with BMS, so we have to
@@ -390,11 +388,9 @@
         mFullBackupTask = createFullBackupTask(mPendingFullBackups);
         registerTask();
 
-        mStatus = BackupTransport.TRANSPORT_OK;
-
         if (mQueue.isEmpty() && mPendingFullBackups.isEmpty()) {
             mReporter.onEmptyQueueAtStart();
-            return false;
+            return;
         }
         // We only backup PM if it was explicitly in the queue or if it's incremental.
         boolean backupPm = mQueue.remove(PM_PACKAGE) || !mNonIncremental;
@@ -415,20 +411,18 @@
             if (pmState.length() <= 0) {
                 mReporter.onInitializeTransport(transportName);
                 mBackupManagerService.resetBackupState(mStateDirectory);
-                mStatus = transport.initializeDevice();
-                mReporter.onTransportInitialized(mStatus);
+                int status = transport.initializeDevice();
+                mReporter.onTransportInitialized(status);
+                if (status != BackupTransport.TRANSPORT_OK) {
+                    throw TaskException.stateCompromised();
+                }
             }
+        } catch (TaskException e) {
+            throw e;
         } catch (Exception e) {
             mReporter.onInitializeTransportError(e);
-            mStatus = BackupTransport.TRANSPORT_ERROR;
+            throw TaskException.stateCompromised();
         }
-
-        if (mStatus != BackupTransport.TRANSPORT_OK) {
-            mBackupManagerService.resetBackupState(mStateDirectory);
-            return false;
-        }
-
-        return true;
     }
 
     private PerformFullTransportBackupTask createFullBackupTask(List<String> packages) {
@@ -446,120 +440,82 @@
                 mUserInitiated);
     }
 
-    /** Returns whether to consume next queue package. */
-    private boolean backupPm() {
-        RemoteResult agentResult = null;
+    private void backupPm() throws TaskException {
+        mReporter.onStartPackageBackup(PM_PACKAGE);
+        mCurrentPackage = new PackageInfo();
+        mCurrentPackage.packageName = PM_PACKAGE;
+
         try {
-            mCurrentPackage = new PackageInfo();
-            mCurrentPackage.packageName = PM_PACKAGE;
-
-            // Since PM is running in the system process we can set up its agent directly.
-            BackupAgent pmAgent = mBackupManagerService.makeMetadataAgent();
-            mAgent = IBackupAgent.Stub.asInterface(pmAgent.onBind());
-
-            Pair<Integer, RemoteResult> statusAndResult = extractAgentData(PM_PACKAGE, mAgent);
-            mStatus = statusAndResult.first;
-            agentResult = statusAndResult.second;
-        } catch (Exception e) {
+            extractPmAgentData(mCurrentPackage);
+            int status = sendDataToTransport(mCurrentPackage);
+            cleanUpAgentForTransportStatus(status);
+        } catch (AgentException | TaskException e) {
             mReporter.onExtractPmAgentDataError(e);
-            mStatus = BackupTransport.TRANSPORT_ERROR;
+            cleanUpAgentForError(e);
+            // PM agent failure is task failure.
+            throw TaskException.stateCompromised(e);
         }
-
-        if (mStatus != BackupTransport.TRANSPORT_OK) {
-            // In this case either extractAgentData() already made the agent clean-up or we haven't
-            // prepared the state for calling the agent, in either case we don't need to clean-up.
-            mBackupManagerService.resetBackupState(mStateDirectory);
-            return false;
-        }
-
-        Preconditions.checkNotNull(agentResult);
-        return handleAgentResult(mCurrentPackage, agentResult);
     }
 
-    /** Returns whether to consume next queue package. */
-    private boolean backupPackage(String packageName) {
+    private void backupPackage(String packageName) throws AgentException, TaskException {
         mReporter.onStartPackageBackup(packageName);
-        mStatus = BackupTransport.TRANSPORT_OK;
+        mCurrentPackage = getPackageForBackup(packageName);
 
-        // Verify that the requested app is eligible for key-value backup.
-        RemoteResult agentResult = null;
         try {
-            mCurrentPackage = mPackageManager.getPackageInfo(
-                    packageName, PackageManager.GET_SIGNING_CERTIFICATES);
-            ApplicationInfo applicationInfo = mCurrentPackage.applicationInfo;
-            if (!AppBackupUtils.appIsEligibleForBackup(applicationInfo, mPackageManager)) {
-                // The manifest has changed. This won't happen again because the app won't be
-                // requesting further backups.
-                mReporter.onPackageNotEligibleForBackup(packageName);
-                return true;
-            }
-
-            if (AppBackupUtils.appGetsFullBackup(mCurrentPackage)) {
-                // Initially enqueued for key-value backup, but only supports full-backup now.
-                mReporter.onPackageEligibleForFullBackup(packageName);
-                return true;
-            }
-
-            if (AppBackupUtils.appIsStopped(applicationInfo)) {
-                // Just as it won't receive broadcasts, we won't run it for backup.
-                mReporter.onPackageStopped(packageName);
-                return true;
-            }
-
-            try {
-                mBackupManagerService.setWorkSource(new WorkSource(applicationInfo.uid));
-                IBackupAgent agent =
-                        mBackupManagerService.bindToAgentSynchronous(
-                                applicationInfo,
-                                ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
-                if (agent != null) {
-                    mAgent = agent;
-                    Pair<Integer, RemoteResult> statusAndResult =
-                            extractAgentData(packageName, agent);
-                    mStatus = statusAndResult.first;
-                    agentResult = statusAndResult.second;
-                } else {
-                    // Timeout waiting for the agent to bind.
-                    mStatus = BackupTransport.AGENT_ERROR;
-                }
-            } catch (SecurityException e) {
-                mReporter.onBindAgentError(e);
-                mStatus = BackupTransport.AGENT_ERROR;
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            mStatus = BackupTransport.AGENT_UNKNOWN;
-        } finally {
-            mBackupManagerService.setWorkSource(null);
+            extractAgentData(mCurrentPackage);
+            int status = sendDataToTransport(mCurrentPackage);
+            cleanUpAgentForTransportStatus(status);
+        } catch (AgentException | TaskException e) {
+            cleanUpAgentForError(e);
+            throw e;
         }
-
-        if (mStatus != BackupTransport.TRANSPORT_OK) {
-            // In this case either extractAgentData() already made the agent clean-up or we haven't
-            // prepared the state for calling the agent, in either case we don't need to clean-up.
-            Preconditions.checkState(mAgent == null);
-
-            if (mStatus == BackupTransport.AGENT_ERROR) {
-                mReporter.onAgentError(packageName);
-                mBackupManagerService.dataChangedImpl(packageName);
-                mStatus = BackupTransport.TRANSPORT_OK;
-                return true;
-            }
-
-            if (mStatus == BackupTransport.AGENT_UNKNOWN) {
-                mReporter.onAgentUnknown(packageName);
-                mStatus = BackupTransport.TRANSPORT_OK;
-                return true;
-            }
-
-            // Transport-level failure, re-enqueue everything.
-            revertTask();
-            return false;
-        }
-
-        Preconditions.checkNotNull(agentResult);
-        return handleAgentResult(mCurrentPackage, agentResult);
     }
 
-    private void finishTask() {
+    private PackageInfo getPackageForBackup(String packageName) throws AgentException {
+        final PackageInfo packageInfo;
+        try {
+            packageInfo =
+                    mPackageManager.getPackageInfo(
+                            packageName, PackageManager.GET_SIGNING_CERTIFICATES);
+        } catch (PackageManager.NameNotFoundException e) {
+            mReporter.onAgentUnknown(packageName);
+            throw AgentException.permanent(e);
+        }
+        ApplicationInfo applicationInfo = packageInfo.applicationInfo;
+        if (!AppBackupUtils.appIsEligibleForBackup(applicationInfo, mPackageManager)) {
+            mReporter.onPackageNotEligibleForBackup(packageName);
+            throw AgentException.permanent();
+        }
+        if (AppBackupUtils.appGetsFullBackup(packageInfo)) {
+            mReporter.onPackageEligibleForFullBackup(packageName);
+            throw AgentException.permanent();
+        }
+        if (AppBackupUtils.appIsStopped(applicationInfo)) {
+            mReporter.onPackageStopped(packageName);
+            throw AgentException.permanent();
+        }
+        return packageInfo;
+    }
+
+    private IBackupAgent bindAgent(PackageInfo packageInfo) throws AgentException {
+        String packageName = packageInfo.packageName;
+        final IBackupAgent agent;
+        try {
+            agent =
+                    mBackupManagerService.bindToAgentSynchronous(
+                            packageInfo.applicationInfo, BACKUP_MODE_INCREMENTAL);
+            if (agent == null) {
+                mReporter.onAgentError(packageName);
+                throw AgentException.transitory();
+            }
+        } catch (SecurityException e) {
+            mReporter.onBindAgentError(packageName, e);
+            throw AgentException.transitory(e);
+        }
+        return agent;
+    }
+
+    private void finishTask(int status) {
         // Mark packages that we couldn't backup as pending backup.
         for (String packageName : mQueue) {
             mBackupManagerService.dataChangedImpl(packageName);
@@ -576,7 +532,7 @@
         // If we succeeded and this is the first time we've done a backup, we can record the current
         // backup dataset token.
         long currentToken = mBackupManagerService.getCurrentToken();
-        if ((mStatus == BackupTransport.TRANSPORT_OK) && (currentToken == 0)) {
+        if ((status == BackupTransport.TRANSPORT_OK) && (currentToken == 0)) {
             try {
                 IBackupTransport transport = mTransportClient.connectOrThrow(callerLogString);
                 mBackupManagerService.setCurrentToken(transport.getCurrentRestoreSet());
@@ -589,9 +545,14 @@
 
         synchronized (mQueueLock) {
             mBackupManagerService.setBackupRunning(false);
-            if (mStatus == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
+            if (status == BackupTransport.TRANSPORT_NOT_INITIALIZED) {
                 mReporter.onTransportNotInitialized();
-                triggerTransportInitializationLocked();
+                try {
+                    triggerTransportInitializationLocked();
+                } catch (Exception e) {
+                    mReporter.onPendingInitializeTransportError(e);
+                    status = BackupTransport.TRANSPORT_ERROR;
+                }
             }
         }
 
@@ -605,7 +566,7 @@
         }
 
         if (!mCancelled
-                && mStatus == BackupTransport.TRANSPORT_OK
+                && status == BackupTransport.TRANSPORT_OK
                 && mFullBackupTask != null
                 && !mPendingFullBackups.isEmpty()) {
             mReporter.onStartFullBackup(mPendingFullBackups);
@@ -621,7 +582,7 @@
             mFullBackupTask.unregisterTask();
         }
         mTaskFinishedListener.onFinished(callerLogString);
-        mReporter.onBackupFinished(getBackupFinishedStatus(mCancelled, mStatus));
+        mReporter.onBackupFinished(getBackupFinishedStatus(mCancelled, status));
         mBackupManagerService.getWakelock().release();
     }
 
@@ -642,17 +603,12 @@
     }
 
     @GuardedBy("mQueueLock")
-    private void triggerTransportInitializationLocked() {
-        try {
-            IBackupTransport transport =
-                    mTransportClient.connectOrThrow("KVBT.triggerTransportInitializationLocked");
-            mBackupManagerService.getPendingInits().add(transport.name());
-            deletePmStateFile();
-            mBackupManagerService.backupNow();
-        } catch (Exception e) {
-            mReporter.onPendingInitializeTransportError(e);
-            mStatus = BackupTransport.TRANSPORT_ERROR;
-        }
+    private void triggerTransportInitializationLocked() throws Exception {
+        IBackupTransport transport =
+                mTransportClient.connectOrThrow("KVBT.triggerTransportInitializationLocked");
+        mBackupManagerService.getPendingInits().add(transport.name());
+        deletePmStateFile();
+        mBackupManagerService.backupNow();
     }
 
     /** Removes PM state, triggering initialization in the next key-value task. */
@@ -660,35 +616,69 @@
         new File(mStateDirectory, PM_PACKAGE).delete();
     }
 
+    /** Same as {@link #extractAgentData(PackageInfo)}, but only for PM package. */
+    private void extractPmAgentData(PackageInfo packageInfo) throws AgentException, TaskException {
+        Preconditions.checkArgument(packageInfo.packageName.equals(PM_PACKAGE));
+        BackupAgent pmAgent = mBackupManagerService.makeMetadataAgent();
+        mAgent = IBackupAgent.Stub.asInterface(pmAgent.onBind());
+        extractAgentData(packageInfo, mAgent);
+    }
+
     /**
-     * Returns a {@link Pair}. The first of the pair contains the status. In case the status is
-     * {@link BackupTransport#TRANSPORT_OK}, the second of the pair contains the agent result,
-     * otherwise {@code null}.
+     * Binds to the agent and extracts its backup data. If this method returns, the data in {@code
+     * mBackupData} is ready to be sent to the transport, otherwise it will throw.
+     *
+     * <p>This method leaves agent resources (agent binder, files and file-descriptors) opened that
+     * need to be cleaned up after terminating, either successfully or exceptionally. This clean-up
+     * can be done with methods {@link #cleanUpAgentForTransportStatus(int)} and {@link
+     * #cleanUpAgentForError(BackupException)}, depending on whether data was successfully sent to
+     * the transport or not. It's the caller responsibility to do the clean-up or delegate it.
      */
-    private Pair<Integer, RemoteResult> extractAgentData(String packageName, IBackupAgent agent) {
+    private void extractAgentData(PackageInfo packageInfo) throws AgentException, TaskException {
+        mBackupManagerService.setWorkSource(new WorkSource(packageInfo.applicationInfo.uid));
+        try {
+            mAgent = bindAgent(packageInfo);
+            extractAgentData(packageInfo, mAgent);
+        } finally {
+            mBackupManagerService.setWorkSource(null);
+        }
+    }
+
+    /**
+     * Calls agent {@link IBackupAgent#doBackup(ParcelFileDescriptor, ParcelFileDescriptor,
+     * ParcelFileDescriptor, long, IBackupCallback, int)} and waits for the result. If this method
+     * returns, the data in {@code mBackupData} is ready to be sent to the transport, otherwise it
+     * will throw.
+     *
+     * <p>This method creates files and file-descriptors for the agent that need to be deleted and
+     * closed after terminating, either successfully or exceptionally. This clean-up can be done
+     * with methods {@link #cleanUpAgentForTransportStatus(int)} and {@link
+     * #cleanUpAgentForError(BackupException)}, depending on whether data was successfully sent to
+     * the transport or not. It's the caller responsibility to do the clean-up or delegate it.
+     */
+    private void extractAgentData(PackageInfo packageInfo, IBackupAgent agent)
+            throws AgentException, TaskException {
+        String packageName = packageInfo.packageName;
         mReporter.onExtractAgentData(packageName);
 
-        File blankStateFile = new File(mStateDirectory, BLANK_STATE_FILE_NAME);
         mSavedStateFile = new File(mStateDirectory, packageName);
         mBackupDataFile = new File(mDataDirectory, packageName + STAGING_FILE_SUFFIX);
         mNewStateFile = new File(mStateDirectory, packageName + NEW_STATE_FILE_SUFFIX);
         mReporter.onAgentFilesReady(mBackupDataFile);
 
-        mSavedState = null;
-        mBackupData = null;
-        mNewState = null;
-
         boolean callingAgent = false;
         final RemoteResult agentResult;
         try {
-            File savedStateFileForAgent = (mNonIncremental) ? blankStateFile : mSavedStateFile;
+            File savedStateFileForAgent = (mNonIncremental) ? mBlankStateFile : mSavedStateFile;
             // MODE_CREATE to make an empty file if necessary
-            mSavedState = ParcelFileDescriptor.open(
-                    savedStateFileForAgent, MODE_READ_ONLY | MODE_CREATE);
-            mBackupData = ParcelFileDescriptor.open(
-                    mBackupDataFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
-            mNewState = ParcelFileDescriptor.open(
-                    mNewStateFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
+            mSavedState =
+                    ParcelFileDescriptor.open(savedStateFileForAgent, MODE_READ_ONLY | MODE_CREATE);
+            mBackupData =
+                    ParcelFileDescriptor.open(
+                            mBackupDataFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
+            mNewState =
+                    ParcelFileDescriptor.open(
+                            mNewStateFile, MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE);
 
             if (!SELinux.restorecon(mBackupDataFile)) {
                 mReporter.onRestoreconFailed(mBackupDataFile);
@@ -713,15 +703,40 @@
                             "doBackup()");
         } catch (Exception e) {
             mReporter.onCallAgentDoBackupError(packageName, callingAgent, e);
-            cleanUpAgentForAgentError();
-            // TODO: Remove the check on callingAgent when RemoteCall supports local agent calls.
-            int status =
-                    callingAgent ? BackupTransport.AGENT_ERROR : BackupTransport.TRANSPORT_ERROR;
-            return Pair.create(status, null);
+            if (callingAgent) {
+                throw AgentException.transitory(e);
+            } else {
+                throw TaskException.create();
+            }
+        } finally {
+            mBlankStateFile.delete();
         }
-        blankStateFile.delete();
+        checkAgentResult(packageInfo, agentResult);
+    }
 
-        return Pair.create(BackupTransport.TRANSPORT_OK, agentResult);
+    private void checkAgentResult(PackageInfo packageInfo, RemoteResult result)
+            throws AgentException, TaskException {
+        if (result == RemoteResult.FAILED_THREAD_INTERRUPTED) {
+            // Not an explicit cancel, we need to flag it.
+            mCancelled = true;
+            mReporter.onAgentCancelled(packageInfo);
+            throw TaskException.create();
+        }
+        if (result == RemoteResult.FAILED_CANCELLED) {
+            mReporter.onAgentCancelled(packageInfo);
+            throw TaskException.create();
+        }
+        if (result == RemoteResult.FAILED_TIMED_OUT) {
+            mReporter.onAgentTimedOut(packageInfo);
+            throw AgentException.transitory();
+        }
+        Preconditions.checkState(result.isPresent());
+        long resultCode = result.get();
+        if (resultCode == BackupAgent.RESULT_ERROR) {
+            mReporter.onAgentResultError(packageInfo);
+            throw AgentException.transitory();
+        }
+        Preconditions.checkState(resultCode == BackupAgent.RESULT_SUCCESS);
     }
 
     private void agentFail(IBackupAgent agent, String message) {
@@ -801,94 +816,79 @@
         }
     }
 
-    /** Returns whether to consume next queue package. */
-    private boolean sendDataToTransport() {
+    /** Returns transport status. */
+    private int sendDataToTransport() throws AgentException, TaskException, IOException {
         Preconditions.checkState(mBackupData != null);
+        checkBackupData(mCurrentPackage.applicationInfo, mBackupDataFile);
 
         String packageName = mCurrentPackage.packageName;
-        ApplicationInfo applicationInfo = mCurrentPackage.applicationInfo;
-
-        boolean writingWidgetData = false;
-        try {
-            if (!validateBackupData(applicationInfo, mBackupDataFile)) {
-                cleanUpAgentForAgentError();
-                return true;
-            }
-            writingWidgetData = true;
-            writeWidgetPayloadIfAppropriate(mBackupData.getFileDescriptor(), packageName);
-        } catch (IOException e) {
-            if (writingWidgetData) {
-                mReporter.onWriteWidgetDataError(packageName, e);
-            } else {
-                mReporter.onReadAgentDataError(packageName, e);
-            }
-            cleanUpAgentForAgentError();
-            revertTask();
-            return false;
-        }
+        writeWidgetPayloadIfAppropriate(mBackupData.getFileDescriptor(), packageName);
 
         boolean nonIncremental = mSavedStateFile.length() == 0;
-        long size = mBackupDataFile.length();
-        if (size > 0) {
-            try (ParcelFileDescriptor backupData =
-                         ParcelFileDescriptor.open(mBackupDataFile, MODE_READ_ONLY)) {
-                IBackupTransport transport =
-                        mTransportClient.connectOrThrow("KVBT.sendDataToTransport()");
-                mReporter.onSendDataToTransport(packageName);
-                int flags = getPerformBackupFlags(mUserInitiated, nonIncremental);
-
-                mStatus = transport.performBackup(mCurrentPackage, backupData, flags);
-                if (mStatus == BackupTransport.TRANSPORT_OK) {
-                    mStatus = transport.finishBackup();
-                }
-            } catch (Exception e) {
-                mReporter.onPackageBackupError(packageName, e);
-                mStatus = BackupTransport.TRANSPORT_ERROR;
-            }
-        } else {
-            mReporter.onEmptyData(mCurrentPackage);
-            mStatus = BackupTransport.TRANSPORT_OK;
-        }
-
-        if (nonIncremental
-                && mStatus == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
-            mReporter.onNonIncrementalAndNonIncrementalRequired();
-            mStatus = BackupTransport.TRANSPORT_ERROR;
-        }
-
-
-        boolean processQueue = handleTransportStatus(mStatus, packageName, size);
-        // We might report quota exceeded to the agent in handleTransportStatus() above, so we
-        // only clean-up after it.
-        cleanUpAgentForTransportStatus(mStatus);
-        return processQueue;
+        int status = transportPerformBackup(mCurrentPackage, mBackupDataFile, nonIncremental);
+        handleTransportStatus(status, packageName, mBackupDataFile.length());
+        return status;
     }
 
-    /** Returns whether to consume next queue package. */
-    private boolean handleTransportStatus(int status, String packageName, long size) {
+    private int transportPerformBackup(
+            PackageInfo packageInfo, File backupDataFile, boolean nonIncremental)
+            throws TaskException {
+        String packageName = packageInfo.packageName;
+        long size = backupDataFile.length();
+        if (size <= 0) {
+            mReporter.onEmptyData(packageInfo);
+            return BackupTransport.TRANSPORT_OK;
+        }
+
+        int status;
+        try (ParcelFileDescriptor backupData =
+                ParcelFileDescriptor.open(backupDataFile, MODE_READ_ONLY)) {
+            IBackupTransport transport =
+                    mTransportClient.connectOrThrow("KVBT.transportPerformBackup()");
+            mReporter.onTransportPerformBackup(packageName);
+            int flags = getPerformBackupFlags(mUserInitiated, nonIncremental);
+
+            status = transport.performBackup(packageInfo, backupData, flags);
+            if (status == BackupTransport.TRANSPORT_OK) {
+                status = transport.finishBackup();
+            }
+        } catch (Exception e) {
+            mReporter.onPackageBackupTransportError(packageName, e);
+            throw TaskException.causedBy(e);
+        }
+
+        if (nonIncremental && status == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
+            mReporter.onPackageBackupNonIncrementalAndNonIncrementalRequired(packageName);
+            throw TaskException.create();
+        }
+
+        return status;
+    }
+
+    private void handleTransportStatus(int status, String packageName, long size)
+            throws TaskException, AgentException {
         if (status == BackupTransport.TRANSPORT_OK) {
             mReporter.onPackageBackupComplete(packageName, size);
-            return true;
-        }
-        if (status == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
-            mReporter.onPackageBackupRejected(packageName);
-            return true;
+            return;
         }
         if (status == BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED) {
             mReporter.onPackageBackupNonIncrementalRequired(mCurrentPackage);
             // Immediately retry the current package.
             mQueue.add(0, packageName);
-            return true;
+            return;
+        }
+        if (status == BackupTransport.TRANSPORT_PACKAGE_REJECTED) {
+            mReporter.onPackageBackupRejected(packageName);
+            throw AgentException.permanent();
         }
         if (status == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
             mReporter.onPackageBackupQuotaExceeded(packageName);
             agentDoQuotaExceeded(mAgent, packageName, size);
-            return true;
+            throw AgentException.permanent();
         }
         // Any other error here indicates a transport-level failure.
         mReporter.onPackageBackupTransportFailure(packageName);
-        revertTask();
-        return false;
+        throw TaskException.forStatus(status);
     }
 
     private void agentDoQuotaExceeded(@Nullable IBackupAgent agent, String packageName, long size) {
@@ -908,19 +908,17 @@
     }
 
     /**
-     * For system apps and pseudo-apps always return {@code true}. For regular apps returns whether
-     * {@code backupDataFile} doesn't have any protected keys.
-     *
-     * <p>If the app has attempted to write any protected keys we also crash them.
+     * For system apps and pseudo-apps never throws. For regular apps throws {@link AgentException}
+     * if {@code backupDataFile} has any protected keys, also crashing the app.
      */
-    private boolean validateBackupData(
-            @Nullable ApplicationInfo applicationInfo, File backupDataFile) throws IOException {
+    private void checkBackupData(@Nullable ApplicationInfo applicationInfo, File backupDataFile)
+            throws IOException, AgentException {
         if (applicationInfo == null || (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
             // System apps and pseudo-apps can write what they want.
-            return true;
+            return;
         }
         try (ParcelFileDescriptor backupData =
-                     ParcelFileDescriptor.open(backupDataFile, MODE_READ_ONLY)) {
+                ParcelFileDescriptor.open(backupDataFile, MODE_READ_ONLY)) {
             BackupDataInput backupDataInput = new BackupDataInput(backupData.getFileDescriptor());
             while (backupDataInput.readNextHeader()) {
                 String key = backupDataInput.getKey();
@@ -928,12 +926,11 @@
                     mReporter.onAgentIllegalKey(mCurrentPackage, key);
                     // Crash them if they wrote any protected keys.
                     agentFail(mAgent, "Illegal backup key: " + key);
-                    return false;
+                    throw AgentException.permanent();
                 }
                 backupDataInput.skipEntityData();
             }
         }
-        return true;
     }
 
     private int getPerformBackupFlags(boolean userInitiated, boolean nonIncremental) {
@@ -1009,44 +1006,39 @@
         }
     }
 
-    /** Cleans-up after having called the agent. */
+    /**
+     * Cleans up agent resources opened by {@link #extractAgentData(PackageInfo)} for exceptional
+     * case.
+     *
+     * <p>Note: Declaring exception parameter so that the caller only calls this when an exception
+     * is thrown.
+     */
+    private void cleanUpAgentForError(BackupException exception) {
+        cleanUpAgent(StateTransaction.DISCARD_NEW);
+    }
+
+    /**
+     * Cleans up agent resources opened by {@link #extractAgentData(PackageInfo)} according to
+     * transport status returned in {@link #sendDataToTransport(PackageInfo)}.
+     */
     private void cleanUpAgentForTransportStatus(int status) {
-        updateFiles(status);
-        cleanUpAgent();
-    }
-
-    /** Cleans-up if we failed to call the agent. */
-    private void cleanUpAgentForAgentError() {
-        mBackupDataFile.delete();
-        mNewStateFile.delete();
-        cleanUpAgent();
-    }
-
-    private void updateFiles(int status) {
         switch (status) {
             case BackupTransport.TRANSPORT_OK:
-                mBackupDataFile.delete();
-                mNewStateFile.renameTo(mSavedStateFile);
+                cleanUpAgent(StateTransaction.COMMIT_NEW);
                 break;
             case BackupTransport.TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED:
-                mSavedStateFile.delete();
-                mBackupDataFile.delete();
-                mNewStateFile.delete();
+                cleanUpAgent(StateTransaction.DISCARD_ALL);
                 break;
             default:
-                // Includes:
-                // * BackupTransport.TRANSPORT_PACKAGE_REJECTED
-                // * BackupTransport.TRANSPORT_QUOTA_EXCEEDED
-                // * BackupTransport.TRANSPORT_ERROR
-                mBackupDataFile.delete();
-                mNewStateFile.delete();
-                break;
+                // All other transport statuses are properly converted to agent or task exceptions.
+                throw new AssertionError();
         }
     }
 
-    /** Cleans-up file-descriptors and unbinds agent. */
-    private void cleanUpAgent() {
-        mAgent = null;
+    private void cleanUpAgent(@StateTransaction int stateTransaction) {
+        applyStateTransaction(stateTransaction);
+        mBackupDataFile.delete();
+        mBlankStateFile.delete();
         tryCloseFileDescriptor(mSavedState, "old state");
         tryCloseFileDescriptor(mBackupData, "backup data");
         tryCloseFileDescriptor(mNewState, "new state");
@@ -1058,6 +1050,24 @@
         if (mCurrentPackage.applicationInfo != null) {
             mBackupManagerService.unbindAgent(mCurrentPackage.applicationInfo);
         }
+        mAgent = null;
+    }
+
+    private void applyStateTransaction(@StateTransaction int stateTransaction) {
+        switch (stateTransaction) {
+            case StateTransaction.COMMIT_NEW:
+                mNewStateFile.renameTo(mSavedStateFile);
+                break;
+            case StateTransaction.DISCARD_NEW:
+                mNewStateFile.delete();
+                break;
+            case StateTransaction.DISCARD_ALL:
+                mSavedStateFile.delete();
+                mNewStateFile.delete();
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown state transaction " + stateTransaction);
+        }
     }
 
     private void tryCloseFileDescriptor(@Nullable Closeable closeable, String logName) {
@@ -1079,4 +1089,16 @@
         mPendingCall = null;
         return result;
     }
+
+    @IntDef({
+        StateTransaction.COMMIT_NEW,
+        StateTransaction.DISCARD_NEW,
+        StateTransaction.DISCARD_ALL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface StateTransaction {
+        int COMMIT_NEW = 0;
+        int DISCARD_NEW = 1;
+        int DISCARD_ALL = 2;
+    }
 }
diff --git a/services/backup/java/com/android/server/backup/keyvalue/TaskException.java b/services/backup/java/com/android/server/backup/keyvalue/TaskException.java
new file mode 100644
index 0000000..08d2895
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/keyvalue/TaskException.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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.backup.keyvalue;
+
+import android.app.backup.BackupTransport;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * The key-value backup task has failed, no more packages will be processed and we shouldn't attempt
+ * any more backups now. These can be caused by transport failures (as opposed to agent failures).
+ *
+ * @see KeyValueBackupTask
+ * @see AgentException
+ */
+class TaskException extends BackupException {
+    private static final int DEFAULT_STATUS = BackupTransport.TRANSPORT_ERROR;
+
+    static TaskException stateCompromised() {
+        return new TaskException(/* stateCompromised */ true, DEFAULT_STATUS);
+    }
+
+    static TaskException stateCompromised(Exception cause) {
+        if (cause instanceof TaskException) {
+            TaskException exception = (TaskException) cause;
+            return new TaskException(cause, /* stateCompromised */ true, exception.getStatus());
+        }
+        return new TaskException(cause, /* stateCompromised */ true, DEFAULT_STATUS);
+    }
+
+    static TaskException forStatus(int status) {
+        Preconditions.checkArgument(
+                status != BackupTransport.TRANSPORT_OK, "Exception based on TRANSPORT_OK");
+        return new TaskException(/* stateCompromised */ false, status);
+    }
+
+    static TaskException causedBy(Exception cause) {
+        if (cause instanceof TaskException) {
+            return (TaskException) cause;
+        }
+        return new TaskException(cause, /* stateCompromised */ false, DEFAULT_STATUS);
+    }
+
+    static TaskException create() {
+        return new TaskException(/* stateCompromised */ false, DEFAULT_STATUS);
+    }
+
+    private final boolean mStateCompromised;
+    private final int mStatus;
+
+    private TaskException(Exception cause, boolean stateCompromised, int status) {
+        super(cause);
+        mStateCompromised = stateCompromised;
+        mStatus = status;
+    }
+
+    private TaskException(boolean stateCompromised, int status) {
+        mStateCompromised = stateCompromised;
+        mStatus = status;
+    }
+
+    boolean isStateCompromised() {
+        return mStateCompromised;
+    }
+
+    int getStatus() {
+        return mStatus;
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 510d333..461d39d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -629,7 +629,7 @@
                 return false;
             }
 
-            IIntentSender target = mAm.getIntentSenderLocked(
+            IIntentSender target = mAm.mPendingIntentController.getIntentSender(
                     ActivityManager.INTENT_SENDER_SERVICE, callingPackage,
                     callingUid, userId, null, null, 0, new Intent[]{service},
                     new String[]{service.resolveType(mAm.mContext.getContentResolver())},
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 01421c7..fab967c 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -890,6 +890,8 @@
         mRemoved = true;
 
         releaseSelfIfNeeded();
+
+        mSupervisor.getKeyguardController().onDisplayRemoved(mDisplayId);
     }
 
     private void releaseSelfIfNeeded() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8c7fc84..6e90d5b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -134,7 +134,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SERVICE;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_UID_OBSERVERS;
-import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_URI_PERMISSION;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -417,7 +416,6 @@
     private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
     private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
     private static final String TAG_UID_OBSERVERS = TAG + POSTFIX_UID_OBSERVERS;
-    private static final String TAG_URI_PERMISSION = TAG + POSTFIX_URI_PERMISSION;
 
     // Mock "pretend we're idle now" broadcast action to the job scheduler; declared
     // here so that while the job scheduler can depend on AMS, the other way around
@@ -563,6 +561,7 @@
     String mDeviceOwnerName;
 
     final UserController mUserController;
+    final PendingIntentController mPendingIntentController;
 
     final AppErrors mAppErrors;
 
@@ -821,12 +820,6 @@
     final SparseArray<UidRecord> mValidateUids = new SparseArray<>();
 
     /**
-     * Set of IntentSenderRecord objects that are currently active.
-     */
-    final HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> mIntentSenderRecords
-            = new HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>>();
-
-    /**
      * Fingerprints (hashCode()) of stack traces that we've
      * already logged DropBox entries for.  Guarded by itself.  If
      * something (rogue user app) forces this over
@@ -1426,7 +1419,6 @@
     static final int UPDATE_TIME_ZONE = 13;
     static final int PROC_START_TIMEOUT_MSG = 20;
     static final int KILL_APPLICATION_MSG = 22;
-    static final int FINALIZE_PENDING_INTENT_MSG = 23;
     static final int SHOW_STRICT_MODE_VIOLATION_UI_MSG = 26;
     static final int CHECK_EXCESSIVE_POWER_USE_MSG = 27;
     static final int CLEAR_DNS_CACHE_MSG = 28;
@@ -1445,7 +1437,6 @@
     static final int IDLE_UIDS_MSG = 58;
     static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63;
     static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66;
-    static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67;
     static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
     static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
     static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
@@ -1644,21 +1635,6 @@
                 mServices.serviceForegroundCrash(
                     (ProcessRecord) msg.obj, msg.getData().getCharSequence(SERVICE_RECORD_KEY));
             } break;
-            case DISPATCH_PENDING_INTENT_CANCEL_MSG: {
-                RemoteCallbackList<IResultReceiver> callbacks
-                        = (RemoteCallbackList<IResultReceiver>)msg.obj;
-                int N = callbacks.beginBroadcast();
-                for (int i = 0; i < N; i++) {
-                    try {
-                        callbacks.getBroadcastItem(i).send(Activity.RESULT_CANCELED, null);
-                    } catch (RemoteException e) {
-                    }
-                }
-                callbacks.finishBroadcast();
-                // We have to clean up the RemoteCallbackList here, because otherwise it will
-                // needlessly hold the enclosed callbacks until the remote process dies.
-                callbacks.kill();
-            } break;
             case UPDATE_TIME_ZONE: {
                 synchronized (ActivityManagerService.this) {
                     for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
@@ -1738,9 +1714,6 @@
                             false, userId, reason);
                 }
             } break;
-            case FINALIZE_PENDING_INTENT_MSG: {
-                ((PendingIntentRecord)msg.obj).completeFinalize();
-            } break;
             case CHECK_EXCESSIVE_POWER_USE_MSG: {
                 synchronized (ActivityManagerService.this) {
                     checkExcessivePowerUsageLocked();
@@ -2354,6 +2327,7 @@
         mSystemThread = null;
         mUiHandler = injector.getUiHandler(null);
         mUserController = null;
+        mPendingIntentController = null;
         mProcStartHandlerThread = null;
         mProcStartHandler = null;
         mHiddenApiBlacklist = null;
@@ -2438,6 +2412,9 @@
         mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
         mStackSupervisor = mActivityTaskManager.mStackSupervisor;
 
+        mPendingIntentController = new PendingIntentController(
+                mHandlerThread.getLooper(), mUserController);
+
         mProcessCpuThread = new Thread("CpuTracker") {
             @Override
             public void run() {
@@ -2508,6 +2485,7 @@
         LocalServices.addService(ActivityManagerInternal.class, new LocalService());
         mActivityTaskManager.onActivityManagerInternalAdded();
         mUgmInternal.onActivityManagerInternalAdded();
+        mPendingIntentController.onActivityManagerInternalAdded();
         // Wait for the synchronized block started in mProcessCpuThread,
         // so that any other access to mProcessCpuTracker from main thread
         // will be blocked during mProcessCpuTracker initialization.
@@ -5511,55 +5489,8 @@
         }
 
         if (packageName == null || uninstalling) {
-            // Remove pending intents.  For now we only do this when force
-            // stopping users, because we have some problems when doing this
-            // for packages -- app widgets are not currently cleaned up for
-            // such packages, so they can be left with bad pending intents.
-            if (mIntentSenderRecords.size() > 0) {
-                Iterator<WeakReference<PendingIntentRecord>> it
-                        = mIntentSenderRecords.values().iterator();
-                while (it.hasNext()) {
-                    WeakReference<PendingIntentRecord> wpir = it.next();
-                    if (wpir == null) {
-                        it.remove();
-                        continue;
-                    }
-                    PendingIntentRecord pir = wpir.get();
-                    if (pir == null) {
-                        it.remove();
-                        continue;
-                    }
-                    if (packageName == null) {
-                        // Stopping user, remove all objects for the user.
-                        if (pir.key.userId != userId) {
-                            // Not the same user, skip it.
-                            continue;
-                        }
-                    } else {
-                        if (UserHandle.getAppId(pir.uid) != appId) {
-                            // Different app id, skip it.
-                            continue;
-                        }
-                        if (userId != UserHandle.USER_ALL && pir.key.userId != userId) {
-                            // Different user, skip it.
-                            continue;
-                        }
-                        if (!pir.key.packageName.equals(packageName)) {
-                            // Different package, skip it.
-                            continue;
-                        }
-                    }
-                    if (!doit) {
-                        return true;
-                    }
-                    didSomething = true;
-                    it.remove();
-                    makeIntentSenderCanceledLocked(pir);
-                    if (pir.key.activity != null && pir.key.activity.pendingResults != null) {
-                        pir.key.activity.pendingResults.remove(pir.ref);
-                    }
-                }
-            }
+            didSomething |= mPendingIntentController.removePendingIntentsForPackage(
+                    packageName, userId, appId, doit);
         }
 
         if (doit) {
@@ -6342,90 +6273,19 @@
                     }
                 }
 
-                return getIntentSenderLocked(type, packageName, callingUid, userId,
-                        token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
-
+                if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
+                    return mAtmInternal.getIntentSender(type, packageName, callingUid, userId,
+                            token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
+                }
+                return mPendingIntentController.getIntentSender(type, packageName, callingUid,
+                        userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
+                        bOptions);
             } catch (RemoteException e) {
                 throw new SecurityException(e);
             }
         }
     }
 
-    IIntentSender getIntentSenderLocked(int type, String packageName,
-            int callingUid, int userId, IBinder token, String resultWho,
-            int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
-            Bundle bOptions) {
-        if (DEBUG_MU) Slog.v(TAG_MU, "getIntentSenderLocked(): uid=" + callingUid);
-        ActivityRecord activity = null;
-        if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
-            activity = ActivityRecord.isInStackLocked(token);
-            if (activity == null) {
-                Slog.w(TAG, "Failed createPendingResult: activity " + token + " not in any stack");
-                return null;
-            }
-            if (activity.finishing) {
-                Slog.w(TAG, "Failed createPendingResult: activity " + activity + " is finishing");
-                return null;
-            }
-        }
-
-        // We're going to be splicing together extras before sending, so we're
-        // okay poking into any contained extras.
-        if (intents != null) {
-            for (int i = 0; i < intents.length; i++) {
-                intents[i].setDefusable(true);
-            }
-        }
-        Bundle.setDefusable(bOptions, true);
-
-        final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0;
-        final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0;
-        final boolean updateCurrent = (flags&PendingIntent.FLAG_UPDATE_CURRENT) != 0;
-        flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT
-                |PendingIntent.FLAG_UPDATE_CURRENT);
-
-        PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, activity,
-                resultWho, requestCode, intents, resolvedTypes, flags,
-                SafeActivityOptions.fromBundle(bOptions), userId);
-        WeakReference<PendingIntentRecord> ref;
-        ref = mIntentSenderRecords.get(key);
-        PendingIntentRecord rec = ref != null ? ref.get() : null;
-        if (rec != null) {
-            if (!cancelCurrent) {
-                if (updateCurrent) {
-                    if (rec.key.requestIntent != null) {
-                        rec.key.requestIntent.replaceExtras(intents != null ?
-                                intents[intents.length - 1] : null);
-                    }
-                    if (intents != null) {
-                        intents[intents.length-1] = rec.key.requestIntent;
-                        rec.key.allIntents = intents;
-                        rec.key.allResolvedTypes = resolvedTypes;
-                    } else {
-                        rec.key.allIntents = null;
-                        rec.key.allResolvedTypes = null;
-                    }
-                }
-                return rec;
-            }
-            makeIntentSenderCanceledLocked(rec);
-            mIntentSenderRecords.remove(key);
-        }
-        if (noCreate) {
-            return rec;
-        }
-        rec = new PendingIntentRecord(this, key, callingUid);
-        mIntentSenderRecords.put(key, rec.ref);
-        if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
-            if (activity.pendingResults == null) {
-                activity.pendingResults
-                        = new HashSet<WeakReference<PendingIntentRecord>>();
-            }
-            activity.pendingResults.add(rec.ref);
-        }
-        return rec;
-    }
-
     @Override
     public int sendIntentSender(IIntentSender target, IBinder whitelistToken, int code,
             Intent intent, String resolvedType,
@@ -6465,44 +6325,7 @@
 
     @Override
     public void cancelIntentSender(IIntentSender sender) {
-        if (!(sender instanceof PendingIntentRecord)) {
-            return;
-        }
-        synchronized(this) {
-            PendingIntentRecord rec = (PendingIntentRecord)sender;
-            try {
-                final int uid = AppGlobals.getPackageManager().getPackageUid(rec.key.packageName,
-                        MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getCallingUserId());
-                if (!UserHandle.isSameApp(uid, Binder.getCallingUid())) {
-                    String msg = "Permission Denial: cancelIntentSender() from pid="
-                        + Binder.getCallingPid()
-                        + ", uid=" + Binder.getCallingUid()
-                        + " is not allowed to cancel package "
-                        + rec.key.packageName;
-                    Slog.w(TAG, msg);
-                    throw new SecurityException(msg);
-                }
-            } catch (RemoteException e) {
-                throw new SecurityException(e);
-            }
-            cancelIntentSenderLocked(rec, true);
-        }
-    }
-
-    void cancelIntentSenderLocked(PendingIntentRecord rec, boolean cleanActivity) {
-        makeIntentSenderCanceledLocked(rec);
-        mIntentSenderRecords.remove(rec.key);
-        if (cleanActivity && rec.key.activity != null) {
-            rec.key.activity.pendingResults.remove(rec.ref);
-        }
-    }
-
-    void makeIntentSenderCanceledLocked(PendingIntentRecord rec) {
-        rec.canceled = true;
-        RemoteCallbackList<IResultReceiver> callbacks = rec.detachCancelListenersLocked();
-        if (callbacks != null) {
-            mHandler.obtainMessage(DISPATCH_PENDING_INTENT_CANCEL_MSG, callbacks).sendToTarget();
-        }
+        mPendingIntentController.cancelIntentSender(sender);
     }
 
     @Override
@@ -10866,7 +10689,7 @@
                 pw.println("-------------------------------------------------------------------------------");
 
             }
-            dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+            mPendingIntentController.dumpPendingIntents(pw, dumpAll, dumpPackage);
             pw.println();
             if (dumpAll) {
                 pw.println("-------------------------------------------------------------------------------");
@@ -11159,7 +10982,7 @@
                     opti++;
                 }
                 synchronized (this) {
-                    dumpPendingIntentsLocked(fd, pw, args, opti, true, dumpPackage);
+                    mPendingIntentController.dumpPendingIntents(pw, true, dumpPackage);
                 }
             } else if ("processes".equals(cmd) || "p".equals(cmd)) {
                 if (opti < args.length) {
@@ -12857,61 +12680,6 @@
         mUgmInternal.dump(pw, dumpAll, dumpPackage);
     }
 
-    void dumpPendingIntentsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
-            int opti, boolean dumpAll, String dumpPackage) {
-        boolean printed = false;
-
-        pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)");
-
-        if (mIntentSenderRecords.size() > 0) {
-            // Organize these by package name, so they are easier to read.
-            final ArrayMap<String, ArrayList<PendingIntentRecord>> byPackage = new ArrayMap<>();
-            final ArrayList<WeakReference<PendingIntentRecord>> weakRefs = new ArrayList<>();
-            final Iterator<WeakReference<PendingIntentRecord>> it
-                    = mIntentSenderRecords.values().iterator();
-            while (it.hasNext()) {
-                WeakReference<PendingIntentRecord> ref = it.next();
-                PendingIntentRecord rec = ref != null ? ref.get() : null;
-                if (rec == null) {
-                    weakRefs.add(ref);
-                    continue;
-                }
-                if (dumpPackage != null && !dumpPackage.equals(rec.key.packageName)) {
-                    continue;
-                }
-                ArrayList<PendingIntentRecord> list = byPackage.get(rec.key.packageName);
-                if (list == null) {
-                    list = new ArrayList<>();
-                    byPackage.put(rec.key.packageName, list);
-                }
-                list.add(rec);
-            }
-            for (int i = 0; i < byPackage.size(); i++) {
-                ArrayList<PendingIntentRecord> intents = byPackage.valueAt(i);
-                printed = true;
-                pw.print("  * "); pw.print(byPackage.keyAt(i));
-                pw.print(": "); pw.print(intents.size()); pw.println(" items");
-                for (int j = 0; j < intents.size(); j++) {
-                    pw.print("    #"); pw.print(j); pw.print(": "); pw.println(intents.get(j));
-                    if (dumpAll) {
-                        intents.get(j).dump(pw, "      ");
-                    }
-                }
-            }
-            if (weakRefs.size() > 0) {
-                printed = true;
-                pw.println("  * WEAK REFS:");
-                for (int i = 0; i < weakRefs.size(); i++) {
-                    pw.print("    #"); pw.print(i); pw.print(": "); pw.println(weakRefs.get(i));
-                }
-            }
-        }
-
-        if (!printed) {
-            pw.println("  (nothing)");
-        }
-    }
-
     private static final int dumpProcessList(PrintWriter pw,
             ActivityManagerService service, List list,
             String prefix, String normalLabel, String persistentLabel,
@@ -15186,24 +14954,6 @@
         }
     }
 
-    ComponentName startServiceInPackage(int uid, Intent service, String resolvedType,
-            boolean fgRequired, String callingPackage, int userId)
-            throws TransactionTooLargeException {
-        synchronized(this) {
-            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
-                    "startServiceInPackage: " + service + " type=" + resolvedType);
-            final long origId = Binder.clearCallingIdentity();
-            ComponentName res;
-            try {
-                res = mServices.startServiceLocked(null, service,
-                        resolvedType, -1, uid, fgRequired, callingPackage, userId);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-            return res;
-        }
-    }
-
     @Override
     public int stopService(IApplicationThread caller, Intent service,
             String resolvedType, int userId) {
@@ -20892,7 +20642,8 @@
                                     memoryStat.pgmajfault,
                                     memoryStat.rssInBytes,
                                     memoryStat.cacheInBytes,
-                                    memoryStat.swapInBytes);
+                                    memoryStat.swapInBytes,
+                                    memoryStat.rssHighWatermarkInBytes);
                     processMemoryStates.add(processMemoryState);
                 }
             }
@@ -21091,6 +20842,46 @@
         public void finishBooting() {
             ActivityManagerService.this.finishBooting();
         }
+
+        @Override
+        public void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid,
+                long duration, String tag) {
+            synchronized (ActivityManagerService.this) {
+                ActivityManagerService.this.tempWhitelistForPendingIntentLocked(
+                        callerPid, callerUid, targetUid, duration, tag);
+            }
+        }
+
+        @Override
+        public int broadcastIntentInPackage(String packageName, int uid, Intent intent,
+                String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData,
+                Bundle resultExtras, String requiredPermission, Bundle bOptions, boolean serialized,
+                boolean sticky, int userId) {
+            synchronized (ActivityManagerService.this) {
+                return ActivityManagerService.this.broadcastIntentInPackage(packageName, uid,
+                        intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
+                        requiredPermission, bOptions, serialized, sticky, userId);
+            }
+        }
+
+        @Override
+        public ComponentName startServiceInPackage(int uid, Intent service, String resolvedType,
+                boolean fgRequired, String callingPackage, int userId)
+                throws TransactionTooLargeException {
+            synchronized(ActivityManagerService.this) {
+                if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
+                        "startServiceInPackage: " + service + " type=" + resolvedType);
+                final long origId = Binder.clearCallingIdentity();
+                ComponentName res;
+                try {
+                    res = mServices.startServiceLocked(null, service,
+                            resolvedType, -1, uid, fgRequired, callingPackage, userId);
+                } finally {
+                    Binder.restoreCallingIdentity(origId);
+                }
+                return res;
+            }
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 35a1eb8..63778a4 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -4144,7 +4144,7 @@
             for (WeakReference<PendingIntentRecord> apr : r.pendingResults) {
                 PendingIntentRecord rec = apr.get();
                 if (rec != null) {
-                    mService.mAm.cancelIntentSenderLocked(rec, false);
+                    mService.mPendingIntentController.cancelIntentSender(rec, false);
                 }
             }
             r.pendingResults = null;
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 177e2f5..1fb8f87 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -126,7 +126,7 @@
 
     private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) {
         Bundle activityOptions = deferCrossProfileAppsAnimationIfNecessary();
-        final IIntentSender target = mService.mAm.getIntentSenderLocked(
+        final IIntentSender target = mService.getIntentSenderLocked(
                 INTENT_SENDER_ACTIVITY, mCallingPackage, callingUid, mUserId, null /*token*/,
                 null /*resultCode*/, 0 /*requestCode*/,
                 new Intent[] { mIntent }, new String[] { mResolvedType },
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 7da0519..890aafe 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -783,7 +783,7 @@
         if (aInfo != null) {
             if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                     aInfo.packageName, userId)) {
-                IIntentSender target = mService.mAm.getIntentSenderLocked(
+                IIntentSender target = mService.getIntentSenderLocked(
                         ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
                         callingUid, userId, null, null, 0, new Intent[]{intent},
                         new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
@@ -1096,7 +1096,7 @@
                             }
                         }
 
-                        IIntentSender target = mService.mAm.getIntentSenderLocked(
+                        IIntentSender target = mService.getIntentSenderLocked(
                                 ActivityManager.INTENT_SENDER_ACTIVITY, "android",
                                 appCallingUid, userId, null, null, 0, new Intent[] { intent },
                                 new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 4dc2851..add9f2a 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -239,7 +239,9 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 
@@ -276,6 +278,7 @@
     UriGrantsManagerInternal mUgmInternal;
     private PackageManagerInternal mPmInternal;
     private ActivityTaskManagerInternal mInternal;
+    PendingIntentController mPendingIntentController;
     /* Global service lock used by the package the owns this service. */
     Object mGlobalLock;
     ActivityStackSupervisor mStackSupervisor;
@@ -628,6 +631,7 @@
         final File systemDir = SystemServiceManager.ensureSystemDir();
         mAppWarnings = new AppWarnings(this, mUiContext, mH, mUiHandler, systemDir);
         mCompatModePackages = new CompatModePackages(this, systemDir, mH);
+        mPendingIntentController = mAm.mPendingIntentController;
 
         mTempConfig.setToDefaults();
         mTempConfig.setLocales(LocaleList.getDefault());
@@ -5019,6 +5023,39 @@
 
     }
 
+    IIntentSender getIntentSenderLocked(int type, String packageName, int callingUid, int userId,
+            IBinder token, String resultWho, int requestCode, Intent[] intents,
+            String[] resolvedTypes, int flags, Bundle bOptions) {
+
+        ActivityRecord activity = null;
+        if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
+            activity = ActivityRecord.isInStackLocked(token);
+            if (activity == null) {
+                Slog.w(TAG, "Failed createPendingResult: activity " + token + " not in any stack");
+                return null;
+            }
+            if (activity.finishing) {
+                Slog.w(TAG, "Failed createPendingResult: activity " + activity + " is finishing");
+                return null;
+            }
+        }
+
+        final PendingIntentRecord rec = mPendingIntentController.getIntentSender(type, packageName,
+                callingUid, userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
+                bOptions);
+        final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
+        if (noCreate) {
+            return rec;
+        }
+        if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
+            if (activity.pendingResults == null) {
+                activity.pendingResults = new HashSet<>();
+            }
+            activity.pendingResults.add(rec.ref);
+        }
+        return rec;
+    }
+
     // TODO(b/111541062): Update app time tracking to make it aware of multiple resumed activities
     private void startTimeTrackingFocusedActivityLocked() {
         final ActivityRecord resumedActivity = mStackSupervisor.getTopResumedActivity();
@@ -5310,6 +5347,31 @@
         }
 
         @Override
+        public int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
+                String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
+                boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent) {
+            synchronized (mGlobalLock) {
+                return getActivityStartController().startActivitiesInPackage(uid, callingPackage,
+                        intents, resolvedTypes, resultTo, options, userId, validateIncomingUser,
+                        originatingPendingIntent);
+            }
+        }
+
+        @Override
+        public int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
+                String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
+                String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
+                int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
+                PendingIntentRecord originatingPendingIntent) {
+            synchronized (mGlobalLock) {
+                return getActivityStartController().startActivityInPackage(uid, realCallingPid,
+                        realCallingUid, callingPackage, intent, resolvedType, resultTo, resultWho,
+                        requestCode, startFlags, options, userId, inTask, reason,
+                        validateIncomingUser, originatingPendingIntent);
+            }
+        }
+
+        @Override
         public int startActivityAsUser(IApplicationThread caller, String callerPacakge,
                 Intent intent, Bundle options, int userId) {
             return ActivityTaskManagerService.this.startActivityAsUser(
@@ -5684,5 +5746,39 @@
                 }
             });
         }
+
+        @Override
+        public void sendActivityResult(int callingUid, IBinder activityToken, String resultWho,
+                int requestCode, int resultCode, Intent data) {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
+                if (r != null && r.getStack() != null) {
+                    r.getStack().sendActivityResultLocked(callingUid, r, resultWho, requestCode,
+                            resultCode, data);
+                }
+            }
+        }
+
+        @Override
+        public void clearPendingResultForActivity(IBinder activityToken,
+                WeakReference<PendingIntentRecord> pir) {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
+                if (r != null && r.pendingResults != null) {
+                    r.pendingResults.remove(pir);
+                }
+            }
+        }
+
+        @Override
+        public IIntentSender getIntentSender(int type, String packageName,
+                int callingUid, int userId, IBinder token, String resultWho,
+                int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
+                Bundle bOptions) {
+            synchronized (mGlobalLock) {
+                return getIntentSenderLocked(type, packageName, callingUid, userId, token,
+                        resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 16c3235..e2035f6 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -741,7 +741,7 @@
 
         // Show a permission review UI only for explicit broadcast from a foreground app
         if (callerForeground && receiverRecord.intent.getComponent() != null) {
-            IIntentSender target = mService.getIntentSenderLocked(
+            IIntentSender target = mService.mPendingIntentController.getIntentSender(
                     ActivityManager.INTENT_SENDER_BROADCAST, receiverRecord.callerPackage,
                     receiverRecord.callingUid, receiverRecord.userId, null, null, 0,
                     new Intent[]{receiverRecord.intent},
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index ee4e36f..cfe2829 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -29,16 +29,20 @@
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
+
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED;
+import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES;
 import static com.android.server.am.KeyguardControllerProto.KEYGUARD_SHOWING;
+import static com.android.server.am.KeyguardOccludedProto.DISPLAY_ID;
+import static com.android.server.am.KeyguardOccludedProto.KEYGUARD_OCCLUDED;
 
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.Trace;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.policy.IKeyguardDismissCallback;
@@ -58,19 +62,18 @@
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "KeyguardController" : TAG_AM;
 
-    private final ActivityTaskManagerService mService;
     private final ActivityStackSupervisor mStackSupervisor;
     private WindowManagerService mWindowManager;
     private boolean mKeyguardShowing;
     private boolean mAodShowing;
     private boolean mKeyguardGoingAway;
-    private boolean mOccluded;
     private boolean mDismissalRequested;
-    private ActivityRecord mDismissingKeyguardActivity;
     private int mBeforeUnoccludeTransit;
     private int mVisibilityTransactionDepth;
-    private SleepToken mSleepToken;
+    // TODO(b/111955725): Support multiple external displays
     private int mSecondaryDisplayShowing = INVALID_DISPLAY;
+    private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
+    private final ActivityTaskManagerService mService;
 
     KeyguardController(ActivityTaskManagerService service,
             ActivityStackSupervisor stackSupervisor) {
@@ -87,8 +90,8 @@
      *         on the given display, false otherwise
      */
     boolean isKeyguardOrAodShowing(int displayId) {
-        return (mKeyguardShowing || mAodShowing) && !mKeyguardGoingAway &&
-                (displayId == DEFAULT_DISPLAY ? !mOccluded : displayId == mSecondaryDisplayShowing);
+        return (mKeyguardShowing || mAodShowing) && !mKeyguardGoingAway
+                && !isDisplayOccluded(displayId);
     }
 
     /**
@@ -96,8 +99,7 @@
      *         display, false otherwise
      */
     boolean isKeyguardShowing(int displayId) {
-        return mKeyguardShowing && !mKeyguardGoingAway &&
-                (displayId == DEFAULT_DISPLAY ? !mOccluded : displayId == mSecondaryDisplayShowing);
+        return mKeyguardShowing && !mKeyguardGoingAway && !isDisplayOccluded(displayId);
     }
 
     /**
@@ -133,6 +135,7 @@
         if (showingChanged) {
             dismissDockedStackIfNeeded();
             setKeyguardGoingAway(false);
+            // TODO(b/113840485): Check usage for non-default display
             mWindowManager.setKeyguardOrAodShowingOnDefaultDisplay(
                     isKeyguardOrAodShowing(DEFAULT_DISPLAY));
             if (keyguardShowing) {
@@ -248,7 +251,8 @@
         // already the dismissing activity, in which case we don't allow it to repeatedly dismiss
         // Keyguard.
         return dismissKeyguard && canDismissKeyguard() && !mAodShowing
-                && (mDismissalRequested || r != mDismissingKeyguardActivity);
+                && (mDismissalRequested
+                || getDisplay(r.getDisplayId()).mDismissingKeyguardActivity != r);
     }
 
     /**
@@ -259,44 +263,16 @@
     }
 
     private void visibilitiesUpdated() {
-        final boolean lastOccluded = mOccluded;
-        final ActivityRecord lastDismissingKeyguardActivity = mDismissingKeyguardActivity;
-        mOccluded = false;
-        mDismissingKeyguardActivity = null;
-
+        boolean requestDismissKeyguard = false;
         for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
             final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
-
-                // Only the top activity of the focused stack on the default display may control
-                // occluded state.
-                if (display.mDisplayId == DEFAULT_DISPLAY
-                        && mStackSupervisor.isTopDisplayFocusedStack(stack)) {
-
-                    // A dismissing activity occludes Keyguard in the insecure case for legacy
-                    // reasons.
-                    final ActivityRecord topDismissing = stack.getTopDismissingKeyguardActivity();
-                    mOccluded =
-                            stack.topActivityOccludesKeyguard()
-                                    || (topDismissing != null
-                                            && stack.topRunningActivityLocked() == topDismissing
-                                            && canShowWhileOccluded(
-                                                    true /* dismissKeyguard */,
-                                                    false /* showWhenLocked */));
-                }
-
-                if (mDismissingKeyguardActivity == null
-                        && stack.getTopDismissingKeyguardActivity() != null) {
-                    mDismissingKeyguardActivity = stack.getTopDismissingKeyguardActivity();
-                }
-            }
+            final KeyguardDisplayState state = getDisplay(display.mDisplayId);
+            state.visibilitiesUpdated(this, display);
+            requestDismissKeyguard |= state.mRequestDismissKeyguard;
         }
-        mOccluded |= mWindowManager.isShowingDream();
-        if (mOccluded != lastOccluded) {
-            handleOccludedChanged();
-        }
-        if (mDismissingKeyguardActivity != lastDismissingKeyguardActivity) {
+
+        // Dismissing Keyguard happens globally using the information from all displays.
+        if (requestDismissKeyguard) {
             handleDismissKeyguard();
         }
     }
@@ -305,7 +281,7 @@
      * Called when occluded state changed.
      */
     private void handleOccludedChanged() {
-        mWindowManager.onKeyguardOccludedChanged(mOccluded);
+        mWindowManager.onKeyguardOccludedChanged(isDisplayOccluded(DEFAULT_DISPLAY));
         if (isKeyguardLocked()) {
             mWindowManager.deferSurfaceLayout();
             try {
@@ -322,14 +298,13 @@
     }
 
     /**
-     * Called when somebody might want to dismiss the Keyguard.
+     * Called when somebody wants to dismiss the Keyguard via the flag.
      */
     private void handleDismissKeyguard() {
         // We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy
         // reasons, because that's how apps used to dismiss Keyguard in the secure case. In the
         // insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded.
-        if (!mOccluded && mDismissingKeyguardActivity != null
-                && mWindowManager.isKeyguardSecure()) {
+        if (mWindowManager.isKeyguardSecure()) {
             mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
             mDismissalRequested = true;
 
@@ -345,6 +320,10 @@
         }
     }
 
+    private boolean isDisplayOccluded(int displayId) {
+        return getDisplay(displayId).mOccluded;
+    }
+
     /**
      * @return true if Keyguard can be currently dismissed without entering credentials.
      */
@@ -355,12 +334,14 @@
     private int resolveOccludeTransit() {
         if (mBeforeUnoccludeTransit != TRANSIT_UNSET
                 && mWindowManager.getPendingAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
-                && mOccluded) {
+                // TODO(b/113840485): Handle app transition for individual display.
+                && isDisplayOccluded(DEFAULT_DISPLAY)) {
 
             // Reuse old transit in case we are occluding Keyguard again, meaning that we never
             // actually occclude/unocclude Keyguard, but just run a normal transition.
             return mBeforeUnoccludeTransit;
-        } else if (!mOccluded) {
+            // TODO(b/113840485): Handle app transition for individual display.
+        } else if (!isDisplayOccluded(DEFAULT_DISPLAY)) {
 
             // Save transit in case we dismiss/occlude Keyguard shortly after.
             mBeforeUnoccludeTransit = mWindowManager.getPendingAppTransition();
@@ -371,7 +352,8 @@
     }
 
     private void dismissDockedStackIfNeeded() {
-        if (mKeyguardShowing && mOccluded) {
+        // TODO(b/113840485): Handle docked stack for individual display.
+        if (mKeyguardShowing && isDisplayOccluded(DEFAULT_DISPLAY)) {
             // The lock screen is currently showing, but is occluded by a window that can
             // show on top of the lock screen. In this can we want to dismiss the docked
             // stack since it will be complicated/risky to try to put the activity on top
@@ -386,11 +368,116 @@
     }
 
     private void updateKeyguardSleepToken() {
-        if (mSleepToken == null && isKeyguardOrAodShowing(DEFAULT_DISPLAY)) {
-            mSleepToken = mService.acquireSleepToken("Keyguard", DEFAULT_DISPLAY);
-        } else if (mSleepToken != null && !isKeyguardOrAodShowing(DEFAULT_DISPLAY)) {
-            mSleepToken.release();
-            mSleepToken = null;
+        for (int displayNdx = mStackSupervisor.getChildCount() - 1; displayNdx >= 0; displayNdx--) {
+            final ActivityDisplay display = mStackSupervisor.getChildAt(displayNdx);
+            final KeyguardDisplayState state = getDisplay(display.mDisplayId);
+            if (isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken == null) {
+                state.acquiredSleepToken();
+            } else if (!isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken != null) {
+                state.releaseSleepToken();
+            }
+        }
+    }
+
+    private KeyguardDisplayState getDisplay(int displayId) {
+        if (mDisplayStates.get(displayId) == null) {
+            mDisplayStates.append(displayId,
+                    new KeyguardDisplayState(mService, displayId));
+        }
+        return mDisplayStates.get(displayId);
+    }
+
+    void onDisplayRemoved(int displayId) {
+        if (mDisplayStates.get(displayId) != null) {
+            mDisplayStates.get(displayId).onRemoved();
+            mDisplayStates.remove(displayId);
+        }
+    }
+
+    /** Represents Keyguard state per individual display. */
+    private static class KeyguardDisplayState {
+        private final int mDisplayId;
+        private boolean mOccluded;
+        private ActivityRecord mDismissingKeyguardActivity;
+        private boolean mRequestDismissKeyguard;
+        private final ActivityTaskManagerService mService;
+        private SleepToken mSleepToken;
+
+        KeyguardDisplayState(ActivityTaskManagerService service, int displayId) {
+            mService = service;
+            mDisplayId = displayId;
+        }
+
+        void onRemoved() {
+            mDismissingKeyguardActivity = null;
+            releaseSleepToken();
+        }
+
+        void acquiredSleepToken() {
+            if (mSleepToken == null) {
+                mSleepToken = mService.acquireSleepToken("keyguard", mDisplayId);
+            }
+        }
+
+        void releaseSleepToken() {
+            if (mSleepToken != null) {
+                mSleepToken.release();
+                mSleepToken = null;
+            }
+        }
+
+        void visibilitiesUpdated(KeyguardController controller, ActivityDisplay display) {
+            final boolean lastOccluded = mOccluded;
+            final ActivityRecord lastDismissActivity = mDismissingKeyguardActivity;
+            mRequestDismissKeyguard = false;
+            mOccluded = false;
+            mDismissingKeyguardActivity = null;
+
+            // Only the top activity of the focused stack on each display may control it's
+            // occluded state.
+            final ActivityStack focusedStack = display.getFocusedStack();
+            if (focusedStack != null) {
+                final ActivityRecord topDismissing =
+                        focusedStack.getTopDismissingKeyguardActivity();
+                mOccluded = focusedStack.topActivityOccludesKeyguard() || (topDismissing != null
+                                && focusedStack.topRunningActivityLocked() == topDismissing
+                                && controller.canShowWhileOccluded(
+                                true /* dismissKeyguard */,
+                                false /* showWhenLocked */));
+                if (focusedStack.getTopDismissingKeyguardActivity() != null) {
+                    mDismissingKeyguardActivity = focusedStack.getTopDismissingKeyguardActivity();
+                }
+                mOccluded |= controller.mWindowManager.isShowingDream();
+            }
+
+            // TODO(b/113840485): Handle app transition for individual display.
+            // For now, only default display can change occluded.
+            if (lastOccluded != mOccluded && mDisplayId == DEFAULT_DISPLAY) {
+                controller.handleOccludedChanged();
+            }
+            if (lastDismissActivity != mDismissingKeyguardActivity && !mOccluded
+                    && mDismissingKeyguardActivity != null
+                    && controller.mWindowManager.isKeyguardSecure()) {
+                mRequestDismissKeyguard = true;
+            }
+        }
+
+        void dumpStatus(PrintWriter pw, String prefix) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append(prefix);
+            sb.append("  Occluded=").append(mOccluded)
+                    .append(" DismissingKeyguardActivity=")
+                    .append(mDismissingKeyguardActivity)
+                    .append(" at display=")
+                    .append(mDisplayId);
+            pw.println(sb.toString());
+        }
+
+        void writeToProto(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+            proto.write(DISPLAY_ID, mDisplayId);
+            proto.write(KEYGUARD_OCCLUDED, mOccluded);
+            proto.end(token);
         }
     }
 
@@ -399,8 +486,7 @@
         pw.println(prefix + "  mKeyguardShowing=" + mKeyguardShowing);
         pw.println(prefix + "  mAodShowing=" + mAodShowing);
         pw.println(prefix + "  mKeyguardGoingAway=" + mKeyguardGoingAway);
-        pw.println(prefix + "  mOccluded=" + mOccluded);
-        pw.println(prefix + "  mDismissingKeyguardActivity=" + mDismissingKeyguardActivity);
+        dumpDisplayStates(pw, prefix);
         pw.println(prefix + "  mDismissalRequested=" + mDismissalRequested);
         pw.println(prefix + "  mVisibilityTransactionDepth=" + mVisibilityTransactionDepth);
     }
@@ -408,7 +494,19 @@
     void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         proto.write(KEYGUARD_SHOWING, mKeyguardShowing);
-        proto.write(KEYGUARD_OCCLUDED, mOccluded);
+        writeDisplayStatesToProto(proto, KEYGUARD_OCCLUDED_STATES);
         proto.end(token);
     }
+
+    private void dumpDisplayStates(PrintWriter pw, String prefix) {
+        for (int i = 0; i < mDisplayStates.size(); i++) {
+            mDisplayStates.valueAt(i).dumpStatus(pw, prefix);
+        }
+    }
+
+    private void writeDisplayStatesToProto(ProtoOutputStream proto, long fieldId) {
+        for (int i = 0; i < mDisplayStates.size(); i++) {
+            mDisplayStates.valueAt(i).writeToProto(proto, fieldId);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java
index aad890b..228c71d 100644
--- a/services/core/java/com/android/server/am/MemoryStatUtil.java
+++ b/services/core/java/com/android/server/am/MemoryStatUtil.java
@@ -37,18 +37,25 @@
  * Static utility methods related to {@link MemoryStat}.
  */
 final class MemoryStatUtil {
+    static final int BYTES_IN_KILOBYTE = 1024;
+
     private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM;
 
     /** True if device has per-app memcg */
-    private static final Boolean DEVICE_HAS_PER_APP_MEMCG =
+    private static final boolean DEVICE_HAS_PER_APP_MEMCG =
             SystemProperties.getBoolean("ro.config.per_app_memcg", false);
 
     /** Path to check if device has memcg */
     private static final String MEMCG_TEST_PATH = "/dev/memcg/apps/memory.stat";
     /** Path to memory stat file for logging app start memory state */
     private static final String MEMORY_STAT_FILE_FMT = "/dev/memcg/apps/uid_%d/pid_%d/memory.stat";
+    /** Path to memory max usage file for logging app memory state */
+    private static final String MEMORY_MAX_USAGE_FILE_FMT =
+            "/dev/memcg/apps/uid_%d/pid_%d/memory.max_usage_in_bytes";
     /** Path to procfs stat file for logging app start memory state */
     private static final String PROC_STAT_FILE_FMT = "/proc/%d/stat";
+    /** Path to procfs status file for logging app memory state */
+    private static final String PROC_STATUS_FILE_FMT = "/proc/%d/status";
 
     private static final Pattern PGFAULT = Pattern.compile("total_pgfault (\\d+)");
     private static final Pattern PGMAJFAULT = Pattern.compile("total_pgmajfault (\\d+)");
@@ -56,6 +63,9 @@
     private static final Pattern CACHE_IN_BYTES = Pattern.compile("total_cache (\\d+)");
     private static final Pattern SWAP_IN_BYTES = Pattern.compile("total_swap (\\d+)");
 
+    private static final Pattern RSS_HIGH_WATERMARK_IN_BYTES =
+            Pattern.compile("VmHWM:\\s*(\\d+)\\s*kB");
+
     private static final int PGFAULT_INDEX = 9;
     private static final int PGMAJFAULT_INDEX = 11;
     private static final int RSS_IN_BYTES_INDEX = 23;
@@ -80,8 +90,15 @@
      */
     @Nullable
     static MemoryStat readMemoryStatFromMemcg(int uid, int pid) {
-        final String path = String.format(Locale.US, MEMORY_STAT_FILE_FMT, uid, pid);
-        return parseMemoryStatFromMemcg(readFileContents(path));
+        final String statPath = String.format(Locale.US, MEMORY_STAT_FILE_FMT, uid, pid);
+        MemoryStat stat = parseMemoryStatFromMemcg(readFileContents(statPath));
+        if (stat == null) {
+            return null;
+        }
+        String maxUsagePath = String.format(Locale.US, MEMORY_MAX_USAGE_FILE_FMT, uid, pid);
+        stat.rssHighWatermarkInBytes = parseMemoryMaxUsageFromMemCg(
+                readFileContents(maxUsagePath));
+        return stat;
     }
 
     /**
@@ -91,8 +108,14 @@
      */
     @Nullable
     static MemoryStat readMemoryStatFromProcfs(int pid) {
-        final String path = String.format(Locale.US, PROC_STAT_FILE_FMT, pid);
-        return parseMemoryStatFromProcfs(readFileContents(path));
+        final String statPath = String.format(Locale.US, PROC_STAT_FILE_FMT, pid);
+        MemoryStat stat = parseMemoryStatFromProcfs(readFileContents(statPath));
+        if (stat == null) {
+            return null;
+        }
+        final String statusPath = String.format(Locale.US, PROC_STATUS_FILE_FMT, pid);
+        stat.rssHighWatermarkInBytes = parseVmHWMFromProcfs(readFileContents(statusPath));
+        return stat;
     }
 
     private static String readFileContents(String path) {
@@ -113,7 +136,7 @@
     /**
      * Parses relevant statistics out from the contents of a memory.stat file in memcg.
      */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    @VisibleForTesting
     @Nullable
     static MemoryStat parseMemoryStatFromMemcg(String memoryStatContents) {
         if (memoryStatContents == null || memoryStatContents.isEmpty()) {
@@ -135,10 +158,18 @@
         return memoryStat;
     }
 
+    @VisibleForTesting
+    static long parseMemoryMaxUsageFromMemCg(String memoryMaxUsageContents) {
+        if (memoryMaxUsageContents == null || memoryMaxUsageContents.isEmpty()) {
+            return 0;
+        }
+        return Long.valueOf(memoryMaxUsageContents);
+    }
+
     /**
-     * Parses relevant statistics out from the contents of a /proc/pid/stat file in procfs.
+     * Parses relevant statistics out from the contents of the /proc/pid/stat file in procfs.
      */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    @VisibleForTesting
     @Nullable
     static MemoryStat parseMemoryStatFromProcfs(String procStatContents) {
         if (procStatContents == null || procStatContents.isEmpty()) {
@@ -158,6 +189,20 @@
     }
 
     /**
+     * Parses RSS high watermark out from the contents of the /proc/pid/status file in procfs. The
+     * returned value is in bytes.
+     */
+    @VisibleForTesting
+    static long parseVmHWMFromProcfs(String procStatusContents) {
+        if (procStatusContents == null || procStatusContents.isEmpty()) {
+            return 0;
+        }
+        Matcher m = RSS_HIGH_WATERMARK_IN_BYTES.matcher(procStatusContents);
+        // Convert value read from /proc/pid/status from kilobytes to bytes.
+        return m.find() ? Long.valueOf(m.group(1)) * BYTES_IN_KILOBYTE : 0;
+    }
+
+    /**
      * Returns whether per-app memcg is available on device.
      */
     static boolean hasMemcg() {
@@ -175,5 +220,7 @@
         long cacheInBytes;
         /** Number of bytes of swap usage */
         long swapInBytes;
+        /** Number of bytes of peak anonymous and swap cache memory */
+        long rssHighWatermarkInBytes;
     }
 }
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
new file mode 100644
index 0000000..a9c00a7
--- /dev/null
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2018 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.am;
+
+import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.app.Activity;
+import android.app.ActivityManagerInternal;
+import android.app.AppGlobals;
+import android.app.PendingIntent;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Slog;
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.LocalServices;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * Helper class for {@link ActivityManagerService} responsible for managing pending intents.
+ *
+ * <p>This class uses {@link #mLock} to synchronize access to internal state and doesn't make use of
+ * {@link ActivityManagerService} lock since there can be direct calls into this class from outside
+ * AM. This helps avoid deadlocks.
+ */
+public class PendingIntentController {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "PendingIntentController" : TAG_AM;
+    private static final String TAG_MU = TAG + POSTFIX_MU;
+
+    /** Lock for internal state. */
+    final Object mLock = new Object();
+    final Handler mH;
+    ActivityManagerInternal mAmInternal;
+    final UserController mUserController;
+    final ActivityTaskManagerInternal mAtmInternal;
+
+    /** Set of IntentSenderRecord objects that are currently active. */
+    final HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>> mIntentSenderRecords
+            = new HashMap<>();
+
+    PendingIntentController(Looper looper, UserController userController) {
+        mH = new Handler(looper);
+        mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+        mUserController = userController;
+    }
+
+    void onActivityManagerInternalAdded() {
+        synchronized (mLock) {
+            mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+        }
+    }
+
+    PendingIntentRecord getIntentSender(int type, String packageName, int callingUid, int userId,
+            IBinder token, String resultWho, int requestCode, Intent[] intents,
+            String[] resolvedTypes, int flags, Bundle bOptions) {
+        synchronized (mLock) {
+            if (DEBUG_MU) Slog.v(TAG_MU, "getIntentSender(): uid=" + callingUid);
+
+            // We're going to be splicing together extras before sending, so we're
+            // okay poking into any contained extras.
+            if (intents != null) {
+                for (int i = 0; i < intents.length; i++) {
+                    intents[i].setDefusable(true);
+                }
+            }
+            Bundle.setDefusable(bOptions, true);
+
+            final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
+            final boolean cancelCurrent = (flags & PendingIntent.FLAG_CANCEL_CURRENT) != 0;
+            final boolean updateCurrent = (flags & PendingIntent.FLAG_UPDATE_CURRENT) != 0;
+            flags &= ~(PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_CANCEL_CURRENT
+                    | PendingIntent.FLAG_UPDATE_CURRENT);
+
+            PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, token,
+                    resultWho, requestCode, intents, resolvedTypes, flags,
+                    SafeActivityOptions.fromBundle(bOptions), userId);
+            WeakReference<PendingIntentRecord> ref;
+            ref = mIntentSenderRecords.get(key);
+            PendingIntentRecord rec = ref != null ? ref.get() : null;
+            if (rec != null) {
+                if (!cancelCurrent) {
+                    if (updateCurrent) {
+                        if (rec.key.requestIntent != null) {
+                            rec.key.requestIntent.replaceExtras(intents != null ?
+                                    intents[intents.length - 1] : null);
+                        }
+                        if (intents != null) {
+                            intents[intents.length - 1] = rec.key.requestIntent;
+                            rec.key.allIntents = intents;
+                            rec.key.allResolvedTypes = resolvedTypes;
+                        } else {
+                            rec.key.allIntents = null;
+                            rec.key.allResolvedTypes = null;
+                        }
+                    }
+                    return rec;
+                }
+                makeIntentSenderCanceled(rec);
+                mIntentSenderRecords.remove(key);
+            }
+            if (noCreate) {
+                return rec;
+            }
+            rec = new PendingIntentRecord(this, key, callingUid);
+            mIntentSenderRecords.put(key, rec.ref);
+            return rec;
+        }
+    }
+
+    boolean removePendingIntentsForPackage(String packageName, int userId, int appId,
+            boolean doIt) {
+
+        boolean didSomething = false;
+        synchronized (mLock) {
+
+            // Remove pending intents.  For now we only do this when force stopping users, because
+            // we have some problems when doing this for packages -- app widgets are not currently
+            // cleaned up for such packages, so they can be left with bad pending intents.
+            if (mIntentSenderRecords.size() <= 0) {
+                return false;
+            }
+
+            Iterator<WeakReference<PendingIntentRecord>> it
+                    = mIntentSenderRecords.values().iterator();
+            while (it.hasNext()) {
+                WeakReference<PendingIntentRecord> wpir = it.next();
+                if (wpir == null) {
+                    it.remove();
+                    continue;
+                }
+                PendingIntentRecord pir = wpir.get();
+                if (pir == null) {
+                    it.remove();
+                    continue;
+                }
+                if (packageName == null) {
+                    // Stopping user, remove all objects for the user.
+                    if (pir.key.userId != userId) {
+                        // Not the same user, skip it.
+                        continue;
+                    }
+                } else {
+                    if (UserHandle.getAppId(pir.uid) != appId) {
+                        // Different app id, skip it.
+                        continue;
+                    }
+                    if (userId != UserHandle.USER_ALL && pir.key.userId != userId) {
+                        // Different user, skip it.
+                        continue;
+                    }
+                    if (!pir.key.packageName.equals(packageName)) {
+                        // Different package, skip it.
+                        continue;
+                    }
+                }
+                if (!doIt) {
+                    return true;
+                }
+                didSomething = true;
+                it.remove();
+                makeIntentSenderCanceled(pir);
+                if (pir.key.activity != null) {
+                    final Message m = PooledLambda.obtainMessage(
+                            PendingIntentController::clearPendingResultForActivity, this,
+                            pir.key.activity, pir.ref);
+                    mH.sendMessage(m);
+                }
+            }
+        }
+
+        return didSomething;
+    }
+
+    public void cancelIntentSender(IIntentSender sender) {
+        if (!(sender instanceof PendingIntentRecord)) {
+            return;
+        }
+        synchronized (mLock) {
+            final PendingIntentRecord rec = (PendingIntentRecord) sender;
+            try {
+                final int uid = AppGlobals.getPackageManager().getPackageUid(rec.key.packageName,
+                        MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getCallingUserId());
+                if (!UserHandle.isSameApp(uid, Binder.getCallingUid())) {
+                    String msg = "Permission Denial: cancelIntentSender() from pid="
+                            + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+                            + " is not allowed to cancel package " + rec.key.packageName;
+                    Slog.w(TAG, msg);
+                    throw new SecurityException(msg);
+                }
+            } catch (RemoteException e) {
+                throw new SecurityException(e);
+            }
+            cancelIntentSender(rec, true);
+        }
+    }
+
+    public void cancelIntentSender(PendingIntentRecord rec, boolean cleanActivity) {
+        synchronized (mLock) {
+            makeIntentSenderCanceled(rec);
+            mIntentSenderRecords.remove(rec.key);
+            if (cleanActivity && rec.key.activity != null) {
+                final Message m = PooledLambda.obtainMessage(
+                        PendingIntentController::clearPendingResultForActivity, this,
+                        rec.key.activity, rec.ref);
+                mH.sendMessage(m);
+            }
+        }
+    }
+
+    private void makeIntentSenderCanceled(PendingIntentRecord rec) {
+        rec.canceled = true;
+        final RemoteCallbackList<IResultReceiver> callbacks = rec.detachCancelListenersLocked();
+        if (callbacks != null) {
+            final Message m = PooledLambda.obtainMessage(
+                    PendingIntentController::handlePendingIntentCancelled, this, callbacks);
+            mH.sendMessage(m);
+        }
+    }
+
+    private void handlePendingIntentCancelled(RemoteCallbackList<IResultReceiver> callbacks) {
+        int N = callbacks.beginBroadcast();
+        for (int i = 0; i < N; i++) {
+            try {
+                callbacks.getBroadcastItem(i).send(Activity.RESULT_CANCELED, null);
+            } catch (RemoteException e) {
+                // Process is not longer running...whatever.
+            }
+        }
+        callbacks.finishBroadcast();
+        // We have to clean up the RemoteCallbackList here, because otherwise it will
+        // needlessly hold the enclosed callbacks until the remote process dies.
+        callbacks.kill();
+    }
+
+    private void clearPendingResultForActivity(IBinder activityToken,
+            WeakReference<PendingIntentRecord> pir) {
+        mAtmInternal.clearPendingResultForActivity(activityToken, pir);
+    }
+
+    void dumpPendingIntents(PrintWriter pw, boolean dumpAll, String dumpPackage) {
+        synchronized (mLock) {
+            boolean printed = false;
+
+            pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)");
+
+            if (mIntentSenderRecords.size() > 0) {
+                // Organize these by package name, so they are easier to read.
+                final ArrayMap<String, ArrayList<PendingIntentRecord>> byPackage = new ArrayMap<>();
+                final ArrayList<WeakReference<PendingIntentRecord>> weakRefs = new ArrayList<>();
+                final Iterator<WeakReference<PendingIntentRecord>> it
+                        = mIntentSenderRecords.values().iterator();
+                while (it.hasNext()) {
+                    WeakReference<PendingIntentRecord> ref = it.next();
+                    PendingIntentRecord rec = ref != null ? ref.get() : null;
+                    if (rec == null) {
+                        weakRefs.add(ref);
+                        continue;
+                    }
+                    if (dumpPackage != null && !dumpPackage.equals(rec.key.packageName)) {
+                        continue;
+                    }
+                    ArrayList<PendingIntentRecord> list = byPackage.get(rec.key.packageName);
+                    if (list == null) {
+                        list = new ArrayList<>();
+                        byPackage.put(rec.key.packageName, list);
+                    }
+                    list.add(rec);
+                }
+                for (int i = 0; i < byPackage.size(); i++) {
+                    ArrayList<PendingIntentRecord> intents = byPackage.valueAt(i);
+                    printed = true;
+                    pw.print("  * "); pw.print(byPackage.keyAt(i));
+                    pw.print(": "); pw.print(intents.size()); pw.println(" items");
+                    for (int j = 0; j < intents.size(); j++) {
+                        pw.print("    #"); pw.print(j); pw.print(": "); pw.println(intents.get(j));
+                        if (dumpAll) {
+                            intents.get(j).dump(pw, "      ");
+                        }
+                    }
+                }
+                if (weakRefs.size() > 0) {
+                    printed = true;
+                    pw.println("  * WEAK REFS:");
+                    for (int i = 0; i < weakRefs.size(); i++) {
+                        pw.print("    #"); pw.print(i); pw.print(": "); pw.println(weakRefs.get(i));
+                    }
+                }
+            }
+
+            if (!printed) {
+                pw.println("  (nothing)");
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index ee1166e..b9c6fa6 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -38,15 +38,16 @@
 import android.util.TimeUtils;
 
 import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.Objects;
 
-final class PendingIntentRecord extends IIntentSender.Stub {
+public final class PendingIntentRecord extends IIntentSender.Stub {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "PendingIntentRecord" : TAG_AM;
 
-    final ActivityManagerService owner;
+    final PendingIntentController controller;
     final Key key;
     final int uid;
     final WeakReference<PendingIntentRecord> ref;
@@ -62,7 +63,7 @@
     final static class Key {
         final int type;
         final String packageName;
-        final ActivityRecord activity;
+        final IBinder activity;
         final String who;
         final int requestCode;
         final Intent requestIntent;
@@ -76,7 +77,7 @@
 
         private static final int ODD_PRIME_NUMBER = 37;
 
-        Key(int _t, String _p, ActivityRecord _a, String _w,
+        Key(int _t, String _p, IBinder _a, String _w,
                 int _r, Intent[] _i, String[] _it, int _f, SafeActivityOptions _o, int _userId) {
             type = _t;
             packageName = _p;
@@ -114,6 +115,7 @@
             //        + Integer.toHexString(hashCode));
         }
 
+        @Override
         public boolean equals(Object otherObj) {
             if (otherObj == null) {
                 return false;
@@ -188,11 +190,11 @@
         }
     }
 
-    PendingIntentRecord(ActivityManagerService _owner, Key _k, int _u) {
-        owner = _owner;
+    PendingIntentRecord(PendingIntentController _controller, Key _k, int _u) {
+        controller = _controller;
         key = _k;
         uid = _u;
-        ref = new WeakReference<PendingIntentRecord>(this);
+        ref = new WeakReference<>(this);
     }
 
     void setWhitelistDurationLocked(IBinder whitelistToken, long duration) {
@@ -247,189 +249,196 @@
     }
 
     int sendInner(int code, Intent intent, String resolvedType, IBinder whitelistToken,
-            IIntentReceiver finishedReceiver,
-            String requiredPermission, IBinder resultTo, String resultWho, int requestCode,
-            int flagsMask, int flagsValues, Bundle options) {
+            IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo,
+            String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) {
         if (intent != null) intent.setDefusable(true);
         if (options != null) options.setDefusable(true);
 
-        synchronized (owner) {
-            if (!canceled) {
-                sent = true;
-                if ((key.flags&PendingIntent.FLAG_ONE_SHOT) != 0) {
-                    owner.cancelIntentSenderLocked(this, true);
-                }
+        Long duration = null;
+        Intent finalIntent = null;
+        Intent[] allIntents = null;
+        String[] allResolvedTypes = null;
+        SafeActivityOptions mergedOptions = null;
+        synchronized (controller.mLock) {
+            if (canceled) {
+                return ActivityManager.START_CANCELED;
+            }
 
-                Intent finalIntent = key.requestIntent != null
-                        ? new Intent(key.requestIntent) : new Intent();
+            sent = true;
+            if ((key.flags & PendingIntent.FLAG_ONE_SHOT) != 0) {
+                controller.cancelIntentSender(this, true);
+            }
 
-                final boolean immutable = (key.flags & PendingIntent.FLAG_IMMUTABLE) != 0;
-                if (!immutable) {
-                    if (intent != null) {
-                        int changes = finalIntent.fillIn(intent, key.flags);
-                        if ((changes & Intent.FILL_IN_DATA) == 0) {
-                            resolvedType = key.requestResolvedType;
-                        }
-                    } else {
+            finalIntent = key.requestIntent != null ? new Intent(key.requestIntent) : new Intent();
+
+            final boolean immutable = (key.flags & PendingIntent.FLAG_IMMUTABLE) != 0;
+            if (!immutable) {
+                if (intent != null) {
+                    int changes = finalIntent.fillIn(intent, key.flags);
+                    if ((changes & Intent.FILL_IN_DATA) == 0) {
                         resolvedType = key.requestResolvedType;
                     }
-                    flagsMask &= ~Intent.IMMUTABLE_FLAGS;
-                    flagsValues &= flagsMask;
-                    finalIntent.setFlags((finalIntent.getFlags() & ~flagsMask) | flagsValues);
                 } else {
                     resolvedType = key.requestResolvedType;
                 }
-
-                final int callingUid = Binder.getCallingUid();
-                final int callingPid = Binder.getCallingPid();
-
-                // Extract options before clearing calling identity
-                SafeActivityOptions mergedOptions = key.options;
-                if (mergedOptions == null) {
-                    mergedOptions = SafeActivityOptions.fromBundle(options);
-                } else {
-                    mergedOptions.setCallerOptions(ActivityOptions.fromBundle(options));
-                }
-
-                final long origId = Binder.clearCallingIdentity();
-
-                if (whitelistDuration != null) {
-                    Long duration = whitelistDuration.get(whitelistToken);
-                    if (duration != null) {
-                        int procState = owner.getUidState(callingUid);
-                        if (!ActivityManager.isProcStateBackground(procState)) {
-                            StringBuilder tag = new StringBuilder(64);
-                            tag.append("pendingintent:");
-                            UserHandle.formatUid(tag, callingUid);
-                            tag.append(":");
-                            if (finalIntent.getAction() != null) {
-                                tag.append(finalIntent.getAction());
-                            } else if (finalIntent.getComponent() != null) {
-                                finalIntent.getComponent().appendShortString(tag);
-                            } else if (finalIntent.getData() != null) {
-                                tag.append(finalIntent.getData().toSafeString());
-                            }
-                            owner.tempWhitelistForPendingIntentLocked(callingPid,
-                                    callingUid, uid, duration, tag.toString());
-                        } else {
-                            Slog.w(TAG, "Not doing whitelist " + this + ": caller state="
-                                    + procState);
-                        }
-                    }
-                }
-
-                boolean sendFinish = finishedReceiver != null;
-                int userId = key.userId;
-                if (userId == UserHandle.USER_CURRENT) {
-                    userId = owner.mUserController.getCurrentOrTargetUserId();
-                }
-                int res = START_SUCCESS;
-                switch (key.type) {
-                    case ActivityManager.INTENT_SENDER_ACTIVITY:
-                        try {
-                            // Note when someone has a pending intent, even from different
-                            // users, then there's no need to ensure the calling user matches
-                            // the target user, so validateIncomingUser is always false below.
-
-                            if (key.allIntents != null && key.allIntents.length > 1) {
-                                Intent[] allIntents = new Intent[key.allIntents.length];
-                                String[] allResolvedTypes = new String[key.allIntents.length];
-                                System.arraycopy(key.allIntents, 0, allIntents, 0,
-                                        key.allIntents.length);
-                                if (key.allResolvedTypes != null) {
-                                    System.arraycopy(key.allResolvedTypes, 0, allResolvedTypes, 0,
-                                            key.allResolvedTypes.length);
-                                }
-                                allIntents[allIntents.length-1] = finalIntent;
-                                allResolvedTypes[allResolvedTypes.length-1] = resolvedType;
-
-                                res = owner.mActivityTaskManager.getActivityStartController().startActivitiesInPackage(
-                                        uid, key.packageName, allIntents, allResolvedTypes,
-                                        resultTo, mergedOptions, userId,
-                                        false /* validateIncomingUser */,
-                                        this /* originatingPendingIntent */);
-                            } else {
-                                res = owner.mActivityTaskManager.getActivityStartController().startActivityInPackage(uid,
-                                        callingPid, callingUid, key.packageName, finalIntent,
-                                        resolvedType, resultTo, resultWho, requestCode, 0,
-                                        mergedOptions, userId, null, "PendingIntentRecord",
-                                        false /* validateIncomingUser */,
-                                        this /* originatingPendingIntent */);
-                            }
-                        } catch (RuntimeException e) {
-                            Slog.w(TAG, "Unable to send startActivity intent", e);
-                        }
-                        break;
-                    case ActivityManager.INTENT_SENDER_ACTIVITY_RESULT:
-                        final ActivityStack stack = key.activity.getStack();
-                        if (stack != null) {
-                            stack.sendActivityResultLocked(-1, key.activity, key.who,
-                                    key.requestCode, code, finalIntent);
-                        }
-                        break;
-                    case ActivityManager.INTENT_SENDER_BROADCAST:
-                        try {
-                            // If a completion callback has been requested, require
-                            // that the broadcast be delivered synchronously
-                            int sent = owner.broadcastIntentInPackage(key.packageName, uid,
-                                    finalIntent, resolvedType, finishedReceiver, code, null, null,
-                                    requiredPermission, options, (finishedReceiver != null),
-                                    false, userId);
-                            if (sent == ActivityManager.BROADCAST_SUCCESS) {
-                                sendFinish = false;
-                            }
-                        } catch (RuntimeException e) {
-                            Slog.w(TAG, "Unable to send startActivity intent", e);
-                        }
-                        break;
-                    case ActivityManager.INTENT_SENDER_SERVICE:
-                    case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE:
-                        try {
-                            owner.startServiceInPackage(uid, finalIntent, resolvedType,
-                                    key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE,
-                                    key.packageName, userId);
-                        } catch (RuntimeException e) {
-                            Slog.w(TAG, "Unable to send startService intent", e);
-                        } catch (TransactionTooLargeException e) {
-                            res = ActivityManager.START_CANCELED;
-                        }
-                        break;
-                }
-
-                if (sendFinish && res != ActivityManager.START_CANCELED) {
-                    try {
-                        finishedReceiver.performReceive(new Intent(finalIntent), 0,
-                                null, null, false, false, key.userId);
-                    } catch (RemoteException e) {
-                    }
-                }
-
-                Binder.restoreCallingIdentity(origId);
-
-                return res;
+                flagsMask &= ~Intent.IMMUTABLE_FLAGS;
+                flagsValues &= flagsMask;
+                finalIntent.setFlags((finalIntent.getFlags() & ~flagsMask) | flagsValues);
+            } else {
+                resolvedType = key.requestResolvedType;
             }
+
+            // Extract options before clearing calling identity
+            mergedOptions = key.options;
+            if (mergedOptions == null) {
+                mergedOptions = SafeActivityOptions.fromBundle(options);
+            } else {
+                mergedOptions.setCallerOptions(ActivityOptions.fromBundle(options));
+            }
+
+            if (whitelistDuration != null) {
+                duration = whitelistDuration.get(whitelistToken);
+            }
+
+            if (key.type == ActivityManager.INTENT_SENDER_ACTIVITY
+                    && key.allIntents != null && key.allIntents.length > 1) {
+                // Copy all intents and resolved types while we have the controller lock so we can
+                // use it later when the lock isn't held.
+                allIntents = new Intent[key.allIntents.length];
+                allResolvedTypes = new String[key.allIntents.length];
+                System.arraycopy(key.allIntents, 0, allIntents, 0, key.allIntents.length);
+                if (key.allResolvedTypes != null) {
+                    System.arraycopy(key.allResolvedTypes, 0, allResolvedTypes, 0,
+                            key.allResolvedTypes.length);
+                }
+                allIntents[allIntents.length - 1] = finalIntent;
+                allResolvedTypes[allResolvedTypes.length - 1] = resolvedType;
+            }
+
         }
-        return ActivityManager.START_CANCELED;
+        // We don't hold the controller lock beyond this point as we will be calling into AM and WM.
+
+        final int callingUid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
+        final long origId = Binder.clearCallingIdentity();
+
+        int res = START_SUCCESS;
+        try {
+            if (duration != null) {
+                int procState = controller.mAmInternal.getUidProcessState(callingUid);
+                if (!ActivityManager.isProcStateBackground(procState)) {
+                    StringBuilder tag = new StringBuilder(64);
+                    tag.append("pendingintent:");
+                    UserHandle.formatUid(tag, callingUid);
+                    tag.append(":");
+                    if (finalIntent.getAction() != null) {
+                        tag.append(finalIntent.getAction());
+                    } else if (finalIntent.getComponent() != null) {
+                        finalIntent.getComponent().appendShortString(tag);
+                    } else if (finalIntent.getData() != null) {
+                        tag.append(finalIntent.getData().toSafeString());
+                    }
+                    controller.mAmInternal.tempWhitelistForPendingIntent(callingPid, callingUid,
+                            uid, duration, tag.toString());
+                } else {
+                    Slog.w(TAG, "Not doing whitelist " + this + ": caller state=" + procState);
+                }
+            }
+
+            boolean sendFinish = finishedReceiver != null;
+            int userId = key.userId;
+            if (userId == UserHandle.USER_CURRENT) {
+                userId = controller.mUserController.getCurrentOrTargetUserId();
+            }
+
+            switch (key.type) {
+                case ActivityManager.INTENT_SENDER_ACTIVITY:
+                    try {
+                        // Note when someone has a pending intent, even from different
+                        // users, then there's no need to ensure the calling user matches
+                        // the target user, so validateIncomingUser is always false below.
+
+                        if (key.allIntents != null && key.allIntents.length > 1) {
+                            res = controller.mAtmInternal.startActivitiesInPackage(
+                                    uid, key.packageName, allIntents, allResolvedTypes, resultTo,
+                                    mergedOptions, userId, false /* validateIncomingUser */,
+                                    this /* originatingPendingIntent */);
+                        } else {
+                            res = controller.mAtmInternal.startActivityInPackage(
+                                    uid, callingPid, callingUid, key.packageName, finalIntent,
+                                    resolvedType, resultTo, resultWho, requestCode, 0,
+                                    mergedOptions, userId, null, "PendingIntentRecord",
+                                    false /* validateIncomingUser */,
+                                    this /* originatingPendingIntent */);
+                        }
+                    } catch (RuntimeException e) {
+                        Slog.w(TAG, "Unable to send startActivity intent", e);
+                    }
+                    break;
+                case ActivityManager.INTENT_SENDER_ACTIVITY_RESULT:
+                    controller.mAtmInternal.sendActivityResult(-1, key.activity, key.who,
+                                key.requestCode, code, finalIntent);
+                    break;
+                case ActivityManager.INTENT_SENDER_BROADCAST:
+                    try {
+                        // If a completion callback has been requested, require
+                        // that the broadcast be delivered synchronously
+                        int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName,
+                                uid, finalIntent, resolvedType, finishedReceiver, code, null, null,
+                                requiredPermission, options, (finishedReceiver != null),
+                                false, userId);
+                        if (sent == ActivityManager.BROADCAST_SUCCESS) {
+                            sendFinish = false;
+                        }
+                    } catch (RuntimeException e) {
+                        Slog.w(TAG, "Unable to send startActivity intent", e);
+                    }
+                    break;
+                case ActivityManager.INTENT_SENDER_SERVICE:
+                case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE:
+                    try {
+                        controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType,
+                                key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE,
+                                key.packageName, userId);
+                    } catch (RuntimeException e) {
+                        Slog.w(TAG, "Unable to send startService intent", e);
+                    } catch (TransactionTooLargeException e) {
+                        res = ActivityManager.START_CANCELED;
+                    }
+                    break;
+            }
+
+            if (sendFinish && res != ActivityManager.START_CANCELED) {
+                try {
+                    finishedReceiver.performReceive(new Intent(finalIntent), 0,
+                            null, null, false, false, key.userId);
+                } catch (RemoteException e) {
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+
+        return res;
     }
 
     @Override
     protected void finalize() throws Throwable {
         try {
             if (!canceled) {
-                owner.mHandler.sendMessage(owner.mHandler.obtainMessage(
-                        ActivityManagerService.FINALIZE_PENDING_INTENT_MSG, this));
+                controller.mH.sendMessage(PooledLambda.obtainMessage(
+                        PendingIntentRecord::completeFinalize, this));
             }
         } finally {
             super.finalize();
         }
     }
 
-    public void completeFinalize() {
-        synchronized(owner) {
-            WeakReference<PendingIntentRecord> current =
-                    owner.mIntentSenderRecords.get(key);
+    private void completeFinalize() {
+        synchronized(controller.mLock) {
+            WeakReference<PendingIntentRecord> current = controller.mIntentSenderRecords.get(key);
             if (current == ref) {
-                owner.mIntentSenderRecords.remove(key);
+                controller.mIntentSenderRecords.remove(key);
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 8ce650c..6ffd8a9 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -23,8 +23,10 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.service.procstats.ProcessStatsServiceDumpProto;
+import android.text.format.DateFormat;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
+import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -47,6 +49,7 @@
 import java.io.InputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.locks.ReentrantLock;
@@ -482,6 +485,23 @@
         return finalRes;
     }
 
+    static int parseSectionOptions(String optionsStr) {
+        final String sep = ",";
+        String[] sectionsStr = optionsStr.split(sep);
+        if (sectionsStr.length == 0) {
+            return ProcessStats.REPORT_ALL;
+        }
+        int res = 0;
+        List<String> optionStrList = Arrays.asList(ProcessStats.OPTIONS_STR);
+        for (String sectionStr : sectionsStr) {
+            int optionIndex = optionStrList.indexOf(sectionStr);
+            if (optionIndex != -1) {
+                res |= ProcessStats.OPTIONS[optionIndex];
+            }
+        }
+        return res;
+    }
+
     public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) {
         mAm.mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
@@ -514,6 +534,95 @@
         return current.marshall();
     }
 
+    /**
+     * Get stats committed after highWaterMarkMs
+     * @param highWaterMarkMs Report stats committed after this time.
+     * @param section Integer mask to indicage which sections to include in the stats.
+     * @param doAggregate Whether to aggregate the stats or keep them separated.
+     * @return List of proto binary of individual commit files or one that is merged from them.
+     */
+    @Override
+    public long getCommittedStats(long highWaterMarkMs, int section, boolean doAggregate,
+            List<ParcelFileDescriptor> committedStats) {
+        mAm.mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.PACKAGE_USAGE_STATS, null);
+
+        ProcessStats mergedStats = new ProcessStats(false);
+        long newHighWaterMark = highWaterMarkMs;
+        mWriteLock.lock();
+        try {
+            ArrayList<String> files = getCommittedFiles(0, false, true);
+            if (files != null) {
+                String highWaterMarkStr =
+                        DateFormat.format("yyyy-MM-dd-HH-mm-ss", highWaterMarkMs).toString();
+                ProcessStats stats = new ProcessStats(false);
+                for (int i = files.size() - 1; i >= 0; i--) {
+                    String fileName = files.get(i);
+                    try {
+                        String startTimeStr = fileName.substring(
+                                fileName.lastIndexOf(STATE_FILE_PREFIX)
+                                        + STATE_FILE_PREFIX.length(),
+                                fileName.lastIndexOf(STATE_FILE_SUFFIX));
+                        if (startTimeStr.compareToIgnoreCase(highWaterMarkStr) > 0) {
+                            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
+                                    new File(fileName),
+                                    ParcelFileDescriptor.MODE_READ_ONLY);
+                            InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+                            stats.reset();
+                            stats.read(is);
+                            is.close();
+                            if (stats.mTimePeriodStartClock > newHighWaterMark) {
+                                newHighWaterMark = stats.mTimePeriodStartClock;
+                            }
+                            if (doAggregate) {
+                                mergedStats.add(stats);
+                            } else {
+                                committedStats.add(protoToParcelFileDescriptor(stats, section));
+                            }
+                            if (stats.mReadError != null) {
+                                Log.w(TAG, "Failure reading process stats: " + stats.mReadError);
+                                continue;
+                            }
+                        }
+                    } catch (IOException e) {
+                        Slog.w(TAG, "Failure opening procstat file " + fileName, e);
+                    } catch (IndexOutOfBoundsException e) {
+                        Slog.w(TAG, "Failure to read and parse commit file " + fileName, e);
+                    }
+                }
+                if (doAggregate) {
+                    committedStats.add(protoToParcelFileDescriptor(mergedStats, section));
+                }
+                return newHighWaterMark;
+            }
+        } catch (IOException e) {
+            Slog.w(TAG, "Failure opening procstat file", e);
+        } finally {
+            mWriteLock.unlock();
+        }
+        return newHighWaterMark;
+    }
+
+    private ParcelFileDescriptor protoToParcelFileDescriptor(ProcessStats stats, int section)
+            throws IOException {
+        final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
+        Thread thr = new Thread("ProcessStats pipe output") {
+            public void run() {
+                try {
+                    FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]);
+                    final ProtoOutputStream proto = new ProtoOutputStream(fout);
+                    stats.writeToProto(proto, stats.mTimePeriodEndRealtime, section);
+                    proto.flush();
+                    fout.close();
+                } catch (IOException e) {
+                    Slog.w(TAG, "Failure writing pipe", e);
+                }
+            }
+        };
+        thr.start();
+        return fds[0];
+    }
+
     public ParcelFileDescriptor getStatsOverTime(long minTime) {
         mAm.mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
@@ -594,7 +703,7 @@
 
     private void dumpAggregatedStats(PrintWriter pw, long aggregateHours, long now,
             String reqPackage, boolean isCompact, boolean dumpDetails, boolean dumpFullDetails,
-            boolean dumpAll, boolean activeOnly) {
+            boolean dumpAll, boolean activeOnly, int section) {
         ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000
                 - (ProcessStats.COMMIT_PERIOD/2));
         if (pfd == null) {
@@ -609,11 +718,11 @@
             return;
         }
         if (isCompact) {
-            stats.dumpCheckinLocked(pw, reqPackage);
+            stats.dumpCheckinLocked(pw, reqPackage, section);
         } else {
             if (dumpDetails || dumpFullDetails) {
                 stats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails, dumpAll,
-                        activeOnly);
+                        activeOnly, section);
             } else {
                 stats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
             }
@@ -643,6 +752,8 @@
         pw.println("  --max: for -a, max num of historical batches to print.");
         pw.println("  --active: only show currently active processes/services.");
         pw.println("  --commit: commit current stats to disk and reset to start new stats.");
+        pw.println("  --section: proc|pkg-proc|pkg-svc|pkg-asc|pkg-all|all ");
+        pw.println("    options can be combined to select desired stats");
         pw.println("  --reset: reset current stats, without committing.");
         pw.println("  --clear: clear all stats; does both --reset and deletes old stats.");
         pw.println("  --write: write current in-memory stats to disk.");
@@ -696,6 +807,7 @@
         int[] csvMemStats = new int[] { ProcessStats.ADJ_MEM_FACTOR_CRITICAL};
         boolean csvSepProcStats = true;
         int[] csvProcStats = ProcessStats.ALL_PROC_STATES;
+        int section = ProcessStats.REPORT_ALL;
         if (args != null) {
             for (int i=0; i<args.length; i++) {
                 String arg = args[i];
@@ -814,13 +926,14 @@
                         pw.println("Process stats committed.");
                         quit = true;
                     }
-                } else if ("--reset".equals(arg)) {
-                    synchronized (mAm) {
-                        mProcessStats.resetSafely();
-                        mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
-                        pw.println("Process stats reset.");
-                        quit = true;
+                } else if ("--section".equals(arg)) {
+                    i++;
+                    if (i >= args.length) {
+                        pw.println("Error: argument required for --section");
+                        dumpHelp(pw);
+                        return;
                     }
+                    section = parseSectionOptions(args[i]);
                 } else if ("--clear".equals(arg)) {
                     synchronized (mAm) {
                         mProcessStats.resetSafely();
@@ -946,7 +1059,7 @@
         } else if (aggregateHours != 0) {
             pw.print("AGGREGATED OVER LAST "); pw.print(aggregateHours); pw.println(" HOURS:");
             dumpAggregatedStats(pw, aggregateHours, now, reqPackage, isCompact,
-                    dumpDetails, dumpFullDetails, dumpAll, activeOnly);
+                    dumpDetails, dumpFullDetails, dumpAll, activeOnly, section);
             return;
         } else if (lastIndex > 0) {
             pw.print("LAST STATS AT INDEX "); pw.print(lastIndex); pw.println(":");
@@ -968,7 +1081,7 @@
             boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
             if (isCheckin || isCompact) {
                 // Don't really need to lock because we uniquely own this object.
-                processStats.dumpCheckinLocked(pw, reqPackage);
+                processStats.dumpCheckinLocked(pw, reqPackage, section);
             } else {
                 pw.print("COMMITTED STATS FROM ");
                 pw.print(processStats.mTimePeriodStartClockStr);
@@ -976,7 +1089,7 @@
                 pw.println(":");
                 if (dumpDetails || dumpFullDetails) {
                     processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
-                            dumpAll, activeOnly);
+                            dumpAll, activeOnly, section);
                     if (dumpAll) {
                         pw.print("  mFile="); pw.println(mFile.getBaseFile());
                     }
@@ -1015,7 +1128,7 @@
                             boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
                             if (isCheckin || isCompact) {
                                 // Don't really need to lock because we uniquely own this object.
-                                processStats.dumpCheckinLocked(pw, reqPackage);
+                                processStats.dumpCheckinLocked(pw, reqPackage, section);
                             } else {
                                 if (sepNeeded) {
                                     pw.println();
@@ -1031,7 +1144,7 @@
                                 // much crud.
                                 if (dumpFullDetails) {
                                     processStats.dumpLocked(pw, reqPackage, now, false, false,
-                                            false, activeOnly);
+                                            false, activeOnly, section);
                                 } else {
                                     processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
                                 }
@@ -1054,7 +1167,7 @@
         if (!isCheckin) {
             synchronized (mAm) {
                 if (isCompact) {
-                    mProcessStats.dumpCheckinLocked(pw, reqPackage);
+                    mProcessStats.dumpCheckinLocked(pw, reqPackage, section);
                 } else {
                     if (sepNeeded) {
                         pw.println();
@@ -1062,7 +1175,7 @@
                     pw.println("CURRENT STATS:");
                     if (dumpDetails || dumpFullDetails) {
                         mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
-                                dumpAll, activeOnly);
+                                dumpAll, activeOnly, section);
                         if (dumpAll) {
                             pw.print("  mFile="); pw.println(mFile.getBaseFile());
                         }
@@ -1078,11 +1191,11 @@
                 }
                 pw.println("AGGREGATED OVER LAST 24 HOURS:");
                 dumpAggregatedStats(pw, 24, now, reqPackage, isCompact,
-                        dumpDetails, dumpFullDetails, dumpAll, activeOnly);
+                        dumpDetails, dumpFullDetails, dumpAll, activeOnly, section);
                 pw.println();
                 pw.println("AGGREGATED OVER LAST 3 HOURS:");
                 dumpAggregatedStats(pw, 3, now, reqPackage, isCompact,
-                        dumpDetails, dumpFullDetails, dumpAll, activeOnly);
+                        dumpDetails, dumpFullDetails, dumpAll, activeOnly, section);
             }
         }
     }
@@ -1099,7 +1212,9 @@
         if (stats.mReadError != null) {
             return;
         }
-        stats.writeToProto(proto, fieldId, now);
+        final long token = proto.start(fieldId);
+        stats.writeToProto(proto, now, ProcessStats.REPORT_ALL);
+        proto.end(token);
     }
 
     private void dumpProto(FileDescriptor fd) {
@@ -1109,7 +1224,9 @@
         long now;
         synchronized (mAm) {
             now = SystemClock.uptimeMillis();
-            mProcessStats.writeToProto(proto,ProcessStatsServiceDumpProto.PROCSTATS_NOW, now);
+            final long token = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_NOW);
+            mProcessStats.writeToProto(proto, now, ProcessStats.REPORT_ALL);
+            proto.end(token);
         }
 
         // aggregated over last 3 hours procstats
diff --git a/services/core/java/com/android/server/am/SafeActivityOptions.java b/services/core/java/com/android/server/am/SafeActivityOptions.java
index f7de7f4..fa0cb47 100644
--- a/services/core/java/com/android/server/am/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/am/SafeActivityOptions.java
@@ -44,7 +44,7 @@
  * the inner options. Also supports having two set of options: Once from the original caller, and
  * once from the caller that is overriding it, which happens when sending a {@link PendingIntent}.
  */
-class SafeActivityOptions {
+public class SafeActivityOptions {
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_AM;
 
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 9b42d65..ef8cb1c 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -129,7 +129,8 @@
 import java.util.ArrayList;
 import java.util.Objects;
 
-class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
+// TODO: Make package private again once move to WM package is complete.
+public class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_AM;
     private static final String TAG_ADD_REMOVE = TAG + POSTFIX_ADD_REMOVE;
     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 8caa702..66c7c43 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -627,6 +627,13 @@
     // If absolute volume is supported in AVRCP device
     private boolean mAvrcpAbsVolSupported = false;
 
+    // Pre-scale for Bluetooth Absolute Volume
+    private float[] mPrescaleAbsoluteVolume = new float[] {
+        0.5f,    // Pre-scale for index 1
+        0.7f,    // Pre-scale for index 2
+        0.85f,   // Pre-scale for index 3
+    };
+
     private static Long mLastDeviceConnectMsgTime = new Long(0);
 
     private NotificationManager mNm;
@@ -878,6 +885,23 @@
         mUserManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
 
         mRecordMonitor.initMonitor();
+
+        final float[] preScale = new float[3];
+        preScale[0] = mContext.getResources().getFraction(
+                com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index1,
+                1, 1);
+        preScale[1] = mContext.getResources().getFraction(
+                com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index2,
+                1, 1);
+        preScale[2] = mContext.getResources().getFraction(
+                com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index3,
+                1, 1);
+        for (int i = 0; i < preScale.length; i++) {
+            if (0.0f <= preScale[i] && preScale[i] <= 1.0f) {
+                mPrescaleAbsoluteVolume[i] = preScale[i];
+            }
+        }
+
     }
 
     public void systemReady() {
@@ -4926,18 +4950,12 @@
             if (index == 0) {
                 // 0% for volume 0
                 index = 0;
-            } else if (index == 1) {
-                // 50% for volume 1
-                index = (int)(mIndexMax * 0.5) /10;
-            } else if (index == 2) {
-                // 70% for volume 2
-                index = (int)(mIndexMax * 0.70) /10;
-            } else if (index == 3) {
-                // 85% for volume 3
-                index = (int)(mIndexMax * 0.85) /10;
+            } else if (index > 0 && index <= 3) {
+                // Pre-scale for volume steps 1 2 and 3
+                index = (int) (mIndexMax * mPrescaleAbsoluteVolume[index - 1]) / 10;
             } else {
                 // otherwise, full gain
-                index = (mIndexMax + 5)/10;
+                index = (mIndexMax + 5) / 10;
             }
             return index;
         }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index deeae26..2557f46 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2828,7 +2828,8 @@
                 // Assumes it's safe to show starting windows of launched apps while
                 // the keyguard is being hidden. This is okay because starting windows never show
                 // secret information.
-                if (mKeyguardOccluded) {
+                // TODO(b/113840485): Occluded may not only happen on default display
+                if (displayId == DEFAULT_DISPLAY && mKeyguardOccluded) {
                     windowFlags |= FLAG_SHOW_WHEN_LOCKED;
                 }
             }
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 68e636a..5adc248 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -190,6 +190,8 @@
         try {
             mBatteryStats.noteInteractive(true);
         } catch (RemoteException ex) { }
+        StatsLog.write(StatsLog.INTERACTIVE_STATE_CHANGED,
+                StatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON);
     }
 
     /**
@@ -401,6 +403,9 @@
             try {
                 mBatteryStats.noteInteractive(interactive);
             } catch (RemoteException ex) { }
+            StatsLog.write(StatsLog.INTERACTIVE_STATE_CHANGED,
+                    interactive ? StatsLog.INTERACTIVE_STATE_CHANGED__STATE__ON :
+                            StatsLog.INTERACTIVE_STATE_CHANGED__STATE__OFF);
 
             // Handle early behaviors.
             mInteractive = interactive;
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index bfa03ca..c6e6449 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -966,6 +966,7 @@
             e.writeLong(processMemoryState.rssInBytes);
             e.writeLong(processMemoryState.cacheInBytes);
             e.writeLong(processMemoryState.swapInBytes);
+            e.writeLong(processMemoryState.rssHighWatermarkInBytes);
             pulledData.add(e);
         }
     }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 7d2fc15..bcf9212 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -22,6 +22,7 @@
 import android.app.IActivityManager;
 import android.app.IApplicationThread;
 import android.content.ComponentName;
+import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.res.CompatibilityInfo;
@@ -32,8 +33,12 @@
 import android.util.SparseIntArray;
 
 import com.android.internal.app.IVoiceInteractor;
+import com.android.server.am.PendingIntentRecord;
+import com.android.server.am.SafeActivityOptions;
+import com.android.server.am.TaskRecord;
 import com.android.server.am.WindowProcessController;
 
+import java.lang.ref.WeakReference;
 import java.util.List;
 
 /**
@@ -178,6 +183,27 @@
             int userId, Intent[] intents, Bundle bOptions);
 
     /**
+     * Start intents as a package.
+     *
+     * @param uid Make a call as if this UID did.
+     * @param callingPackage Make a call as if this package did.
+     * @param intents Intents to start.
+     * @param userId Start the intents on this user.
+     * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
+     * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
+     *        null if not originated by PendingIntent
+     */
+    public abstract int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
+            String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
+            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent);
+
+    public abstract int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
+            String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
+            int userId, TaskRecord inTask, String reason, boolean validateIncomingUser,
+            PendingIntentRecord originatingPendingIntent);
+
+    /**
      * Start activity {@code intent} without calling user-id check.
      *
      * - DO NOT call it with the calling UID cleared.
@@ -297,4 +323,13 @@
      * @param displayId The ID of the display showing the IME.
      */
     public abstract void onImeWindowSetOnDisplay(int pid, int displayId);
+
+    public abstract void sendActivityResult(int callingUid, IBinder activityToken,
+            String resultWho, int requestCode, int resultCode, Intent data);
+    public abstract void clearPendingResultForActivity(
+            IBinder activityToken, WeakReference<PendingIntentRecord> pir);
+    public abstract IIntentSender getIntentSender(int type, String packageName,
+            int callingUid, int userId, IBinder token, String resultWho,
+            int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
+            Bundle bOptions);
 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index eaaf804..236982f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2652,6 +2652,12 @@
      */
     void setInputMethodWindowLocked(WindowState win) {
         mInputMethodWindow = win;
+        // Update display configuration for IME process.
+        if (mInputMethodWindow != null) {
+            final int imePid = mInputMethodWindow.mSession.mPid;
+            mService.mAtmInternal.onImeWindowSetOnDisplay(imePid,
+                    mInputMethodWindow.getDisplayId());
+        }
         computeImeTarget(true /* updateImeTarget */);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7161a70..f7c6d77 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4535,7 +4535,8 @@
         updateSurfacePosition(getPendingTransaction());
     }
 
-    private void updateSurfacePosition(Transaction t) {
+    @VisibleForTesting
+    void updateSurfacePosition(Transaction t) {
         if (mSurfaceControl == null) {
             return;
         }
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java b/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
new file mode 100644
index 0000000..3730335
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/keyvalue/AgentExceptionTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 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.backup.keyvalue;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import java.io.IOException;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class AgentExceptionTest {
+    @Test
+    public void testTransitory_isTransitory() throws Exception {
+        AgentException exception = AgentException.transitory();
+
+        assertThat(exception.isTransitory()).isTrue();
+    }
+
+    @Test
+    public void testTransitory_withCause() throws Exception {
+        Exception cause = new IOException();
+
+        AgentException exception = AgentException.transitory(cause);
+
+        assertThat(exception.isTransitory()).isTrue();
+        assertThat(exception.getCause()).isEqualTo(cause);
+    }
+
+    @Test
+    public void testPermanent_isNotTransitory() throws Exception {
+        AgentException exception = AgentException.permanent();
+
+        assertThat(exception.isTransitory()).isFalse();
+    }
+
+    @Test
+    public void testPermanent_withCause() throws Exception {
+        Exception cause = new IOException();
+
+        AgentException exception = AgentException.permanent(cause);
+
+        assertThat(exception.isTransitory()).isFalse();
+        assertThat(exception.getCause()).isEqualTo(cause);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java b/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
new file mode 100644
index 0000000..5ea74f1
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/keyvalue/BackupExceptionTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 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.backup.keyvalue;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import java.io.IOException;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class BackupExceptionTest {
+    @Test
+    public void testConstructor_passesCause() {
+        Exception cause = new IOException();
+
+        Exception exception = new BackupException(cause);
+
+        assertThat(exception.getCause()).isEqualTo(cause);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index b4bc9d1..fb57d68 100644
--- a/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -155,9 +155,7 @@
 import java.util.concurrent.TimeoutException;
 import java.util.stream.Stream;
 
-// TODO: When returning to RUNNING_QUEUE vs FINAL, RUNNING_QUEUE sets status = OK. Why? Verify?
-// TODO: Check queue in general, behavior w/ multiple packages
-// TODO: Test PM invocation
+// TODO: Test agents timing out
 @RunWith(FrameworkRobolectricTestRunner.class)
 @Config(
         manifest = Config.NONE,
@@ -370,6 +368,47 @@
     }
 
     @Test
+    public void testRunTask_whenOnePackage_cleansUpPmFiles() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgent(PACKAGE_1);
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+        runTask(task);
+
+        assertCleansUpFiles(mTransport, PM_PACKAGE);
+    }
+
+    @Test
+    public void testRunTask_whenTransportReturnsTransportErrorForPm_cleansUpPmFiles()
+            throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PM_PACKAGE)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+        setUpAgent(PACKAGE_1);
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+        runTask(task);
+
+        assertCleansUpFiles(mTransport, PM_PACKAGE);
+    }
+
+    @Test
+    public void testRunTask_whenTransportReturnsTransportErrorForPm_resetsBackupState()
+            throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PM_PACKAGE)), any(), anyInt()))
+                .thenReturn(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+        setUpAgent(PACKAGE_1);
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mBackupManagerService).resetBackupState(getStateDirectory(mTransport).toFile());
+    }
+
+    @Test
     public void testRunTask_whenOnePackage_updatesBookkeeping() throws Exception {
         // Transport has to be initialized to not reset current token
         TransportMock transportMock = setUpInitializedTransport(mTransport);
@@ -418,7 +457,7 @@
     public void testRunTask_whenNonPmPackageAndNonIncremental_doesNotBackUpPm() throws Exception {
         TransportMock transportMock = setUpInitializedTransport(mTransport);
         setUpAgentWithData(PACKAGE_1);
-        PackageManagerBackupAgent pmAgent = spy(createPmAgent());
+        BackupAgent pmAgent = spy(createPmAgent());
         when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
         KeyValueBackupTask task = createKeyValueBackupTask(transportMock, true, PACKAGE_1);
 
@@ -431,7 +470,7 @@
     public void testRunTask_whenNonPmPackageAndPmAndNonIncremental_backsUpPm() throws Exception {
         TransportMock transportMock = setUpInitializedTransport(mTransport);
         setUpAgentWithData(PACKAGE_1);
-        PackageManagerBackupAgent pmAgent = spy(createPmAgent());
+        BackupAgent pmAgent = spy(createPmAgent());
         when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
         KeyValueBackupTask task =
                 createKeyValueBackupTask(transportMock, true, PACKAGE_1, PM_PACKAGE);
@@ -445,7 +484,7 @@
     public void testRunTask_whenNonPmPackageAndIncremental_backsUpPm() throws Exception {
         TransportMock transportMock = setUpInitializedTransport(mTransport);
         setUpAgentWithData(PACKAGE_1);
-        PackageManagerBackupAgent pmAgent = spy(createPmAgent());
+        BackupAgent pmAgent = spy(createPmAgent());
         when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
         KeyValueBackupTask task = createKeyValueBackupTask(transportMock, false, PACKAGE_1);
 
@@ -529,6 +568,35 @@
     }
 
     @Test
+    public void testRunTask_whenPackageUnknown() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        // Not calling setUpAgent() for PACKAGE_1
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+        runTask(task);
+
+        verify(transportMock.transport, never())
+                .performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
+        verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_PACKAGE_NOT_FOUND);
+        verify(mObserver).backupFinished(SUCCESS);
+        assertBackupNotPendingFor(PACKAGE_1);
+    }
+
+    @Test
+    public void testRunTask_whenFirstPackageUnknown_callsTransportForSecondPackage()
+            throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        // Not calling setUpAgent() for PACKAGE_1
+        setUpAgentWithData(PACKAGE_2);
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2);
+
+        runTask(task);
+
+        verify(transportMock.transport)
+                .performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt());
+    }
+
+    @Test
     public void testRunTask_whenPackageNotEligibleForBackup() throws Exception {
         TransportMock transportMock = setUpInitializedTransport(mTransport);
         AgentMock agentMock = setUpAgentWithData(PACKAGE_1.backupNotAllowed());
@@ -545,6 +613,19 @@
     }
 
     @Test
+    public void testRunTask_whenFirstPackageNotEligibleForBackup_callsTransportForSecondPackage()
+            throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgentsWithData(PACKAGE_1.backupNotAllowed(), PACKAGE_2);
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2);
+
+        runTask(task);
+
+        verify(transportMock.transport)
+                .performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt());
+    }
+
+    @Test
     public void testRunTask_whenPackageDoesFullBackup() throws Exception {
         TransportMock transportMock = setUpInitializedTransport(mTransport);
         PackageData packageData = fullBackupPackage(1);
@@ -561,6 +642,20 @@
     }
 
     @Test
+    public void testRunTask_whenFirstPackageDoesFullBackup_callsTransportForSecondPackage()
+            throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        PackageData packageData = fullBackupPackage(1);
+        setUpAgentsWithData(packageData, PACKAGE_2);
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, packageData, PACKAGE_2);
+
+        runTask(task);
+
+        verify(transportMock.transport)
+                .performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt());
+    }
+
+    @Test
     public void testRunTask_whenPackageIsStopped() throws Exception {
         TransportMock transportMock = setUpInitializedTransport(mTransport);
         AgentMock agentMock = setUpAgentWithData(PACKAGE_1.stopped());
@@ -575,18 +670,16 @@
     }
 
     @Test
-    public void testRunTask_whenPackageUnknown() throws Exception {
+    public void testRunTask_whenFirstPackageIsStopped_callsTransportForSecondPackage()
+            throws Exception {
         TransportMock transportMock = setUpInitializedTransport(mTransport);
-        // Not calling setUpAgent()
-        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+        setUpAgentsWithData(PACKAGE_1.stopped(), PACKAGE_2);
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1, PACKAGE_2);
 
         runTask(task);
 
-        verify(transportMock.transport, never())
-                .performBackup(argThat(packageInfo(PACKAGE_1)), any(), anyInt());
-        verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_PACKAGE_NOT_FOUND);
-        verify(mObserver).backupFinished(SUCCESS);
-        assertBackupNotPendingFor(PACKAGE_1);
+        verify(transportMock.transport)
+                .performBackup(argThat(packageInfo(PACKAGE_2)), any(), anyInt());
     }
 
     @Test
@@ -629,6 +722,7 @@
         verify(mBackupManagerService).setWorkSource(null);
         verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
         verify(mObserver).backupFinished(BackupManager.SUCCESS);
+        assertBackupPendingFor(PACKAGE_1);
     }
 
     @Test
@@ -645,6 +739,7 @@
         verify(mBackupManagerService).setWorkSource(null);
         verify(mObserver).onResult(PACKAGE_1.packageName, ERROR_AGENT_FAILURE);
         verify(mObserver).backupFinished(BackupManager.SUCCESS);
+        assertBackupPendingFor(PACKAGE_1);
     }
 
     @Test
@@ -798,7 +893,7 @@
 
         runTask(task);
 
-        assertBackupNotPendingFor(PACKAGE_1);
+        assertBackupPendingFor(PACKAGE_1);
     }
 
     @Test
@@ -1140,6 +1235,38 @@
     }
 
     @Test
+    public void testRunTask_whenPmAgentWritesData_callsTransportPerformBackupWithAgentData()
+            throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgent(PACKAGE_1);
+        Path backupDataPath = createTemporaryFile();
+        when(transportMock.transport.performBackup(
+                        argThat(packageInfo(PM_PACKAGE)), any(), anyInt()))
+                .then(copyBackupDataTo(backupDataPath));
+        BackupAgent pmAgent = spy(createPmAgent());
+        when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+        agentOnBackupDo(
+                pmAgent,
+                (oldState, dataOutput, newState) -> {
+                    writeData(dataOutput, "key1", "data1".getBytes());
+                    writeData(dataOutput, "key2", "data2".getBytes());
+                    writeState(newState, "newState".getBytes());
+                });
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+        runTask(task);
+
+        verify(transportMock.transport)
+                .performBackup(argThat(packageInfo(PM_PACKAGE)), any(), anyInt());
+        try (FileInputStream inputStream = new FileInputStream(backupDataPath.toFile())) {
+            BackupDataInput backupData = new BackupDataInput(inputStream.getFD());
+            assertDataHasKeyValue(backupData, "key1", "data1".getBytes());
+            assertDataHasKeyValue(backupData, "key2", "data2".getBytes());
+            assertThat(backupData.readNextHeader()).isFalse();
+        }
+    }
+
+    @Test
     public void testRunTask_whenPerformBackupSucceeds_callsTransportFinishBackup()
             throws Exception {
         TransportMock transportMock = setUpInitializedTransport(mTransport);
@@ -1176,6 +1303,50 @@
     }
 
     @Test
+    public void testRunTask_whenFinishBackupSucceedsForPm_cleansUp() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgent(PACKAGE_1);
+        when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK);
+        BackupAgent pmAgent = spy(createPmAgent());
+        when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+        agentOnBackupDo(
+                pmAgent,
+                (oldState, dataOutput, newState) -> {
+                    writeData(dataOutput, "key", "data".getBytes());
+                    writeState(newState, "newState".getBytes());
+                });
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+        runTask(task);
+
+        assertThat(Files.readAllBytes(getStateFile(mTransport, PM_PACKAGE)))
+                .isEqualTo("newState".getBytes());
+        assertCleansUpFiles(mTransport, PM_PACKAGE);
+        // We don't unbind PM
+        verify(mBackupManagerService, never()).unbindAgent(argThat(applicationInfo(PM_PACKAGE)));
+    }
+
+    @Test
+    public void testRunTask_whenFinishBackupSucceedsForPm_doesNotUnbindPm() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgent(PACKAGE_1);
+        when(transportMock.transport.finishBackup()).thenReturn(BackupTransport.TRANSPORT_OK);
+        BackupAgent pmAgent = spy(createPmAgent());
+        when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+        agentOnBackupDo(
+                pmAgent,
+                (oldState, dataOutput, newState) -> {
+                    writeData(dataOutput, "key", "data".getBytes());
+                    writeState(newState, "newState".getBytes());
+                });
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mBackupManagerService, never()).unbindAgent(argThat(applicationInfo(PM_PACKAGE)));
+    }
+
+    @Test
     public void testRunTask_whenFinishBackupSucceeds_logsBackupPackageEvent() throws Exception {
         TransportMock transportMock = setUpInitializedTransport(mTransport);
         setUpAgentWithData(PACKAGE_1);
@@ -1354,6 +1525,7 @@
     public void testRunTask_whenTransportReturnsQuotaExceeded_updatesBookkeeping()
             throws Exception {
         TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgentWithData(PACKAGE_1);
         when(transportMock.transport.performBackup(
                         argThat(packageInfo(PACKAGE_1)), any(), anyInt()))
                 .thenReturn(BackupTransport.TRANSPORT_QUOTA_EXCEEDED);
@@ -1701,9 +1873,9 @@
     }
 
     @Test
-    public void testRunTask_whenPmAgentFails() throws Exception {
+    public void testRunTask_whenPmAgentFails_reportsCorrectly() throws Exception {
         TransportMock transportMock = setUpInitializedTransport(mTransport);
-        PackageManagerBackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
+        BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
         when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
         KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
 
@@ -1718,6 +1890,75 @@
     }
 
     @Test
+    public void testRunTask_whenPmAgentFails_revertsTask() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgent(PACKAGE_1);
+        BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
+        when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+        runTask(task);
+
+        assertTaskReverted(transportMock, PACKAGE_1);
+    }
+
+    @Test
+    public void testRunTask_whenPmAgentFails_cleansUpFiles() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgent(PACKAGE_1);
+        BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
+        when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+        runTask(task);
+
+        assertCleansUpFiles(mTransport, PM_PACKAGE);
+    }
+
+    @Test
+    public void testRunTask_whenPmAgentFails_resetsBackupState() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgent(PACKAGE_1);
+        BackupAgent pmAgent = createThrowingPmAgent(new RuntimeException());
+        when(mBackupManagerService.makeMetadataAgent()).thenReturn(pmAgent);
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+
+        runTask(task);
+
+        verify(mBackupManagerService).resetBackupState(getStateDirectory(mTransport).toFile());
+    }
+
+    @Test
+    public void testRunTask_whenMarkCancelDuringPmOnBackup_resetsBackupState() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgent(PACKAGE_1);
+        BackupAgent pmAgent = spy(createPmAgent());
+        when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+        agentOnBackupDo(
+                pmAgent, (oldState, dataOutput, newState) -> runInWorkerThread(task::markCancel));
+
+        runTask(task);
+
+        verify(mBackupManagerService).resetBackupState(getStateDirectory(mTransport).toFile());
+    }
+
+    @Test
+    public void testRunTask_whenMarkCancelDuringPmOnBackup_cleansUpFiles() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        setUpAgent(PACKAGE_1);
+        BackupAgent pmAgent = spy(createPmAgent());
+        when(mBackupManagerService.makeMetadataAgent()).thenReturn(forward(pmAgent));
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+        agentOnBackupDo(
+                pmAgent, (oldState, dataOutput, newState) -> runInWorkerThread(task::markCancel));
+
+        runTask(task);
+
+        assertCleansUpFiles(mTransport, PM_PACKAGE);
+    }
+
+    @Test
     public void testRunTask_whenBackupRunning_doesNotThrow() throws Exception {
         TransportMock transportMock = setUpInitializedTransport(mTransport);
         when(mBackupManagerService.isBackupOperationInProgress()).thenReturn(true);
@@ -1736,7 +1977,7 @@
 
         runTask(task);
 
-        verify(mReporter).onReadAgentDataError(eq(PACKAGE_1.packageName), any());
+        verify(mReporter).onAgentDataError(eq(PACKAGE_1.packageName), any());
     }
 
     @Test
@@ -1779,6 +2020,24 @@
     }
 
     @Test
+    public void testRunTask_whenMarkCancelDuringAgentOnBackup_cleansUpFiles() throws Exception {
+        TransportMock transportMock = setUpInitializedTransport(mTransport);
+        AgentMock agentMock = setUpAgent(PACKAGE_1);
+        KeyValueBackupTask task = createKeyValueBackupTask(transportMock, PACKAGE_1);
+        agentOnBackupDo(
+                agentMock,
+                (oldState, dataOutput, newState) -> {
+                    writeData(dataOutput, "key", "data".getBytes());
+                    writeState(newState, "newState".getBytes());
+                    runInWorkerThread(task::markCancel);
+                });
+
+        runTask(task);
+
+        assertCleansUpFiles(mTransport, PACKAGE_1);
+    }
+
+    @Test
     public void
             testRunTask_whenMarkCancelDuringFirstAgentOnBackup_doesNotCallTransportAfterWaitCancel()
                     throws Exception {
@@ -2293,20 +2552,28 @@
      */
     private static void agentOnBackupDo(AgentMock agentMock, BackupAgentOnBackup function)
             throws Exception {
-        doAnswer(
-                        (BackupAgentOnBackup)
-                                (oldState, dataOutput, newState) -> {
-                                    ByteArrayOutputStream outputStream =
-                                            new ByteArrayOutputStream();
-                                    transferStreamedData(
-                                            new FileInputStream(oldState.getFileDescriptor()),
-                                            outputStream);
-                                    agentMock.oldState = outputStream.toByteArray();
-                                    agentMock.oldStateHistory.add(agentMock.oldState);
-                                    function.onBackup(oldState, dataOutput, newState);
-                                })
-                .when(agentMock.agent)
-                .onBackup(any(), any(), any());
+        agentOnBackupDo(
+                agentMock.agent,
+                (oldState, dataOutput, newState) -> {
+                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+                    transferStreamedData(
+                            new FileInputStream(oldState.getFileDescriptor()), outputStream);
+                    agentMock.oldState = outputStream.toByteArray();
+                    agentMock.oldStateHistory.add(agentMock.oldState);
+                    function.onBackup(oldState, dataOutput, newState);
+                });
+    }
+
+    /**
+     * Implements {@code function} for {@link BackupAgent#onBackup(ParcelFileDescriptor,
+     * BackupDataOutput, ParcelFileDescriptor)} of {@code agentMock}.
+     *
+     * @see #agentOnBackupDo(AgentMock, BackupAgentOnBackup)
+     * @see #remoteAgentOnBackupThrows(AgentMock, BackupAgentOnBackup)
+     */
+    private static void agentOnBackupDo(BackupAgent backupAgent, BackupAgentOnBackup function)
+            throws IOException {
+        doAnswer(function).when(backupAgent).onBackup(any(), any(), any());
     }
 
     /**
@@ -2400,6 +2667,10 @@
             // constructor
             assertJournalDoesNotContain(mBackupManagerService.getJournal(), packageName);
             assertThat(mBackupManagerService.getPendingBackups()).doesNotContainKey(packageName);
+            // Also verifying BMS is never called since for some cases the package wouldn't be
+            // pending for other reasons (for example it's not eligible for backup). Regardless of
+            // these reasons, we shouldn't mark them as pending backup (call dataChangedImpl()).
+            verify(mBackupManagerService, never()).dataChangedImpl(packageName);
         }
     }
 
diff --git a/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java b/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
new file mode 100644
index 0000000..4b79657
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/keyvalue/TaskExceptionTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 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.backup.keyvalue;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.expectThrows;
+
+import android.app.backup.BackupTransport;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import java.io.IOException;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class TaskExceptionTest {
+    @Test
+    public void testStateCompromised() {
+        TaskException exception = TaskException.stateCompromised();
+
+        assertThat(exception.isStateCompromised()).isTrue();
+        assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+    }
+
+    @Test
+    public void testStateCompromised_whenCauseInstanceOfTaskException() {
+        Exception cause = TaskException.forStatus(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+
+        TaskException exception = TaskException.stateCompromised(cause);
+
+        assertThat(exception.isStateCompromised()).isTrue();
+        assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+        assertThat(exception.getCause()).isEqualTo(cause);
+    }
+
+    @Test
+    public void testStateCompromised_whenCauseNotInstanceOfTaskException() {
+        Exception cause = new IOException();
+
+        TaskException exception = TaskException.stateCompromised(cause);
+
+        assertThat(exception.isStateCompromised()).isTrue();
+        assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+        assertThat(exception.getCause()).isEqualTo(cause);
+    }
+
+    @Test
+    public void testForStatus_whenTransportOk_throws() {
+        expectThrows(
+                IllegalArgumentException.class,
+                () -> TaskException.forStatus(BackupTransport.TRANSPORT_OK));
+    }
+
+    @Test
+    public void testForStatus_whenTransportNotInitialized() {
+        TaskException exception =
+                TaskException.forStatus(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+
+        assertThat(exception.isStateCompromised()).isFalse();
+        assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+    }
+
+    @Test
+    public void testCausedBy_whenCauseInstanceOfTaskException_returnsCause() {
+        Exception cause = TaskException.forStatus(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+
+        TaskException exception = TaskException.causedBy(cause);
+
+        assertThat(exception).isEqualTo(cause);
+    }
+
+    @Test
+    public void testCausedBy_whenCauseNotInstanceOfTaskException() {
+        Exception cause = new IOException();
+
+        TaskException exception = TaskException.causedBy(cause);
+
+        assertThat(exception).isNotEqualTo(cause);
+        assertThat(exception.isStateCompromised()).isFalse();
+        assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+        assertThat(exception.getCause()).isEqualTo(cause);
+    }
+
+    @Test
+    public void testCreate() {
+        TaskException exception = TaskException.create();
+
+        assertThat(exception.isStateCompromised()).isFalse();
+        assertThat(exception.getStatus()).isEqualTo(BackupTransport.TRANSPORT_ERROR);
+    }
+
+    @Test
+    public void testIsStateCompromised_whenStateCompromised_returnsTrue() {
+        TaskException taskException = TaskException.stateCompromised();
+
+        boolean stateCompromised = taskException.isStateCompromised();
+
+        assertThat(stateCompromised).isTrue();
+    }
+
+    @Test
+    public void testIsStateCompromised_whenCreatedWithCreate_returnsFalse() {
+        TaskException taskException = TaskException.create();
+
+        boolean stateCompromised = taskException.isStateCompromised();
+
+        assertThat(stateCompromised).isFalse();
+    }
+
+    @Test
+    public void testGetStatus_whenStatusIsTransportPackageRejected() {
+        TaskException taskException =
+                TaskException.forStatus(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+
+        int status = taskException.getStatus();
+
+        assertThat(status).isEqualTo(BackupTransport.TRANSPORT_PACKAGE_REJECTED);
+    }
+
+    @Test
+    public void testGetStatus_whenStatusIsTransportNotInitialized() {
+        TaskException taskException =
+                TaskException.forStatus(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+
+        int status = taskException.getStatus();
+
+        assertThat(status).isEqualTo(BackupTransport.TRANSPORT_NOT_INITIALIZED);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
index 06c7437..e8a824a 100644
--- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java
@@ -16,9 +16,12 @@
 
 package com.android.server.am;
 
+import static com.android.server.am.MemoryStatUtil.BYTES_IN_KILOBYTE;
 import static com.android.server.am.MemoryStatUtil.MemoryStat;
+import static com.android.server.am.MemoryStatUtil.parseMemoryMaxUsageFromMemCg;
 import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg;
 import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs;
+import static com.android.server.am.MemoryStatUtil.parseVmHWMFromProcfs;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
@@ -32,7 +35,7 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class MemoryStatUtilTest {
-    private String MEMORY_STAT_CONTENTS = String.join(
+    private static final String MEMORY_STAT_CONTENTS = String.join(
             "\n",
             "cache 96", // keep different from total_cache to catch reading wrong value
             "rss 97", // keep different from total_rss to catch reading wrong value
@@ -67,7 +70,7 @@
             "total_active_file 81920",
             "total_unevictable 0");
 
-    private String PROC_STAT_CONTENTS = String.join(
+    private static final String PROC_STAT_CONTENTS = String.join(
             " ",
             "1040",
             "(system_server)",
@@ -122,14 +125,61 @@
             "3198889956",
             "0");
 
+    private static final String PROC_STATUS_CONTENTS = "Name:\tandroid.youtube\n"
+        + "State:\tS (sleeping)\n"
+        + "Tgid:\t12088\n"
+        + "Pid:\t12088\n"
+        + "PPid:\t723\n"
+        + "TracerPid:\t0\n"
+        + "Uid:\t10083\t10083\t10083\t10083\n"
+        + "Gid:\t10083\t10083\t10083\t10083\n"
+        + "Ngid:\t0\n"
+        + "FDSize:\t128\n"
+        + "Groups:\t3003 9997 20083 50083 \n"
+        + "VmPeak:\t 4546844 kB\n"
+        + "VmSize:\t 4542636 kB\n"
+        + "VmLck:\t       0 kB\n"
+        + "VmPin:\t       0 kB\n"
+        + "VmHWM:\t  137668 kB\n" // RSS high watermark
+        + "VmRSS:\t  126776 kB\n"
+        + "RssAnon:\t   37860 kB\n"
+        + "RssFile:\t   88764 kB\n"
+        + "RssShmem:\t     152 kB\n"
+        + "VmData:\t 4125112 kB\n"
+        + "VmStk:\t    8192 kB\n"
+        + "VmExe:\t      24 kB\n"
+        + "VmLib:\t  102432 kB\n"
+        + "VmPTE:\t    1300 kB\n"
+        + "VmPMD:\t      36 kB\n"
+        + "VmSwap:\t       0 kB\n"
+        + "Threads:\t95\n"
+        + "SigQ:\t0/13641\n"
+        + "SigPnd:\t0000000000000000\n"
+        + "ShdPnd:\t0000000000000000\n"
+        + "SigBlk:\t0000000000001204\n"
+        + "SigIgn:\t0000000000000001\n"
+        + "SigCgt:\t00000006400084f8\n"
+        + "CapInh:\t0000000000000000\n"
+        + "CapPrm:\t0000000000000000\n"
+        + "CapEff:\t0000000000000000\n"
+        + "CapBnd:\t0000000000000000\n"
+        + "CapAmb:\t0000000000000000\n"
+        + "Seccomp:\t2\n"
+        + "Cpus_allowed:\tff\n"
+        + "Cpus_allowed_list:\t0-7\n"
+        + "Mems_allowed:\t1\n"
+        + "Mems_allowed_list:\t0\n"
+        + "voluntary_ctxt_switches:\t903\n"
+        + "nonvoluntary_ctxt_switches:\t104\n";
+
     @Test
     public void testParseMemoryStatFromMemcg_parsesCorrectValues() throws Exception {
         MemoryStat stat = parseMemoryStatFromMemcg(MEMORY_STAT_CONTENTS);
-        assertEquals(stat.pgfault, 1);
-        assertEquals(stat.pgmajfault, 2);
-        assertEquals(stat.rssInBytes, 3);
-        assertEquals(stat.cacheInBytes, 4);
-        assertEquals(stat.swapInBytes, 5);
+        assertEquals(1, stat.pgfault);
+        assertEquals(2, stat.pgmajfault);
+        assertEquals(3, stat.rssInBytes);
+        assertEquals(4, stat.cacheInBytes);
+        assertEquals(5, stat.swapInBytes);
     }
 
     @Test
@@ -142,6 +192,18 @@
     }
 
     @Test
+    public void testParseMemoryMaxUsageFromMemCg_parsesCorrectValue() {
+        assertEquals(1234, parseMemoryMaxUsageFromMemCg("1234"));
+    }
+
+    @Test
+    public void testParseMemoryMaxUsageFromMemCg_emptyContents() {
+        assertEquals(0, parseMemoryMaxUsageFromMemCg(""));
+
+        assertEquals(0, parseMemoryMaxUsageFromMemCg(null));
+    }
+
+    @Test
     public void testParseMemoryStatFromProcfs_parsesCorrectValues() throws Exception {
         MemoryStat stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS);
         assertEquals(1, stat.pgfault);
@@ -159,4 +221,16 @@
         stat = parseMemoryStatFromProcfs(null);
         assertNull(stat);
     }
+
+    @Test
+    public void testParseVmHWMFromProcfs_parsesCorrectValue() {
+        assertEquals(137668, parseVmHWMFromProcfs(PROC_STATUS_CONTENTS) / BYTES_IN_KILOBYTE);
+    }
+
+    @Test
+    public void testParseVmHWMFromProcfs_emptyContents() {
+        assertEquals(0, parseVmHWMFromProcfs(""));
+
+        assertEquals(0, parseVmHWMFromProcfs(null));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index fa65212..b7cc9ce 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -370,22 +370,31 @@
         final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
 
         app.mHasSurface = true;
-        app.mToken.mSurfaceControl = mock(SurfaceControl.class);
+        app.mSurfaceControl = mock(SurfaceControl.class);
         try {
             app.getFrameLw().set(10, 20, 60, 80);
+            app.updateSurfacePosition(t);
 
             app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_90, true);
 
             assertTrue(app.mSeamlesslyRotated);
+
+            // Verify we un-rotate the window state surface.
             Matrix matrix = new Matrix();
             // Un-rotate 90 deg
             matrix.setRotate(270);
             // Translate it back to origin
             matrix.postTranslate(0, mDisplayInfo.logicalWidth);
-            verify(t).setMatrix(eq(app.mToken.mSurfaceControl), eq(matrix), any(float[].class));
+            verify(t).setMatrix(eq(app.mSurfaceControl), eq(matrix), any(float[].class));
+
+            // Verify we update the position as well.
+            float[] currentSurfacePos = {app.mLastSurfacePosition.x, app.mLastSurfacePosition.y};
+            matrix.mapPoints(currentSurfacePos);
+            verify(t).setPosition(eq(app.mSurfaceControl), eq(currentSurfacePos[0]),
+                    eq(currentSurfacePos[1]));
         } finally {
+            app.mSurfaceControl = null;
             app.mHasSurface = false;
-            app.mToken.mSurfaceControl = null;
         }
     }
 
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 26bd4a1..08bc9bc 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -141,6 +141,8 @@
      * The caller must specify the {@link #EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE} to indicate to
      * Telecom which {@link PhoneAccountHandle} the {@link Call} should be handed over to.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EVENT_REQUEST_HANDOVER =
             "android.telecom.event.REQUEST_HANDOVER";
@@ -149,6 +151,8 @@
      * Extra key used with the {@link #EVENT_REQUEST_HANDOVER} call event.  Specifies the
      * {@link PhoneAccountHandle} to which a call should be handed over to.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE =
             "android.telecom.extra.HANDOVER_PHONE_ACCOUNT_HANDLE";
@@ -161,6 +165,8 @@
      * {@link VideoProfile#STATE_BIDIRECTIONAL}, {@link VideoProfile#STATE_RX_ENABLED}, and
      * {@link VideoProfile#STATE_TX_ENABLED}.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EXTRA_HANDOVER_VIDEO_STATE =
             "android.telecom.extra.HANDOVER_VIDEO_STATE";
@@ -176,6 +182,8 @@
      * {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}
      * is called to initate the handover.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EXTRA_HANDOVER_EXTRAS = "android.telecom.extra.HANDOVER_EXTRAS";
 
@@ -186,6 +194,8 @@
      * <p>
      * A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EVENT_HANDOVER_COMPLETE =
             "android.telecom.event.HANDOVER_COMPLETE";
@@ -198,6 +208,8 @@
      * <p>
      * A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EVENT_HANDOVER_SOURCE_DISCONNECTED =
             "android.telecom.event.HANDOVER_SOURCE_DISCONNECTED";
@@ -209,6 +221,8 @@
      * <p>
      * A handover is initiated with the {@link #EVENT_REQUEST_HANDOVER} call event.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EVENT_HANDOVER_FAILED =
             "android.telecom.event.HANDOVER_FAILED";
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index d494692..34603a3 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -594,6 +594,8 @@
      * {@link Call#EVENT_REQUEST_HANDOVER} that the handover from this {@link Connection} has
      * successfully completed.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EVENT_HANDOVER_COMPLETE =
             "android.telecom.event.HANDOVER_COMPLETE";
@@ -603,6 +605,8 @@
      * {@link Call#EVENT_REQUEST_HANDOVER} that the handover from this {@link Connection} has failed
      * to complete.
      * @hide
+     * @deprecated Use {@link Call#handoverTo(PhoneAccountHandle, int, Bundle)} and its associated
+     * APIs instead.
      */
     public static final String EVENT_HANDOVER_FAILED =
             "android.telecom.event.HANDOVER_FAILED";
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 777b850..38ee79f 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1852,6 +1852,19 @@
     }
 
     /**
+     * Checks if the supplied subscription ID corresponds to an active subscription.
+     *
+     * @param subscriptionId the subscription ID.
+     * @return {@code true} if the supplied subscription ID corresponds to an active subscription;
+     * {@code false} if it does not correspond to an active subscription; or throw a
+     * SecurityException if the caller hasn't got the right permission.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public boolean isActiveSubscriptionId(int subscriptionId) {
+        return isActiveSubId(subscriptionId);
+    }
+
+    /**
      * @return true if the sub ID is active. i.e. The sub ID corresponds to a known subscription
      * and the SIM providing the subscription is present in a slot and in "LOADED" state.
      * @hide
@@ -1861,7 +1874,7 @@
         try {
             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
             if (iSub != null) {
-                return iSub.isActiveSubId(subId);
+                return iSub.isActiveSubId(subId, mContext.getOpPackageName());
             }
         } catch (RemoteException ex) {
         }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d58b932..b80b54a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import static android.content.Context.TELECOM_SERVICE;
+
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.annotation.IntDef;
@@ -4372,7 +4374,7 @@
     * @hide
     */
     private ITelecomService getTelecomService() {
-        return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));
+        return ITelecomService.Stub.asInterface(ServiceManager.getService(TELECOM_SERVICE));
     }
 
     private ITelephonyRegistry getTelephonyRegistry() {
@@ -5402,7 +5404,19 @@
         }
     }
 
-    // ICC SIM Application Types
+    /**
+     * UICC SIM Application Types
+     * @hide
+     */
+    @IntDef(prefix = { "APPTYPE_" }, value = {
+            APPTYPE_SIM,
+            APPTYPE_USIM,
+            APPTYPE_RUIM,
+            APPTYPE_CSIM,
+            APPTYPE_ISIM
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UiccAppType{}
     /** UICC application type is SIM */
     public static final int APPTYPE_SIM = PhoneConstants.APPTYPE_SIM;
     /** UICC application type is USIM */
@@ -5413,6 +5427,7 @@
     public static final int APPTYPE_CSIM = PhoneConstants.APPTYPE_CSIM;
     /** UICC application type is ISIM */
     public static final int APPTYPE_ISIM = PhoneConstants.APPTYPE_ISIM;
+
     // authContext (parameter P2) when doing UICC challenge,
     // per 3GPP TS 31.102 (Section 7.1.2)
     /** Authentication type for UICC challenge is EAP SIM. See RFC 4186 for details. */
@@ -5679,6 +5694,202 @@
         }
     }
 
+    /** @hide */
+    @IntDef(prefix = { "NETWORK_MODE_" }, value = {
+            NETWORK_MODE_WCDMA_PREF,
+            NETWORK_MODE_GSM_ONLY,
+            NETWORK_MODE_WCDMA_ONLY,
+            NETWORK_MODE_GSM_UMTS,
+            NETWORK_MODE_CDMA_EVDO,
+            NETWORK_MODE_CDMA_NO_EVDO,
+            NETWORK_MODE_EVDO_NO_CDMA,
+            NETWORK_MODE_GLOBAL,
+            NETWORK_MODE_LTE_CDMA_EVDO,
+            NETWORK_MODE_LTE_GSM_WCDMA,
+            NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA,
+            NETWORK_MODE_LTE_ONLY,
+            NETWORK_MODE_LTE_WCDMA,
+            NETWORK_MODE_TDSCDMA_ONLY,
+            NETWORK_MODE_TDSCDMA_WCDMA,
+            NETWORK_MODE_LTE_TDSCDMA,
+            NETWORK_MODE_TDSCDMA_GSM,
+            NETWORK_MODE_LTE_TDSCDMA_GSM,
+            NETWORK_MODE_TDSCDMA_GSM_WCDMA,
+            NETWORK_MODE_LTE_TDSCDMA_WCDMA,
+            NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA,
+            NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA,
+            NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PrefNetworkMode{}
+
+    /**
+     * preferred network mode is GSM/WCDMA (WCDMA preferred).
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_WCDMA_PREF = RILConstants.NETWORK_MODE_WCDMA_PREF;
+
+    /**
+     * preferred network mode is GSM only.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_GSM_ONLY = RILConstants.NETWORK_MODE_GSM_ONLY;
+
+    /**
+     * preferred network mode is WCDMA only.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_WCDMA_ONLY = RILConstants.NETWORK_MODE_WCDMA_ONLY;
+
+    /**
+     * preferred network mode is GSM/WCDMA (auto mode, according to PRL).
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_GSM_UMTS = RILConstants.NETWORK_MODE_GSM_UMTS;
+
+    /**
+     * preferred network mode is CDMA and EvDo (auto mode, according to PRL).
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_CDMA_EVDO = RILConstants.NETWORK_MODE_CDMA;
+
+    /**
+     * preferred network mode is CDMA only.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_CDMA_NO_EVDO = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
+
+    /**
+     * preferred network mode is EvDo only.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_EVDO_NO_CDMA = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
+
+    /**
+     * preferred network mode is GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL).
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_GLOBAL = RILConstants.NETWORK_MODE_GLOBAL;
+
+    /**
+     * preferred network mode is LTE, CDMA and EvDo.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_CDMA_EVDO = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
+
+    /**
+     * preferred network mode is LTE, GSM/WCDMA.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_GSM_WCDMA = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
+
+    /**
+     * preferred network mode is LTE, CDMA, EvDo, GSM/WCDMA.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
+
+    /**
+     * preferred network mode is LTE Only.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_ONLY = RILConstants.NETWORK_MODE_LTE_ONLY;
+
+    /**
+     * preferred network mode is LTE/WCDMA.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_WCDMA = RILConstants.NETWORK_MODE_LTE_WCDMA;
+
+    /**
+     * preferred network mode is TD-SCDMA only.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_TDSCDMA_ONLY = RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
+
+    /**
+     * preferred network mode is TD-SCDMA and WCDMA.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_TDSCDMA_WCDMA = RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
+
+    /**
+     * preferred network mode is TD-SCDMA and LTE.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_TDSCDMA = RILConstants.NETWORK_MODE_LTE_TDSCDMA;
+
+    /**
+     * preferred network mode is TD-SCDMA and GSM.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_TDSCDMA_GSM = RILConstants.NETWORK_MODE_TDSCDMA_GSM;
+
+    /**
+     * preferred network mode is TD-SCDMA,GSM and LTE.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_TDSCDMA_GSM =
+            RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
+
+    /**
+     * preferred network mode is TD-SCDMA, GSM/WCDMA.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_TDSCDMA_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
+
+    /**
+     * preferred network mode is TD-SCDMA, WCDMA and LTE.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_TDSCDMA_WCDMA =
+            RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
+
+    /**
+     * preferred network mode is TD-SCDMA, GSM/WCDMA and LTE.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
+
+    /**
+     * preferred network mode is TD-SCDMA,EvDo,CDMA,GSM/WCDMA.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+    /**
+     * preferred network mode is TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo.
+     * @hide
+     */
+    @SystemApi
+    public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
+            RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+
     /**
      * Get the preferred network type.
      * Used for device configuration by some CDMA operators.
@@ -5687,11 +5898,12 @@
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
-     * @return the preferred network type, defined in RILConstants.java.
+     * @return the preferred network type.
      * @hide
      */
-    @UnsupportedAppUsage
-    public int getPreferredNetworkType(int subId) {
+    @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE))
+    @SystemApi
+    public @PrefNetworkMode int getPreferredNetworkType(int subId) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
@@ -6273,7 +6485,7 @@
     }
 
     /**
-     * @deprecated Use {@link android.telecom.TelecomManager#endCall()} instead.
+     * @removed Use {@link android.telecom.TelecomManager#endCall()} instead.
      * @hide
      * @removed
      */
@@ -6285,7 +6497,7 @@
     }
 
     /**
-     * @deprecated Use {@link android.telecom.TelecomManager#acceptRingingCall} instead
+     * @removed Use {@link android.telecom.TelecomManager#acceptRingingCall} instead
      * @hide
      * @removed
      */
@@ -6293,26 +6505,22 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void answerRingingCall() {
-
+        // No-op
     }
 
     /**
-     * @deprecated Use {@link android.telecom.TelecomManager#silenceRinger} instead
+     * @removed Use {@link android.telecom.TelecomManager#silenceRinger} instead
      * @hide
      */
     @Deprecated
     @SystemApi
     @SuppressLint("Doclava125")
     public void silenceRinger() {
-        try {
-            getTelecomService().silenceRinger(getOpPackageName());
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelecomService#silenceRinger", e);
-        }
+        // No-op
     }
 
     /**
-     * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
+     * @removed Use {@link android.telecom.TelecomManager#isInCall} instead
      * @hide
      */
     @Deprecated
@@ -6322,18 +6530,11 @@
             android.Manifest.permission.READ_PHONE_STATE
     })
     public boolean isOffhook() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                return telephony.isOffhook(getOpPackageName());
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#isOffhook", e);
-        }
         return false;
     }
 
     /**
-     * @deprecated Use {@link android.telecom.TelecomManager#isRinging} instead
+     * @removed Use {@link android.telecom.TelecomManager#isRinging} instead
      * @hide
      */
     @Deprecated
@@ -6343,18 +6544,11 @@
             android.Manifest.permission.READ_PHONE_STATE
     })
     public boolean isRinging() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                return telephony.isRinging(getOpPackageName());
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#isRinging", e);
-        }
         return false;
     }
 
     /**
-     * @deprecated Use {@link android.telecom.TelecomManager#isInCall} instead
+     * @removed Use {@link android.telecom.TelecomManager#isInCall} instead
      * @hide
      */
     @Deprecated
@@ -6364,13 +6558,6 @@
             android.Manifest.permission.READ_PHONE_STATE
     })
     public boolean isIdle() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                return telephony.isIdle(getOpPackageName());
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#isIdle", e);
-        }
         return true;
     }
 
@@ -7856,26 +8043,23 @@
     }
 
     /**
-     * Return the application ID for the app type like {@link APPTYPE_CSIM}.
+     * Return the application ID for the uicc application type like {@link #APPTYPE_CSIM}.
+     * All uicc applications are uniquely identified by application ID. See ETSI 102.221 and 101.220
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
      *
-     * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
-     *
-     * @param appType the uicc app type like {@link APPTYPE_CSIM}
-     * @return Application ID for specificied app type or null if no uicc or error.
+     * @param appType the uicc app type.
+     * @return Application ID for specified app type or {@code null} if no uicc or error.
      * @hide
      */
-    public String getAidForAppType(int appType) {
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public String getAidForAppType(@UiccAppType int appType) {
         return getAidForAppType(getSubId(), appType);
     }
 
     /**
-     * Return the application ID for the app type like {@link APPTYPE_CSIM}.
-     *
-     * Requires that the calling app has READ_PRIVILEGED_PHONE_STATE permission
-     *
-     * @param subId the subscription ID that this request applies to.
-     * @param appType the uicc app type, like {@link APPTYPE_CSIM}
-     * @return Application ID for specificied app type or null if no uicc or error.
+     * same as {@link #getAidForAppType(int)}
      * @hide
      */
     public String getAidForAppType(int subId, int appType) {
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 6521f0b..0ccd748 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -232,5 +232,5 @@
      */
     int getSimStateForSlotIndex(int slotIndex);
 
-    boolean isActiveSubId(int subId);
+    boolean isActiveSubId(int subId, String callingPackage);
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index e1c770c..ca2bcff 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -75,116 +75,6 @@
     void call(String callingPackage, String number);
 
     /**
-     * End call if there is a call in progress, otherwise does nothing.
-     *
-     * @return whether it hung up
-     */
-    boolean endCall();
-
-    /**
-     * End call on particular subId or go to the Home screen
-     * @param subId user preferred subId.
-     * @return whether it hung up
-     */
-    boolean endCallForSubscriber(int subId);
-
-    /**
-     * Answer the currently-ringing call.
-     *
-     * If there's already a current active call, that call will be
-     * automatically put on hold.  If both lines are currently in use, the
-     * current active call will be ended.
-     *
-     * TODO: provide a flag to let the caller specify what policy to use
-     * if both lines are in use.  (The current behavior is hardwired to
-     * "answer incoming, end ongoing", which is how the CALL button
-     * is specced to behave.)
-     *
-     * TODO: this should be a oneway call (especially since it's called
-     * directly from the key queue thread).
-     */
-    void answerRingingCall();
-
-    /**
-     * Answer the currently-ringing call on particular subId .
-     *
-     * If there's already a current active call, that call will be
-     * automatically put on hold.  If both lines are currently in use, the
-     * current active call will be ended.
-     *
-     * TODO: provide a flag to let the caller specify what policy to use
-     * if both lines are in use.  (The current behavior is hardwired to
-     * "answer incoming, end ongoing", which is how the CALL button
-     * is specced to behave.)
-     *
-     * TODO: this should be a oneway call (especially since it's called
-     * directly from the key queue thread).
-     */
-    void answerRingingCallForSubscriber(int subId);
-
-    /**
-     * Silence the ringer if an incoming call is currently ringing.
-     * (If vibrating, stop the vibrator also.)
-     *
-     * It's safe to call this if the ringer has already been silenced, or
-     * even if there's no incoming call.  (If so, this method will do nothing.)
-     *
-     * TODO: this should be a oneway call too (see above).
-     *       (Actually *all* the methods here that return void can
-     *       probably be oneway.)
-     */
-    void silenceRinger();
-
-    /**
-     * Check if we are in either an active or holding call
-     * @param callingPackage the name of the package making the call.
-     * @return true if the phone state is OFFHOOK.
-     */
-    boolean isOffhook(String callingPackage);
-
-    /**
-     * Check if a particular subId has an active or holding call
-     *
-     * @param subId user preferred subId.
-     * @param callingPackage the name of the package making the call.
-     * @return true if the phone state is OFFHOOK.
-     */
-    boolean isOffhookForSubscriber(int subId, String callingPackage);
-
-    /**
-     * Check if an incoming phone call is ringing or call waiting
-     * on a particular subId.
-     *
-     * @param subId user preferred subId.
-     * @param callingPackage the name of the package making the call.
-     * @return true if the phone state is RINGING.
-     */
-    boolean isRingingForSubscriber(int subId, String callingPackage);
-
-    /**
-     * Check if an incoming phone call is ringing or call waiting.
-     * @param callingPackage the name of the package making the call.
-     * @return true if the phone state is RINGING.
-     */
-    boolean isRinging(String callingPackage);
-
-    /**
-     * Check if the phone is idle.
-     * @param callingPackage the name of the package making the call.
-     * @return true if the phone state is IDLE.
-     */
-    boolean isIdle(String callingPackage);
-
-    /**
-     * Check if the phone is idle on a particular subId.
-     *
-     * @param subId user preferred subId.
-     * @param callingPackage the name of the package making the call.
-     * @return true if the phone state is IDLE.
-     */
-    boolean isIdleForSubscriber(int subId, String callingPackage);
-
-    /**
      * Check to see if the radio is on or not.
      * @param callingPackage the name of the package making the call.
      * @return returns true if the radio is on.
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 4a6fe49..1f60b71 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -247,8 +247,14 @@
                     mIterationCycle = false;
                     // In the "applaunch.txt" file, trail launches is referenced using
                     // "TRIAL_LAUNCH"
-                    String appPkgName = mNameToIntent.get(launch.getApp())
-                        .getComponent().getPackageName();
+                    Intent startIntent = mNameToIntent.get(launch.getApp());
+                    if (startIntent == null) {
+                        Log.w(TAG, "App does not exist: " + launch.getApp());
+                        mResult.putString(mNameToResultKey.get(launch.getApp()),
+                            "App does not exist");
+                        continue;
+                    }
+                    String appPkgName = startIntent.getComponent().getPackageName();
                     if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) {
                         assertTrue(String.format("Not able to compile the app : %s", appPkgName),
                               compileApp(VERIFY_FILTER, appPkgName));
diff --git a/tools/aapt2/io/FileStream_test.cpp b/tools/aapt2/io/FileStream_test.cpp
index c0eaa8e..7872738 100644
--- a/tools/aapt2/io/FileStream_test.cpp
+++ b/tools/aapt2/io/FileStream_test.cpp
@@ -41,46 +41,46 @@
   ASSERT_FALSE(in.HadError());
   EXPECT_THAT(in.ByteCount(), Eq(0u));
 
-  const char* buffer;
+  const void* buffer;
   size_t size;
-  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size)) << in.GetError();
+  ASSERT_TRUE(in.Next(&buffer, &size)) << in.GetError();
   ASSERT_THAT(size, Eq(10u));
   ASSERT_THAT(buffer, NotNull());
   EXPECT_THAT(in.ByteCount(), Eq(10u));
-  EXPECT_THAT(StringPiece(buffer, size), Eq("this is a "));
+  EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("this is a "));
 
-  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  ASSERT_TRUE(in.Next(&buffer, &size));
   ASSERT_THAT(size, Eq(10u));
   ASSERT_THAT(buffer, NotNull());
   EXPECT_THAT(in.ByteCount(), Eq(20u));
-  EXPECT_THAT(StringPiece(buffer, size), Eq("cool strin"));
+  EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("cool strin"));
 
   in.BackUp(5u);
   EXPECT_THAT(in.ByteCount(), Eq(15u));
 
-  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  ASSERT_TRUE(in.Next(&buffer, &size));
   ASSERT_THAT(size, Eq(5u));
   ASSERT_THAT(buffer, NotNull());
   ASSERT_THAT(in.ByteCount(), Eq(20u));
-  EXPECT_THAT(StringPiece(buffer, size), Eq("strin"));
+  EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("strin"));
 
   // Backup 1 more than possible. Should clamp.
   in.BackUp(11u);
   EXPECT_THAT(in.ByteCount(), Eq(10u));
 
-  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  ASSERT_TRUE(in.Next(&buffer, &size));
   ASSERT_THAT(size, Eq(10u));
   ASSERT_THAT(buffer, NotNull());
   ASSERT_THAT(in.ByteCount(), Eq(20u));
-  EXPECT_THAT(StringPiece(buffer, size), Eq("cool strin"));
+  EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("cool strin"));
 
-  ASSERT_TRUE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  ASSERT_TRUE(in.Next(&buffer, &size));
   ASSERT_THAT(size, Eq(1u));
   ASSERT_THAT(buffer, NotNull());
   ASSERT_THAT(in.ByteCount(), Eq(21u));
-  EXPECT_THAT(StringPiece(buffer, size), Eq("g"));
+  EXPECT_THAT(StringPiece(reinterpret_cast<const char*>(buffer), size), Eq("g"));
 
-  EXPECT_FALSE(in.Next(reinterpret_cast<const void**>(&buffer), &size));
+  EXPECT_FALSE(in.Next(&buffer, &size));
   EXPECT_FALSE(in.HadError());
 }
 
@@ -93,25 +93,25 @@
   ASSERT_FALSE(out.HadError());
   EXPECT_THAT(out.ByteCount(), Eq(0u));
 
-  char* buffer;
+  void* buffer;
   size_t size;
-  ASSERT_TRUE(out.Next(reinterpret_cast<void**>(&buffer), &size));
+  ASSERT_TRUE(out.Next(&buffer, &size));
   ASSERT_THAT(size, Eq(10u));
   ASSERT_THAT(buffer, NotNull());
   EXPECT_THAT(out.ByteCount(), Eq(10u));
-  memcpy(buffer, input.c_str(), size);
+  memcpy(reinterpret_cast<char*>(buffer), input.c_str(), size);
 
-  ASSERT_TRUE(out.Next(reinterpret_cast<void**>(&buffer), &size));
+  ASSERT_TRUE(out.Next(&buffer, &size));
   ASSERT_THAT(size, Eq(10u));
   ASSERT_THAT(buffer, NotNull());
   EXPECT_THAT(out.ByteCount(), Eq(20u));
-  memcpy(buffer, input.c_str() + 10u, size);
+  memcpy(reinterpret_cast<char*>(buffer), input.c_str() + 10u, size);
 
-  ASSERT_TRUE(out.Next(reinterpret_cast<void**>(&buffer), &size));
+  ASSERT_TRUE(out.Next(&buffer, &size));
   ASSERT_THAT(size, Eq(10u));
   ASSERT_THAT(buffer, NotNull());
   EXPECT_THAT(out.ByteCount(), Eq(30u));
-  buffer[0] = input[20u];
+  reinterpret_cast<char*>(buffer)[0] = input[20u];
   out.BackUp(size - 1);
   EXPECT_THAT(out.ByteCount(), Eq(21u));