Merge "DisplayCutout: Cache inflations from resources"
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
index 2c84db1..40778de 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
@@ -150,6 +150,8 @@
         final Bundle status = new Bundle();
         status.putLong(key + "_median", mStats.getMedian());
         status.putLong(key + "_mean", (long) mStats.getMean());
+        status.putLong(key + "_percentile90", mStats.getPercentile90());
+        status.putLong(key + "_percentile95", mStats.getPercentile95());
         status.putLong(key + "_stddev", (long) mStats.getStandardDeviation());
         instrumentation.sendStatus(Activity.RESULT_OK, status);
     }
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java b/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java
index acc44a8..5e50073 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java
@@ -21,7 +21,7 @@
 import java.util.List;
 
 public class Stats {
-    private long mMedian, mMin, mMax;
+    private long mMedian, mMin, mMax, mPercentile90, mPercentile95;
     private double mMean, mStandardDeviation;
 
     /* Calculate stats in constructor. */
@@ -35,12 +35,14 @@
 
         Collections.sort(values);
 
-        mMedian = size % 2 == 0 ? (values.get(size / 2) + values.get(size / 2 - 1)) / 2 :
-                values.get(size / 2);
-
         mMin = values.get(0);
         mMax = values.get(values.size() - 1);
 
+        mMedian = size % 2 == 0 ? (values.get(size / 2) + values.get(size / 2 - 1)) / 2 :
+                values.get(size / 2);
+        mPercentile90 = getPercentile(values, 90);
+        mPercentile95 = getPercentile(values, 95);
+
         for (int i = 0; i < size; ++i) {
             long result = values.get(i);
             mMean += result;
@@ -73,4 +75,21 @@
     public double getStandardDeviation() {
         return mStandardDeviation;
     }
+
+    public long getPercentile90() {
+        return mPercentile90;
+    }
+
+    public long getPercentile95() {
+        return mPercentile95;
+    }
+
+    private static long getPercentile(List<Long> values, int percentile) {
+        if (percentile < 0 || percentile > 100) {
+            throw new IllegalArgumentException(
+                    "invalid percentile " + percentile + ", should be 0-100");
+        }
+        int idx = (values.size() - 1) * percentile / 100;
+        return values.get(idx);
+    }
 }
diff --git a/api/current.txt b/api/current.txt
index 453eac5..bc2d893 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4070,7 +4070,7 @@
     method public android.app.ActivityOptions setAppVerificationBundle(android.os.Bundle);
     method public android.app.ActivityOptions setLaunchBounds(android.graphics.Rect);
     method public android.app.ActivityOptions setLaunchDisplayId(int);
-    method public android.app.ActivityOptions setLockTaskMode(boolean);
+    method public android.app.ActivityOptions setLockTaskEnabled(boolean);
     method public android.os.Bundle toBundle();
     method public void update(android.app.ActivityOptions);
     field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
@@ -5349,18 +5349,18 @@
     ctor public Notification.Action.WearableExtender(android.app.Notification.Action);
     method public android.app.Notification.Action.WearableExtender clone();
     method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
-    method public java.lang.CharSequence getCancelLabel();
-    method public java.lang.CharSequence getConfirmLabel();
+    method public deprecated java.lang.CharSequence getCancelLabel();
+    method public deprecated java.lang.CharSequence getConfirmLabel();
     method public boolean getHintDisplayActionInline();
     method public boolean getHintLaunchesActivity();
-    method public java.lang.CharSequence getInProgressLabel();
+    method public deprecated java.lang.CharSequence getInProgressLabel();
     method public boolean isAvailableOffline();
     method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
-    method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
-    method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
+    method public deprecated android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
+    method public deprecated android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
     method public android.app.Notification.Action.WearableExtender setHintDisplayActionInline(boolean);
     method public android.app.Notification.Action.WearableExtender setHintLaunchesActivity(boolean);
-    method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
+    method public deprecated android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
   }
 
   public static class Notification.BigPictureStyle extends android.app.Notification.Style {
@@ -5583,39 +5583,39 @@
     method public android.graphics.Bitmap getBackground();
     method public java.lang.String getBridgeTag();
     method public int getContentAction();
-    method public int getContentIcon();
-    method public int getContentIconGravity();
+    method public deprecated int getContentIcon();
+    method public deprecated int getContentIconGravity();
     method public boolean getContentIntentAvailableOffline();
-    method public int getCustomContentHeight();
-    method public int getCustomSizePreset();
+    method public deprecated int getCustomContentHeight();
+    method public deprecated int getCustomSizePreset();
     method public java.lang.String getDismissalId();
     method public android.app.PendingIntent getDisplayIntent();
-    method public int getGravity();
+    method public deprecated int getGravity();
     method public boolean getHintAmbientBigPicture();
-    method public boolean getHintAvoidBackgroundClipping();
+    method public deprecated boolean getHintAvoidBackgroundClipping();
     method public boolean getHintContentIntentLaunchesActivity();
-    method public boolean getHintHideIcon();
-    method public int getHintScreenTimeout();
-    method public boolean getHintShowBackgroundOnly();
+    method public deprecated boolean getHintHideIcon();
+    method public deprecated int getHintScreenTimeout();
+    method public deprecated boolean getHintShowBackgroundOnly();
     method public java.util.List<android.app.Notification> getPages();
     method public boolean getStartScrollBottom();
     method public android.app.Notification.WearableExtender setBackground(android.graphics.Bitmap);
     method public android.app.Notification.WearableExtender setBridgeTag(java.lang.String);
     method public android.app.Notification.WearableExtender setContentAction(int);
-    method public android.app.Notification.WearableExtender setContentIcon(int);
-    method public android.app.Notification.WearableExtender setContentIconGravity(int);
+    method public deprecated android.app.Notification.WearableExtender setContentIcon(int);
+    method public deprecated android.app.Notification.WearableExtender setContentIconGravity(int);
     method public android.app.Notification.WearableExtender setContentIntentAvailableOffline(boolean);
-    method public android.app.Notification.WearableExtender setCustomContentHeight(int);
-    method public android.app.Notification.WearableExtender setCustomSizePreset(int);
+    method public deprecated android.app.Notification.WearableExtender setCustomContentHeight(int);
+    method public deprecated android.app.Notification.WearableExtender setCustomSizePreset(int);
     method public android.app.Notification.WearableExtender setDismissalId(java.lang.String);
     method public android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
-    method public android.app.Notification.WearableExtender setGravity(int);
+    method public deprecated android.app.Notification.WearableExtender setGravity(int);
     method public android.app.Notification.WearableExtender setHintAmbientBigPicture(boolean);
-    method public android.app.Notification.WearableExtender setHintAvoidBackgroundClipping(boolean);
+    method public deprecated android.app.Notification.WearableExtender setHintAvoidBackgroundClipping(boolean);
     method public android.app.Notification.WearableExtender setHintContentIntentLaunchesActivity(boolean);
-    method public android.app.Notification.WearableExtender setHintHideIcon(boolean);
-    method public android.app.Notification.WearableExtender setHintScreenTimeout(int);
-    method public android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
+    method public deprecated android.app.Notification.WearableExtender setHintHideIcon(boolean);
+    method public deprecated android.app.Notification.WearableExtender setHintScreenTimeout(int);
+    method public deprecated android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
     method public android.app.Notification.WearableExtender setStartScrollBottom(boolean);
     field public static final int SCREEN_TIMEOUT_LONG = -1; // 0xffffffff
     field public static final int SCREEN_TIMEOUT_SHORT = 0; // 0x0
@@ -6678,7 +6678,7 @@
     field public static final int LOCK_TASK_FEATURE_KEYGUARD = 32; // 0x20
     field public static final int LOCK_TASK_FEATURE_NONE = 0; // 0x0
     field public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = 2; // 0x2
-    field public static final int LOCK_TASK_FEATURE_RECENTS = 8; // 0x8
+    field public static final int LOCK_TASK_FEATURE_OVERVIEW = 8; // 0x8
     field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1
     field public static final int MAKE_USER_EPHEMERAL = 2; // 0x2
     field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
@@ -11389,6 +11389,8 @@
     ctor public PermissionInfo();
     ctor public PermissionInfo(android.content.pm.PermissionInfo);
     method public int describeContents();
+    method public int getProtection();
+    method public int getProtectionFlags();
     method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager);
     field public static final android.os.Parcelable.Creator<android.content.pm.PermissionInfo> CREATOR;
     field public static final int FLAG_COSTS_MONEY = 1; // 0x1
@@ -11405,8 +11407,8 @@
     field public static final int PROTECTION_FLAG_SETUP = 2048; // 0x800
     field public static final deprecated int PROTECTION_FLAG_SYSTEM = 16; // 0x10
     field public static final int PROTECTION_FLAG_VERIFIER = 512; // 0x200
-    field public static final int PROTECTION_MASK_BASE = 15; // 0xf
-    field public static final int PROTECTION_MASK_FLAGS = 65520; // 0xfff0
+    field public static final deprecated int PROTECTION_MASK_BASE = 15; // 0xf
+    field public static final deprecated int PROTECTION_MASK_FLAGS = 65520; // 0xfff0
     field public static final int PROTECTION_NORMAL = 0; // 0x0
     field public static final int PROTECTION_SIGNATURE = 2; // 0x2
     field public static final deprecated int PROTECTION_SIGNATURE_OR_SYSTEM = 3; // 0x3
@@ -11414,7 +11416,7 @@
     field public int flags;
     field public java.lang.String group;
     field public java.lang.CharSequence nonLocalizedDescription;
-    field public int protectionLevel;
+    field public deprecated int protectionLevel;
   }
 
   public final class ProviderInfo extends android.content.pm.ComponentInfo implements android.os.Parcelable {
@@ -21903,6 +21905,7 @@
     field public static final int TYPE_FM_TUNER = 16; // 0x10
     field public static final int TYPE_HDMI = 9; // 0x9
     field public static final int TYPE_HDMI_ARC = 10; // 0xa
+    field public static final int TYPE_HEARING_AID = 23; // 0x17
     field public static final int TYPE_IP = 20; // 0x14
     field public static final int TYPE_LINE_ANALOG = 5; // 0x5
     field public static final int TYPE_LINE_DIGITAL = 6; // 0x6
@@ -41818,12 +41821,12 @@
   public class MbmsDownloadSession implements java.lang.AutoCloseable {
     method public int cancelDownload(android.telephony.mbms.DownloadRequest);
     method public void close();
-    method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, android.os.Handler);
-    method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, int, android.os.Handler);
+    method public static android.telephony.MbmsDownloadSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsDownloadSessionCallback);
+    method public static android.telephony.MbmsDownloadSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsDownloadSessionCallback);
     method public int download(android.telephony.mbms.DownloadRequest);
     method public java.io.File getTempFileRootDirectory();
     method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads();
-    method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler);
+    method public int registerStateCallback(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadStateCallback);
     method public void requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
     method public void requestUpdateFileServices(java.util.List<java.lang.String>);
     method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest);
@@ -41851,10 +41854,10 @@
 
   public class MbmsStreamingSession implements java.lang.AutoCloseable {
     method public void close();
-    method public static android.telephony.MbmsStreamingSession create(android.content.Context, android.telephony.mbms.MbmsStreamingSessionCallback, int, android.os.Handler);
-    method public static android.telephony.MbmsStreamingSession create(android.content.Context, android.telephony.mbms.MbmsStreamingSessionCallback, android.os.Handler);
+    method public static android.telephony.MbmsStreamingSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsStreamingSessionCallback);
+    method public static android.telephony.MbmsStreamingSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsStreamingSessionCallback);
     method public void requestUpdateStreamingServices(java.util.List<java.lang.String>);
-    method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, android.telephony.mbms.StreamingServiceCallback, android.os.Handler);
+    method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback);
   }
 
   public class NeighboringCellInfo implements android.os.Parcelable {
@@ -42727,20 +42730,23 @@
 package android.telephony.mbms {
 
   public final class DownloadRequest implements android.os.Parcelable {
-    method public static android.telephony.mbms.DownloadRequest copy(android.telephony.mbms.DownloadRequest);
     method public int describeContents();
+    method public android.net.Uri getDestinationUri();
     method public java.lang.String getFileServiceId();
     method public static int getMaxAppIntentSize();
     method public static int getMaxDestinationUriSize();
     method public android.net.Uri getSourceUri();
     method public int getSubscriptionId();
+    method public byte[] toByteArray();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.mbms.DownloadRequest> CREATOR;
   }
 
   public static class DownloadRequest.Builder {
-    ctor public DownloadRequest.Builder(android.net.Uri);
+    ctor public DownloadRequest.Builder(android.net.Uri, android.net.Uri);
     method public android.telephony.mbms.DownloadRequest build();
+    method public static android.telephony.mbms.DownloadRequest.Builder fromDownloadRequest(android.telephony.mbms.DownloadRequest);
+    method public static android.telephony.mbms.DownloadRequest.Builder fromSerializedRequest(byte[]);
     method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent);
     method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo);
     method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
@@ -42836,10 +42842,10 @@
     method public java.util.Date getSessionStartTime();
   }
 
-  public class StreamingService {
+  public class StreamingService implements java.lang.AutoCloseable {
+    method public void close();
     method public android.telephony.mbms.StreamingServiceInfo getInfo();
     method public android.net.Uri getPlaybackUri();
-    method public void stopStreaming();
     field public static final int BROADCAST_METHOD = 1; // 0x1
     field public static final int REASON_BY_USER_REQUEST = 1; // 0x1
     field public static final int REASON_END_OF_SESSION = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index 930224d..d1bd184 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -10,6 +10,7 @@
     field public static final java.lang.String ACCESS_MTP = "android.permission.ACCESS_MTP";
     field public static final java.lang.String ACCESS_NETWORK_CONDITIONS = "android.permission.ACCESS_NETWORK_CONDITIONS";
     field public static final java.lang.String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
+    field public static final java.lang.String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS";
     field public static final java.lang.String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER";
     field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER";
     field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
@@ -179,6 +180,7 @@
     field public static final java.lang.String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
     field public static final java.lang.String TV_INPUT_HARDWARE = "android.permission.TV_INPUT_HARDWARE";
     field public static final java.lang.String TV_VIRTUAL_REMOTE_CONTROLLER = "android.permission.TV_VIRTUAL_REMOTE_CONTROLLER";
+    field public static final java.lang.String UNLIMITED_SHORTCUTS_API_CALLS = "android.permission.UNLIMITED_SHORTCUTS_API_CALLS";
     field public static final java.lang.String UPDATE_APP_OPS_STATS = "android.permission.UPDATE_APP_OPS_STATS";
     field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
     field public static final java.lang.String UPDATE_LOCK = "android.permission.UPDATE_LOCK";
@@ -467,7 +469,11 @@
     method public android.app.backup.RestoreSession beginRestoreSession();
     method public void cancelBackups();
     method public long getAvailableRestoreToken(java.lang.String);
+    method public android.content.Intent getConfigurationIntent(java.lang.String);
     method public java.lang.String getCurrentTransport();
+    method public android.content.Intent getDataManagementIntent(java.lang.String);
+    method public java.lang.String getDataManagementLabel(java.lang.String);
+    method public java.lang.String getDestinationString(java.lang.String);
     method public boolean isAppEligibleForBackup(java.lang.String);
     method public boolean isBackupEnabled();
     method public boolean isBackupServiceActive(android.os.UserHandle);
@@ -1085,6 +1091,7 @@
   public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
     field public static final int FLAG_REMOVED = 2; // 0x2
     field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
+    field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
     field public int requestRes;
   }
 
@@ -4295,10 +4302,9 @@
     method public void recoverySecretAvailable(android.security.keystore.recovery.KeyChainProtectionParams) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public void removeKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public void setRecoverySecretTypes(int[]) throws android.security.keystore.recovery.InternalRecoveryServiceException;
-    method public void setRecoveryStatus(java.lang.String, java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.content.pm.PackageManager.NameNotFoundException;
+    method public void setRecoveryStatus(java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public void setServerParams(byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public void setSnapshotCreatedPendingIntent(android.app.PendingIntent) throws android.security.keystore.recovery.InternalRecoveryServiceException;
-    field public static final int RECOVERY_STATUS_MISSING_ACCOUNT = 2; // 0x2
     field public static final int RECOVERY_STATUS_PERMANENT_FAILURE = 3; // 0x3
     field public static final int RECOVERY_STATUS_SYNCED = 0; // 0x0
     field public static final int RECOVERY_STATUS_SYNC_IN_PROGRESS = 1; // 0x1
@@ -6157,12 +6163,7 @@
 
 package android.telephony.mbms {
 
-  public final class DownloadRequest implements android.os.Parcelable {
-    method public byte[] getOpaqueData();
-  }
-
   public static class DownloadRequest.Builder {
-    method public android.telephony.mbms.DownloadRequest.Builder setOpaqueData(byte[]);
     method public android.telephony.mbms.DownloadRequest.Builder setServiceId(java.lang.String);
   }
 
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 48f43e0..ac40052 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -91,6 +91,14 @@
 
 }
 
+package android.security.keystore.recovery {
+
+  public class RecoveryController {
+    method public deprecated void setRecoveryStatus(java.lang.String, java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.content.pm.PackageManager.NameNotFoundException;
+  }
+
+}
+
 package android.service.notification {
 
   public abstract class NotificationListenerService extends android.app.Service {
diff --git a/api/test-current.txt b/api/test-current.txt
index 92251f7..55e7926 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -167,6 +167,17 @@
 
 }
 
+package android.app.backup {
+
+  public class BackupManager {
+    method public android.content.Intent getConfigurationIntent(java.lang.String);
+    method public android.content.Intent getDataManagementIntent(java.lang.String);
+    method public java.lang.String getDataManagementLabel(java.lang.String);
+    method public java.lang.String getDestinationString(java.lang.String);
+  }
+
+}
+
 package android.app.usage {
 
   public class StorageStatsManager {
@@ -216,6 +227,7 @@
   }
 
   public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+    field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
     field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
   }
 
@@ -1096,6 +1108,11 @@
     method public android.graphics.Bitmap getContent();
     method public static android.graphics.PointF getMagnifierDefaultSize();
     method public android.graphics.Rect getWindowPositionOnScreen();
+    method public void setOnOperationCompleteCallback(android.widget.Magnifier.Callback);
+  }
+
+  public static abstract interface Magnifier.Callback {
+    method public abstract void onOperationComplete();
   }
 
   public class NumberPicker extends android.widget.LinearLayout {
diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
index 45a0e7b..ab4382a 100644
--- a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
+++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
@@ -104,7 +104,8 @@
 
                 for (size_t i=0; i<blockHeader.size(); i++) {
                     if (!table.insertField(&proto, blockHeader[i], blockCounts[i+1])) {
-                        return BAD_VALUE;
+                        fprintf(stderr, "Header %s has bad data %s\n", blockHeader[i].c_str(),
+                            blockCounts[i+1].c_str());
                     }
                 }
             } else return BAD_VALUE;
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index d2d24c8..6bdd9be 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -25,16 +25,7 @@
 
 LOCAL_MODULE := incidentd
 
-LOCAL_SRC_FILES := \
-        src/PrivacyBuffer.cpp \
-        src/FdBuffer.cpp \
-        src/IncidentService.cpp \
-        src/Privacy.cpp \
-        src/Reporter.cpp \
-        src/Section.cpp \
-        src/incidentd_util.cpp \
-        src/main.cpp \
-        src/report_directory.cpp
+LOCAL_SRC_FILES := $(call all-cpp-files-under, src) \
 
 LOCAL_CFLAGS += \
         -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
@@ -110,19 +101,15 @@
 
 LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
 
-LOCAL_SRC_FILES := \
+LOCAL_SRC_FILES := $(call all-cpp-files-under, tests) \
     src/PrivacyBuffer.cpp \
     src/FdBuffer.cpp \
     src/Privacy.cpp \
     src/Reporter.cpp \
     src/Section.cpp \
+    src/Throttler.cpp \
     src/incidentd_util.cpp \
     src/report_directory.cpp \
-    tests/section_list.cpp \
-    tests/PrivacyBuffer_test.cpp \
-    tests/FdBuffer_test.cpp \
-    tests/Reporter_test.cpp \
-    tests/Section_test.cpp \
 
 LOCAL_STATIC_LIBRARIES := \
     libgmock \
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index 883924c8..db60794 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define DEBUG false
 #include "Log.h"
 
 #include "FdBuffer.h"
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 9ae6240..28fb38a 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define DEBUG false
 #include "Log.h"
 
 #include "IncidentService.h"
@@ -38,9 +39,11 @@
 
 enum { WHAT_RUN_REPORT = 1, WHAT_SEND_BACKLOG_TO_DROPBOX = 2 };
 
-//#define DEFAULT_BACKLOG_DELAY_NS (1000000000LL * 60 * 5)
 #define DEFAULT_BACKLOG_DELAY_NS (1000000000LL)
 
+#define DEFAULT_BYTES_SIZE_LIMIT (20 * 1024 * 1024)        // 20MB
+#define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000)  // 1 Day
+
 // ================================================================================
 String16 const DUMP_PERMISSION("android.permission.DUMP");
 String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS");
@@ -113,8 +116,12 @@
 }
 
 // ================================================================================
-ReportHandler::ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue)
-    : mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS), mHandlerLooper(handlerLooper), mQueue(queue) {}
+ReportHandler::ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue,
+                             const sp<Throttler>& throttler)
+    : mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS),
+      mHandlerLooper(handlerLooper),
+      mQueue(queue),
+      mThrottler(throttler) {}
 
 ReportHandler::~ReportHandler() {}
 
@@ -159,10 +166,17 @@
         reporter->batch.add(request);
     }
 
+    if (mThrottler->shouldThrottle()) {
+        ALOGW("RunReport got throttled.");
+        return;
+    }
+
     // Take the report, which might take a while. More requests might queue
     // up while we're doing this, and we'll handle them in their next batch.
     // TODO: We should further rate-limit the reports to no more than N per time-period.
-    Reporter::run_report_status_t reportStatus = reporter->runReport();
+    size_t reportByteSize = 0;
+    Reporter::run_report_status_t reportStatus = reporter->runReport(&reportByteSize);
+    mThrottler->addReportSize(reportByteSize);
     if (reportStatus == Reporter::REPORT_NEEDS_DROPBOX) {
         unique_lock<mutex> lock(mLock);
         schedule_send_backlog_to_dropbox_locked();
@@ -184,8 +198,9 @@
 
 // ================================================================================
 IncidentService::IncidentService(const sp<Looper>& handlerLooper)
-    : mQueue(new ReportRequestQueue()) {
-    mHandler = new ReportHandler(handlerLooper, mQueue);
+    : mQueue(new ReportRequestQueue()),
+      mThrottler(new Throttler(DEFAULT_BYTES_SIZE_LIMIT, DEFAULT_REFACTORY_PERIOD_MS)) {
+    mHandler = new ReportHandler(handlerLooper, mQueue, mThrottler);
 }
 
 IncidentService::~IncidentService() {}
@@ -294,6 +309,10 @@
         if (!args[0].compare(String8("privacy"))) {
             return cmd_privacy(in, out, err, args);
         }
+        if (!args[0].compare(String8("throttler"))) {
+            mThrottler->dump(out);
+            return NO_ERROR;
+        }
     }
     return cmd_help(out);
 }
@@ -302,6 +321,9 @@
     fprintf(out, "usage: adb shell cmd incident privacy print <section_id>\n");
     fprintf(out, "usage: adb shell cmd incident privacy parse <section_id> < proto.txt\n");
     fprintf(out, "    Prints/parses for the section id.\n");
+    fprintf(out, "\n");
+    fprintf(out, "usage: adb shell cmd incident throttler\n");
+    fprintf(out, "    Prints the current throttler state\n");
     return NO_ERROR;
 }
 
diff --git a/cmds/incidentd/src/IncidentService.h b/cmds/incidentd/src/IncidentService.h
index 3c66507..0ab34ed 100644
--- a/cmds/incidentd/src/IncidentService.h
+++ b/cmds/incidentd/src/IncidentService.h
@@ -26,6 +26,8 @@
 #include <deque>
 #include <mutex>
 
+#include "Throttler.h"
+
 using namespace android;
 using namespace android::base;
 using namespace android::binder;
@@ -49,7 +51,8 @@
 // ================================================================================
 class ReportHandler : public MessageHandler {
 public:
-    ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue);
+    ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue,
+                  const sp<Throttler>& throttler);
     virtual ~ReportHandler();
 
     virtual void handleMessage(const Message& message);
@@ -70,6 +73,7 @@
     nsecs_t mBacklogDelay;
     sp<Looper> mHandlerLooper;
     sp<ReportRequestQueue> mQueue;
+    sp<Throttler> mThrottler;
 
     /**
      * Runs all of the reports that have been queued.
@@ -109,6 +113,7 @@
 private:
     sp<ReportRequestQueue> mQueue;
     sp<ReportHandler> mHandler;
+    sp<Throttler> mThrottler;
 
     /**
      * Commands print out help.
diff --git a/cmds/incidentd/src/Log.h b/cmds/incidentd/src/Log.h
index 46efbd1..22de46d 100644
--- a/cmds/incidentd/src/Log.h
+++ b/cmds/incidentd/src/Log.h
@@ -23,7 +23,6 @@
 #pragma once
 
 #define LOG_TAG "incidentd"
-#define DEBUG false
 
 #include <log/log.h>
 
diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp
index e4128f4..ee57f4d 100644
--- a/cmds/incidentd/src/PrivacyBuffer.cpp
+++ b/cmds/incidentd/src/PrivacyBuffer.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define DEBUG false
 #include "Log.h"
 
 #include "PrivacyBuffer.h"
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index c0b5358..12764f8 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define DEBUG false
 #include "Log.h"
 
 #include "Reporter.h"
@@ -118,7 +119,7 @@
 
 Reporter::~Reporter() {}
 
-Reporter::run_report_status_t Reporter::runReport() {
+Reporter::run_report_status_t Reporter::runReport(size_t* reportByteSize) {
     status_t err = NO_ERROR;
     bool needMainFd = false;
     int mainFd = -1;
@@ -185,7 +186,6 @@
             int64_t startTime = uptimeMillis();
             err = (*section)->Execute(&batch);
             int64_t endTime = uptimeMillis();
-
             stats->set_success(err == NO_ERROR);
             stats->set_exec_duration_ms(endTime - startTime);
             if (err != NO_ERROR) {
@@ -193,6 +193,7 @@
                       (*section)->name.string(), id, strerror(-err));
                 goto DONE;
             }
+            (*reportByteSize) += stats->report_size_bytes();
 
             // Notify listener of starting
             for (ReportRequestSet::iterator it = batch.begin(); it != batch.end(); it++) {
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
index 0f3f221..ba8965e 100644
--- a/cmds/incidentd/src/Reporter.h
+++ b/cmds/incidentd/src/Reporter.h
@@ -18,8 +18,6 @@
 #ifndef REPORTER_H
 #define REPORTER_H
 
-#include "frameworks/base/libs/incident/proto/android/os/metadata.pb.h"
-
 #include <android/os/IIncidentReportStatusListener.h>
 #include <android/os/IncidentReportArgs.h>
 
@@ -29,6 +27,9 @@
 
 #include <time.h>
 
+#include "Throttler.h"
+#include "frameworks/base/libs/incident/proto/android/os/metadata.pb.h"
+
 using namespace android;
 using namespace android::os;
 using namespace std;
@@ -91,7 +92,7 @@
     virtual ~Reporter();
 
     // Run the report as described in the batch and args parameters.
-    run_report_status_t runReport();
+    run_report_status_t runReport(size_t* reportByteSize);
 
     static run_report_status_t upload_backlog();
 
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 2e4e980..64eae3a 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define DEBUG false
 #include "Log.h"
 
 #include "Section.h"
@@ -244,7 +245,8 @@
     }
     if (requests->mainFd() >= 0 && !metadataBuf.empty()) {
         write_section_header(requests->mainFd(), id, metadataBuf.size());
-        if (!WriteFully(requests->mainFd(), (uint8_t const*)metadataBuf.data(), metadataBuf.size())) {
+        if (!WriteFully(requests->mainFd(), (uint8_t const*)metadataBuf.data(),
+                        metadataBuf.size())) {
             ALOGW("Failed to write metadata to dropbox fd %d", requests->mainFd());
             return -1;
         }
diff --git a/cmds/incidentd/src/Throttler.cpp b/cmds/incidentd/src/Throttler.cpp
new file mode 100644
index 0000000..1abf267
--- /dev/null
+++ b/cmds/incidentd/src/Throttler.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+#define DEBUG false
+#include "Log.h"
+
+#include "Throttler.h"
+
+#include <utils/SystemClock.h>
+
+Throttler::Throttler(size_t limit, int64_t refractoryPeriodMs)
+    : mSizeLimit(limit),
+      mRefractoryPeriodMs(refractoryPeriodMs),
+      mAccumulatedSize(0),
+      mLastRefractoryMs(android::elapsedRealtime()) {}
+
+Throttler::~Throttler() {}
+
+bool Throttler::shouldThrottle() {
+    int64_t now = android::elapsedRealtime();
+    if (now > mRefractoryPeriodMs + mLastRefractoryMs) {
+        mLastRefractoryMs = now;
+        mAccumulatedSize = 0;
+    }
+    return mAccumulatedSize > mSizeLimit;
+}
+
+void Throttler::addReportSize(size_t reportByteSize) {
+    VLOG("The current request took %d bytes to dropbox", (int)reportByteSize);
+    mAccumulatedSize += reportByteSize;
+}
+
+void Throttler::dump(FILE* out) {
+    fprintf(out, "mSizeLimit=%d\n", (int)mSizeLimit);
+    fprintf(out, "mAccumulatedSize=%d\n", (int)mAccumulatedSize);
+    fprintf(out, "mRefractoryPeriodMs=%d\n", (int)mRefractoryPeriodMs);
+    fprintf(out, "mLastRefractoryMs=%d\n", (int)mLastRefractoryMs);
+}
diff --git a/cmds/incidentd/src/Throttler.h b/cmds/incidentd/src/Throttler.h
new file mode 100644
index 0000000..c56f753
--- /dev/null
+++ b/cmds/incidentd/src/Throttler.h
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+#ifndef THROTTLER_H
+#define THROTTLER_H
+
+#include <utils/RefBase.h>
+
+#include <unistd.h>
+/**
+ * This is a size-based throttler which prevents incidentd to take more data.
+ */
+class Throttler : public virtual android::RefBase {
+public:
+    Throttler(size_t limit, int64_t refractoryPeriodMs);
+    ~Throttler();
+
+    /**
+     * Asserts this before starting taking report.
+     */
+    bool shouldThrottle();
+
+    void addReportSize(size_t reportByteSize);
+
+    void dump(FILE* out);
+
+private:
+    const size_t mSizeLimit;
+    const int64_t mRefractoryPeriodMs;
+
+    size_t mAccumulatedSize;
+    int64_t mLastRefractoryMs;
+};
+
+#endif  // THROTTLER_H
diff --git a/cmds/incidentd/src/report_directory.cpp b/cmds/incidentd/src/report_directory.cpp
index b71c066..f023ee1 100644
--- a/cmds/incidentd/src/report_directory.cpp
+++ b/cmds/incidentd/src/report_directory.cpp
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define DEBUG false
 #include "Log.h"
 
 #include "report_directory.h"
diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp
index 956c8d3..0e5eec6 100644
--- a/cmds/incidentd/tests/FdBuffer_test.cpp
+++ b/cmds/incidentd/tests/FdBuffer_test.cpp
@@ -11,6 +11,7 @@
 // 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.
+#define DEBUG false
 #include "Log.h"
 
 #include "FdBuffer.h"
diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
index 7ea9bbf..c7c69a7 100644
--- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp
+++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
@@ -11,6 +11,7 @@
 // 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.
+#define DEBUG false
 #include "Log.h"
 
 #include "FdBuffer.h"
diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp
index bd359ac..955dbac 100644
--- a/cmds/incidentd/tests/Reporter_test.cpp
+++ b/cmds/incidentd/tests/Reporter_test.cpp
@@ -11,6 +11,7 @@
 // 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.
+#define DEBUG false
 #include "Log.h"
 
 #include "Reporter.h"
@@ -106,6 +107,7 @@
     ReportRequestSet requests;
     sp<Reporter> reporter;
     sp<TestListener> l;
+    size_t size;
 };
 
 TEST_F(ReporterTest, IncidentReportArgs) {
@@ -125,7 +127,7 @@
 }
 
 TEST_F(ReporterTest, RunReportEmpty) {
-    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
+    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size));
     EXPECT_EQ(l->startInvoked, 0);
     EXPECT_EQ(l->finishInvoked, 0);
     EXPECT_TRUE(l->startSections.empty());
@@ -147,7 +149,7 @@
     reporter->batch.add(r1);
     reporter->batch.add(r2);
 
-    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
+    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size));
 
     string result;
     ReadFileToString(tf.path, &result);
@@ -171,7 +173,7 @@
     sp<ReportRequest> r = new ReportRequest(args, l, -1);
     reporter->batch.add(r);
 
-    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
+    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size));
     vector<string> results = InspectFiles();
     ASSERT_EQ((int)results.size(), 1);
     EXPECT_EQ(results[0],
@@ -188,7 +190,7 @@
     sp<ReportRequest> r = new ReportRequest(args, l, -1);
     reporter->batch.add(r);
 
-    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
+    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport(&size));
     auto metadata = reporter->batch.metadata();
     EXPECT_EQ(IncidentMetadata_Destination_EXPLICIT, metadata.dest());
     EXPECT_EQ(1, metadata.request_size());
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index a1f4fdc..026bf74 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -11,6 +11,7 @@
 // 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.
+#define DEBUG false
 #include "Log.h"
 
 #include "Section.h"
diff --git a/cmds/incidentd/tests/Throttler_test.cpp b/cmds/incidentd/tests/Throttler_test.cpp
new file mode 100644
index 0000000..213dcef
--- /dev/null
+++ b/cmds/incidentd/tests/Throttler_test.cpp
@@ -0,0 +1,37 @@
+// 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.
+#define DEBUG false
+#include "Log.h"
+
+#include "Throttler.h"
+
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+TEST(ThrottlerTest, DataSizeExceeded) {
+    Throttler t(100, 100000);
+    EXPECT_FALSE(t.shouldThrottle());
+    t.addReportSize(200);
+    EXPECT_TRUE(t.shouldThrottle());
+}
+
+TEST(ThrottlerTest, TimeReset) {
+    Throttler t(100, 500);
+    EXPECT_FALSE(t.shouldThrottle());
+    t.addReportSize(200);
+    EXPECT_TRUE(t.shouldThrottle());
+    sleep(1);  // sleep for 1 second to make sure throttler resets
+    EXPECT_FALSE(t.shouldThrottle());
+}
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index a5eae15..b566099 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -21,6 +21,7 @@
     name: "libstats_proto_host",
     srcs: [
         "src/atoms.proto",
+        "src/atom_field_options.proto",
     ],
 
     shared_libs: [
@@ -30,6 +31,9 @@
     proto: {
         type: "full",
         export_proto_headers: true,
+        include_dirs: [
+            "external/protobuf/src",
+        ],
     },
 
     export_shared_lib_headers: [
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 740fdc0..244fbce 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -17,9 +17,8 @@
 statsd_common_src := \
     ../../core/java/android/os/IStatsCompanionService.aidl \
     ../../core/java/android/os/IStatsManager.aidl \
-    src/stats_log.proto \
+    src/stats_log_common.proto \
     src/statsd_config.proto \
-    src/atoms.proto \
     src/FieldValue.cpp \
     src/stats_log_util.cpp \
     src/anomaly/AnomalyMonitor.cpp \
@@ -168,6 +167,9 @@
 
 LOCAL_SRC_FILES := \
     $(statsd_common_src) \
+    src/atom_field_options.proto \
+    src/atoms.proto \
+    src/stats_log.proto \
     tests/AnomalyMonitor_test.cpp \
     tests/anomaly/AnomalyTracker_test.cpp \
     tests/ConfigManager_test.cpp \
@@ -202,9 +204,13 @@
     $(statsd_common_static_libraries) \
     libgmock
 
-LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries)
+LOCAL_PROTOC_OPTIMIZE_TYPE := full
 
-LOCAL_PROTOC_OPTIMIZE_TYPE := lite
+LOCAL_PROTOC_FLAGS := \
+    -Iexternal/protobuf/src
+
+LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
+                        libprotobuf-cpp-full
 
 include $(BUILD_NATIVE_TEST)
 
@@ -217,6 +223,7 @@
 
 LOCAL_SRC_FILES := \
     src/stats_log.proto \
+    src/stats_log_common.proto \
     src/statsd_config.proto \
     src/perfetto/perfetto_config.proto \
     src/atoms.proto
@@ -226,6 +233,9 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     platformprotoslite
 
+LOCAL_PROTOC_FLAGS := \
+    -Iexternal/protobuf/src
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 ##############################
@@ -261,8 +271,6 @@
     libgtest_prod \
     libstatslog
 
-LOCAL_PROTOC_OPTIMIZE_TYPE := lite
-
 LOCAL_MODULE_TAGS := eng tests
 
 include $(BUILD_NATIVE_BENCHMARK)
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index d901bd6..3c69f9e 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -22,6 +22,8 @@
 namespace android {
 namespace os {
 namespace statsd {
+
+using std::string;
 using std::vector;
 
 android::hash_t hashDimension(const HashableDimensionKey& value) {
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 791fb14..26db3af 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -659,6 +659,7 @@
                                          "Only system uid can call informOnePackageRemoved");
     }
     mUidMap->removeApp(app, uid);
+    mConfigManager->RemoveConfigs(uid);
     return Status::ok();
 }
 
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index 6553a14..babe0fc 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -192,7 +192,7 @@
 
     if (!mSubscriptions.empty()) {
         if (mAlert.has_id()) {
-            ALOGI("An anomaly (%llu) has occurred! Informing subscribers.", mAlert.id());
+            ALOGI("An anomaly (%lld) has occurred! Informing subscribers.", mAlert.id());
             informSubscribers(key);
         } else {
             ALOGI("An anomaly (with no id) has occurred! Not informing any subscribers.");
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
new file mode 100644
index 0000000..19d00b7
--- /dev/null
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -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.
+ */
+
+syntax = "proto2";
+
+package android.os.statsd;
+option java_package = "com.android.os";
+option java_multiple_files = true;
+option java_outer_classname = "AtomFieldOptions";
+
+import "google/protobuf/descriptor.proto";
+
+enum StateField {
+    // Default value for fields that are not primary or exclusive state.
+    STATE_FIELD_UNSET = 0;
+    // Fields that represent the key that the state belongs to.
+    PRIMARY = 1;
+    // The field that represents the state. It's an exclusive state.
+    EXCLUSIVE = 2;
+}
+
+// Used to annotate an atom that reprsents a state change. A state change atom must have exactly ONE
+// exclusive state field, and any number of primary key fields.
+// For example,
+// message UidProcessStateChanged {
+//    optional int32 uid = 1 [(stateFieldOption).option = PRIMARY];
+//    optional android.app.ProcessStateEnum state = 2 [(stateFieldOption).option = EXCLUSIVE];
+//  }
+// Each of this UidProcessStateChanged atom represents a state change for a specific uid.
+// A new state automatically overrides the previous state.
+//
+// If the atom has 2 or more primary fields, it means the combination of the primary fields are
+// the primary key.
+// For example:
+// message ThreadStateChanged {
+//    optional int32 pid = 1  [(stateFieldOption).option = PRIMARY];
+//    optional int32 tid = 2  [(stateFieldOption).option = PRIMARY];
+//    optional int32 state = 3 [(stateFieldOption).option = EXCLUSIVE];
+// }
+//
+// Sometimes, there is no primary key field, when the state is GLOBAL.
+// For example,
+//
+// message ScreenStateChanged {
+//    optional android.view.DisplayStateEnum state = 1 [(stateFieldOption).option = EXCLUSIVE];
+// }
+//
+// Only fields of primary types can be annotated. AttributionNode cannot be primary keys (and they
+// usually are not).
+message StateAtomFieldOption {
+    optional StateField option = 1 [default = STATE_FIELD_UNSET];
+}
+
+extend google.protobuf.FieldOptions {
+    // Flags to decorate an atom that presents a state change.
+    optional StateAtomFieldOption stateFieldOption = 50000;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index d9094dd..2b02025 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -21,6 +21,7 @@
 option java_package = "com.android.os";
 option java_outer_classname = "AtomsProto";
 
+import "frameworks/base/cmds/statsd/src/atom_field_options.proto";
 import "frameworks/base/core/proto/android/app/enums.proto";
 import "frameworks/base/core/proto/android/os/enums.proto";
 import "frameworks/base/core/proto/android/server/enums.proto";
@@ -179,7 +180,7 @@
  */
 message ScreenStateChanged {
     // New screen state, from frameworks/base/core/proto/android/view/enums.proto.
-    optional android.view.DisplayStateEnum state = 1;
+    optional android.view.DisplayStateEnum state = 1 [(stateFieldOption).option = EXCLUSIVE];
 }
 
 /**
@@ -189,10 +190,10 @@
  *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message UidProcessStateChanged {
-    optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation
+    optional int32 uid = 1 [(stateFieldOption).option = PRIMARY];
 
     // The state, from frameworks/base/core/proto/android/app/enums.proto.
-    optional android.app.ProcessStateEnum state = 2;
+    optional android.app.ProcessStateEnum state = 2 [(stateFieldOption).option = EXCLUSIVE];
 }
 
 /**
@@ -1514,5 +1515,4 @@
  */
 message FullBatteryCapacity {
     optional int32 capacity_uAh = 1;
-}
-
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 7baa5e5..8c16e4e 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -16,7 +16,7 @@
 #pragma once
 
 #include "config/ConfigKey.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/stats_log_common.pb.h"
 #include "statslog.h"
 
 #include <gtest/gtest_prod.h>
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index f07fc66..d282b86 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -26,9 +26,10 @@
 namespace statsd {
 
 using namespace android::util;
+using android::util::ProtoOutputStream;
 using std::ostringstream;
 using std::string;
-using android::util::ProtoOutputStream;
+using std::vector;
 
 LogEvent::LogEvent(log_msg& msg) {
     mContext =
@@ -130,7 +131,7 @@
     return false;
 }
 
-bool LogEvent::write(const std::vector<AttributionNode>& nodes) {
+bool LogEvent::write(const std::vector<AttributionNodeInternal>& nodes) {
     if (mContext) {
          if (android_log_write_list_begin(mContext) < 0) {
             return false;
@@ -148,7 +149,7 @@
     return false;
 }
 
-bool LogEvent::write(const AttributionNode& node) {
+bool LogEvent::write(const AttributionNodeInternal& node) {
     if (mContext) {
          if (android_log_write_list_begin(mContext) < 0) {
             return false;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index b3084d5..24d624d 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include "FieldValue.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 
 #include <android/util/ProtoOutputStream.h>
 #include <log/log_event_list.h>
@@ -32,8 +31,26 @@
 namespace os {
 namespace statsd {
 
-using std::string;
-using std::vector;
+struct AttributionNodeInternal {
+    void set_uid(int32_t id) {
+        mUid = id;
+    }
+
+    void set_tag(const std::string& value) {
+        mTag = value;
+    }
+
+    int32_t uid() const {
+        return mUid;
+    }
+
+    const std::string& tag() const {
+        return mTag;
+    }
+
+    int32_t mUid;
+    std::string mTag;
+};
 /**
  * Wrapper for the log_msg structure.
  */
@@ -89,15 +106,15 @@
     bool write(int32_t value);
     bool write(uint64_t value);
     bool write(int64_t value);
-    bool write(const string& value);
+    bool write(const std::string& value);
     bool write(float value);
-    bool write(const std::vector<AttributionNode>& nodes);
-    bool write(const AttributionNode& node);
+    bool write(const std::vector<AttributionNodeInternal>& nodes);
+    bool write(const AttributionNodeInternal& node);
 
     /**
      * Return a string representation of this event.
      */
-    string ToString() const;
+    std::string ToString() const;
 
     /**
      * Write this object to a ProtoOutputStream.
diff --git a/cmds/statsd/src/matchers/matcher_util.h b/cmds/statsd/src/matchers/matcher_util.h
index 872cd8e..ae946d1 100644
--- a/cmds/statsd/src/matchers/matcher_util.h
+++ b/cmds/statsd/src/matchers/matcher_util.h
@@ -24,10 +24,10 @@
 #include <string>
 #include <unordered_map>
 #include <vector>
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/stats_log_common.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "stats_util.h"
 #include "packages/UidMap.h"
+#include "stats_util.h"
 
 namespace android {
 namespace os {
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index fddfd93..96d0cfc 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -129,8 +129,6 @@
 
     long long wrapperToken =
             mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
-
-    long long eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS);
     const bool truncateTimestamp =
         android::util::kNotTruncatingTimestampAtomWhiteList.find(event.GetTagId()) ==
         android::util::kNotTruncatingTimestampAtomWhiteList.end();
@@ -145,6 +143,8 @@
         mProto->write(FIELD_TYPE_INT64 | FIELD_ID_WALL_CLOCK_TIMESTAMP_NANOS,
             (long long)getWallClockNs());
     }
+
+    long long eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS);
     event.ToProto(*mProto);
     mProto->end(eventToken);
     mProto->end(wrapperToken);
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index f1da452..c41e0aa 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -18,7 +18,7 @@
 
 #include "config/ConfigKey.h"
 #include "config/ConfigListener.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/stats_log_common.pb.h"
 #include "packages/PackageInfoListener.h"
 
 #include <binder/IResultReceiver.h>
diff --git a/cmds/statsd/src/perfetto/perfetto_config.proto b/cmds/statsd/src/perfetto/perfetto_config.proto
index dc868f9..56d12f8 100644
--- a/cmds/statsd/src/perfetto/perfetto_config.proto
+++ b/cmds/statsd/src/perfetto/perfetto_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package perfetto.protos;
 
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index b427485..272e90b 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package android.os.statsd;
 
@@ -23,6 +22,7 @@
 option java_outer_classname = "StatsLog";
 
 import "frameworks/base/cmds/statsd/src/atoms.proto";
+import "frameworks/base/cmds/statsd/src/stats_log_common.proto";
 
 message DimensionsValue {
   optional int32 field = 1;
@@ -115,33 +115,6 @@
   repeated GaugeBucketInfo bucket_info = 3;
 }
 
-message UidMapping {
-  message PackageInfoSnapshot {
-    message PackageInfo {
-      optional string name = 1;
-
-      optional int64 version = 2;
-
-      optional int32 uid = 3;
-    }
-    optional int64 elapsed_timestamp_nanos = 1;
-
-    repeated PackageInfo package_info = 2;
-  }
-  repeated PackageInfoSnapshot snapshots = 1;
-
-  message Change {
-    optional bool deletion = 1;
-
-    optional int64 elapsed_timestamp_nanos = 2;
-    optional string app = 3;
-    optional int32 uid = 4;
-
-    optional int64 version = 5;
-  }
-  repeated Change changes = 2;
-}
-
 message StatsLogReport {
   optional int64 metric_id = 1;
 
@@ -191,87 +164,4 @@
   optional ConfigKey config_key = 1;
 
   repeated ConfigMetricsReport reports = 2;
-}
-
-message StatsdStatsReport {
-  optional int32 stats_begin_time_sec = 1;
-
-  optional int32 stats_end_time_sec = 2;
-
-  message MatcherStats {
-    optional int64 id = 1;
-    optional int32 matched_times = 2;
-  }
-
-  message ConditionStats {
-    optional int64 id = 1;
-    optional int32 max_tuple_counts = 2;
-  }
-
-  message MetricStats {
-    optional int64 id = 1;
-    optional int32 max_tuple_counts = 2;
-  }
-
-  message AlertStats {
-    optional int64 id = 1;
-    optional int32 alerted_times = 2;
-  }
-
-  message ConfigStats {
-    optional int32 uid = 1;
-    optional int64 id = 2;
-    optional int32 creation_time_sec = 3;
-    optional int32 deletion_time_sec = 4;
-    optional int32 metric_count = 5;
-    optional int32 condition_count = 6;
-    optional int32 matcher_count = 7;
-    optional int32 alert_count = 8;
-    optional bool is_valid = 9;
-
-    repeated int32 broadcast_sent_time_sec = 10;
-    repeated int32 data_drop_time_sec = 11;
-    repeated int32 dump_report_time_sec = 12;
-    repeated MatcherStats matcher_stats = 13;
-    repeated ConditionStats condition_stats = 14;
-    repeated MetricStats metric_stats = 15;
-    repeated AlertStats alert_stats = 16;
-  }
-
-  repeated ConfigStats config_stats = 3;
-
-  message AtomStats {
-    optional int32 tag = 1;
-    optional int32 count = 2;
-  }
-
-  repeated AtomStats atom_stats = 7;
-
-  message UidMapStats {
-    optional int32 snapshots = 1;
-    optional int32 changes = 2;
-    optional int32 bytes_used = 3;
-    optional int32 dropped_snapshots = 4;
-    optional int32 dropped_changes = 5;
-  }
-  optional UidMapStats uidmap_stats = 8;
-
-  message AnomalyAlarmStats {
-    optional int32 alarms_registered = 1;
-  }
-  optional AnomalyAlarmStats anomaly_alarm_stats = 9;
-
-  message PulledAtomStats {
-    optional int32 atom_id = 1;
-    optional int64 total_pull = 2;
-    optional int64 total_pull_from_cache = 3;
-    optional int64 min_pull_interval_sec = 4;
-  }
-  repeated PulledAtomStats pulled_atom_stats = 10;
-
-  message LoggerErrorStats {
-    optional int32 logger_disconnection_sec = 1;
-    optional int32 error_code = 2;
-  }
-  repeated LoggerErrorStats logger_error_stats = 11;
 }
\ No newline at end of file
diff --git a/cmds/statsd/src/stats_log_common.proto b/cmds/statsd/src/stats_log_common.proto
new file mode 100644
index 0000000..aeecd23
--- /dev/null
+++ b/cmds/statsd/src/stats_log_common.proto
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package android.os.statsd;
+
+option java_package = "com.android.os";
+option java_outer_classname = "StatsLogCommon";
+
+message UidMapping {
+    message PackageInfoSnapshot {
+        message PackageInfo {
+            optional string name = 1;
+
+            optional int64 version = 2;
+
+            optional int32 uid = 3;
+        }
+        optional int64 elapsed_timestamp_nanos = 1;
+
+        repeated PackageInfo package_info = 2;
+    }
+    repeated PackageInfoSnapshot snapshots = 1;
+
+    message Change {
+        optional bool deletion = 1;
+
+        optional int64 elapsed_timestamp_nanos = 2;
+        optional string app = 3;
+        optional int32 uid = 4;
+
+        optional int64 version = 5;
+    }
+    repeated Change changes = 2;
+}
+
+message StatsdStatsReport {
+    optional int32 stats_begin_time_sec = 1;
+
+    optional int32 stats_end_time_sec = 2;
+
+    message MatcherStats {
+        optional int64 id = 1;
+        optional int32 matched_times = 2;
+    }
+
+    message ConditionStats {
+        optional int64 id = 1;
+        optional int32 max_tuple_counts = 2;
+    }
+
+    message MetricStats {
+        optional int64 id = 1;
+        optional int32 max_tuple_counts = 2;
+    }
+
+    message AlertStats {
+        optional int64 id = 1;
+        optional int32 alerted_times = 2;
+    }
+
+    message ConfigStats {
+        optional int32 uid = 1;
+        optional int64 id = 2;
+        optional int32 creation_time_sec = 3;
+        optional int32 deletion_time_sec = 4;
+        optional int32 metric_count = 5;
+        optional int32 condition_count = 6;
+        optional int32 matcher_count = 7;
+        optional int32 alert_count = 8;
+        optional bool is_valid = 9;
+
+        repeated int32 broadcast_sent_time_sec = 10;
+        repeated int32 data_drop_time_sec = 11;
+        repeated int32 dump_report_time_sec = 12;
+        repeated MatcherStats matcher_stats = 13;
+        repeated ConditionStats condition_stats = 14;
+        repeated MetricStats metric_stats = 15;
+        repeated AlertStats alert_stats = 16;
+    }
+
+    repeated ConfigStats config_stats = 3;
+
+    message AtomStats {
+        optional int32 tag = 1;
+        optional int32 count = 2;
+    }
+
+    repeated AtomStats atom_stats = 7;
+
+    message UidMapStats {
+        optional int32 snapshots = 1;
+        optional int32 changes = 2;
+        optional int32 bytes_used = 3;
+        optional int32 dropped_snapshots = 4;
+        optional int32 dropped_changes = 5;
+    }
+    optional UidMapStats uidmap_stats = 8;
+
+    message AnomalyAlarmStats {
+        optional int32 alarms_registered = 1;
+    }
+    optional AnomalyAlarmStats anomaly_alarm_stats = 9;
+
+    message PulledAtomStats {
+        optional int32 atom_id = 1;
+        optional int64 total_pull = 2;
+        optional int64 total_pull_from_cache = 3;
+        optional int64 min_pull_interval_sec = 4;
+    }
+    repeated PulledAtomStats pulled_atom_stats = 10;
+
+    message LoggerErrorStats {
+        optional int32 logger_disconnection_sec = 1;
+        optional int32 error_code = 2;
+    }
+    repeated LoggerErrorStats logger_error_stats = 11;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index 5922642..c512e3c 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -19,7 +19,7 @@
 #include <android/util/ProtoOutputStream.h>
 #include "FieldValue.h"
 #include "HashableDimensionKey.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/stats_log_common.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "guardrail/StatsdStats.h"
 
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index 31f51a7..c4b47dc 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -18,7 +18,7 @@
 
 #include <sstream>
 #include "HashableDimensionKey.h"
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/stats_log_common.pb.h"
 #include "logd/LogReader.h"
 
 #include <unordered_map>
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 5a326a4..8750275 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -15,7 +15,6 @@
  */
 
 syntax = "proto2";
-option optimize_for = LITE_RUNTIME;
 
 package android.os.statsd;
 
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index 9f68fc4..95ecf80 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -27,6 +27,8 @@
 namespace os {
 namespace statsd {
 
+using std::vector;
+
 void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey,
                                                 int64_t subscriberId,
                                                 const sp<IBinder>& intentSender) {
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index c7d1a5b..50100df 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -21,7 +21,6 @@
 
 #include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // subscription
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"  // DimensionsValue
 #include "android/os/StatsDimensionsValue.h"
 #include "HashableDimensionKey.h"
 
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index f1ad0c8..5846761 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 #include <gtest/gtest.h>
-#include "src/logd/LogEvent.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "matchers/matcher_util.h"
+#include "src/logd/LogEvent.h"
 #include "stats_log_util.h"
 #include "stats_util.h"
 #include "subscriber/SubscriberReporter.h"
@@ -64,19 +65,19 @@
     vector<Matcher> matchers;
     translateFieldMatcher(matcher1, &matchers);
 
-    AttributionNode attribution_node1;
+    AttributionNodeInternal attribution_node1;
     attribution_node1.set_uid(1111);
     attribution_node1.set_tag("location1");
 
-    AttributionNode attribution_node2;
+    AttributionNodeInternal attribution_node2;
     attribution_node2.set_uid(2222);
     attribution_node2.set_tag("location2");
 
-    AttributionNode attribution_node3;
+    AttributionNodeInternal attribution_node3;
     attribution_node3.set_uid(3333);
     attribution_node3.set_tag("location3");
-    std::vector<AttributionNode> attribution_nodes = {attribution_node1, attribution_node2,
-                                                      attribution_node3};
+    std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
+                                                              attribution_node3};
 
     // Set up the event
     LogEvent event(10, 12345);
@@ -154,19 +155,19 @@
 }
 
 TEST(AtomMatcherTest, TestMetric2ConditionLink) {
-    AttributionNode attribution_node1;
+    AttributionNodeInternal attribution_node1;
     attribution_node1.set_uid(1111);
     attribution_node1.set_tag("location1");
 
-    AttributionNode attribution_node2;
+    AttributionNodeInternal attribution_node2;
     attribution_node2.set_uid(2222);
     attribution_node2.set_tag("location2");
 
-    AttributionNode attribution_node3;
+    AttributionNodeInternal attribution_node3;
     attribution_node3.set_uid(3333);
     attribution_node3.set_tag("location3");
-    std::vector<AttributionNode> attribution_nodes = {attribution_node1, attribution_node2,
-                                                      attribution_node3};
+    std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
+                                                              attribution_node3};
 
     // Set up the event
     LogEvent event(10, 12345);
@@ -298,15 +299,15 @@
 }
 
 TEST(AtomMatcherTest, TestWriteAtomToProto) {
-    AttributionNode attribution_node1;
+    AttributionNodeInternal attribution_node1;
     attribution_node1.set_uid(1111);
     attribution_node1.set_tag("location1");
 
-    AttributionNode attribution_node2;
+    AttributionNodeInternal attribution_node2;
     attribution_node2.set_uid(2222);
     attribution_node2.set_tag("location2");
 
-    std::vector<AttributionNode> attribution_nodes = {attribution_node1, attribution_node2};
+    std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2};
 
     // Set up the event
     LogEvent event(4, 12345);
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 1023ea4..2320a9d 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -60,19 +60,19 @@
 
 TEST(AtomMatcherTest, TestAttributionMatcher) {
     UidMap uidMap;
-    AttributionNode attribution_node1;
+    AttributionNodeInternal attribution_node1;
     attribution_node1.set_uid(1111);
     attribution_node1.set_tag("location1");
 
-    AttributionNode attribution_node2;
+    AttributionNodeInternal attribution_node2;
     attribution_node2.set_uid(2222);
     attribution_node2.set_tag("location2");
 
-    AttributionNode attribution_node3;
+    AttributionNodeInternal attribution_node3;
     attribution_node3.set_uid(3333);
     attribution_node3.set_tag("location3");
-    std::vector<AttributionNode> attribution_nodes =
-        { attribution_node1, attribution_node2, attribution_node3 };
+    std::vector<AttributionNodeInternal> attribution_nodes = {attribution_node1, attribution_node2,
+                                                              attribution_node3};
 
     // Set up the event
     LogEvent event(TAG_ID, 0);
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index b649215..2fcde29 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -25,14 +25,14 @@
 TEST(LogEventTest, TestLogParsing) {
     LogEvent event1(1, 2000);
 
-    std::vector<AttributionNode> nodes;
+    std::vector<AttributionNodeInternal> nodes;
 
-    AttributionNode node1;
+    AttributionNodeInternal node1;
     node1.set_uid(1000);
     node1.set_tag("tag1");
     nodes.push_back(node1);
 
-    AttributionNode node2;
+    AttributionNodeInternal node2;
     node2.set_uid(2000);
     node2.set_tag("tag2");
     nodes.push_back(node2);
@@ -92,17 +92,17 @@
 TEST(LogEventTest, TestLogParsing2) {
     LogEvent event1(1, 2000);
 
-    std::vector<AttributionNode> nodes;
+    std::vector<AttributionNodeInternal> nodes;
 
     event1.write("hello");
 
     // repeated msg can be in the middle
-    AttributionNode node1;
+    AttributionNodeInternal node1;
     node1.set_uid(1000);
     node1.set_tag("tag1");
     nodes.push_back(node1);
 
-    AttributionNode node2;
+    AttributionNodeInternal node2;
     node2.set_uid(2000);
     node2.set_tag("tag2");
     nodes.push_back(node2);
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 038d449..3dc3fd1 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -58,9 +58,9 @@
 }
 
 void writeAttributionNodesToEvent(LogEvent* event, const std::vector<int> &uids) {
-    std::vector<AttributionNode> nodes;
+    std::vector<AttributionNodeInternal> nodes;
     for (size_t i = 0; i < uids.size(); ++i) {
-        AttributionNode node;
+        AttributionNodeInternal node;
         node.set_uid(uids[i]);
         nodes.push_back(node);
     }
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index 0228004..7a7e000 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -75,41 +75,40 @@
         android::String16("APP3"), 333 /* uid */, 2 /* version code*/);
 
     // GMS core node is in the middle.
-    std::vector<AttributionNode> attributions1 =
-        {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
-         CreateAttribution(333, "App3")};
+    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
+                                                          CreateAttribution(222, "GMSCoreModule1"),
+                                                          CreateAttribution(333, "App3")};
 
     // GMS core node is the last one.
-    std::vector<AttributionNode> attributions2 =
-        {CreateAttribution(111, "App1"), CreateAttribution(333, "App3"),
-         CreateAttribution(222, "GMSCoreModule1")};
+    std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App1"),
+                                                          CreateAttribution(333, "App3"),
+                                                          CreateAttribution(222, "GMSCoreModule1")};
 
     // GMS core node is the first one.
-    std::vector<AttributionNode> attributions3 =
-        {CreateAttribution(222, "GMSCoreModule1"), CreateAttribution(333, "App3")};
+    std::vector<AttributionNodeInternal> attributions3 = {CreateAttribution(222, "GMSCoreModule1"),
+                                                          CreateAttribution(333, "App3")};
 
     // Single GMS core node.
-    std::vector<AttributionNode> attributions4 =
-        {CreateAttribution(222, "GMSCoreModule1")};
+    std::vector<AttributionNodeInternal> attributions4 = {CreateAttribution(222, "GMSCoreModule1")};
 
     // GMS core has another uid.
-    std::vector<AttributionNode> attributions5 =
-        {CreateAttribution(111, "App1"), CreateAttribution(444, "GMSCoreModule2"),
-         CreateAttribution(333, "App3")};
+    std::vector<AttributionNodeInternal> attributions5 = {CreateAttribution(111, "App1"),
+                                                          CreateAttribution(444, "GMSCoreModule2"),
+                                                          CreateAttribution(333, "App3")};
 
     // Multiple GMS core nodes.
-    std::vector<AttributionNode> attributions6 =
-        {CreateAttribution(444, "GMSCoreModule2"), CreateAttribution(222, "GMSCoreModule1")};
+    std::vector<AttributionNodeInternal> attributions6 = {CreateAttribution(444, "GMSCoreModule2"),
+                                                          CreateAttribution(222, "GMSCoreModule1")};
 
     // No GMS core nodes.
-    std::vector<AttributionNode> attributions7 =
-        {CreateAttribution(111, "App1"), CreateAttribution(333, "App3")};
-    std::vector<AttributionNode> attributions8 = {CreateAttribution(111, "App1")};
+    std::vector<AttributionNodeInternal> attributions7 = {CreateAttribution(111, "App1"),
+                                                          CreateAttribution(333, "App3")};
+    std::vector<AttributionNodeInternal> attributions8 = {CreateAttribution(111, "App1")};
 
     // GMS core node with isolated uid.
     const int isolatedUid = 666;
-    std::vector<AttributionNode> attributions9 =
-        {CreateAttribution(isolatedUid, "GMSCoreModule3")};
+    std::vector<AttributionNodeInternal> attributions9 = {
+            CreateAttribution(isolatedUid, "GMSCoreModule3")};
 
     std::vector<std::unique_ptr<LogEvent>> events;
     // Events 1~4 are in the 1st bucket.
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
index 4dffd13..01348bd 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
@@ -78,13 +78,13 @@
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
-    std::vector<AttributionNode> attributions1 = {CreateAttribution(111, "App1"),
-                                                  CreateAttribution(222, "GMSCoreModule1"),
-                                                  CreateAttribution(222, "GMSCoreModule2")};
+    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
+                                                          CreateAttribution(222, "GMSCoreModule1"),
+                                                          CreateAttribution(222, "GMSCoreModule2")};
 
-    std::vector<AttributionNode> attributions2 = {CreateAttribution(333, "App2"),
-                                                  CreateAttribution(222, "GMSCoreModule1"),
-                                                  CreateAttribution(555, "GMSCoreModule2")};
+    std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(333, "App2"),
+                                                          CreateAttribution(222, "GMSCoreModule1"),
+                                                          CreateAttribution(555, "GMSCoreModule2")};
 
     std::vector<std::unique_ptr<LogEvent>> events;
     events.push_back(
@@ -284,13 +284,13 @@
     auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
-    std::vector<AttributionNode> attributions1 = {CreateAttribution(111, "App1"),
-                                                  CreateAttribution(222, "GMSCoreModule1"),
-                                                  CreateAttribution(222, "GMSCoreModule2")};
+    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
+                                                          CreateAttribution(222, "GMSCoreModule1"),
+                                                          CreateAttribution(222, "GMSCoreModule2")};
 
-    std::vector<AttributionNode> attributions2 = {CreateAttribution(333, "App2"),
-                                                  CreateAttribution(222, "GMSCoreModule1"),
-                                                  CreateAttribution(555, "GMSCoreModule2")};
+    std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(333, "App2"),
+                                                          CreateAttribution(222, "GMSCoreModule1"),
+                                                          CreateAttribution(555, "GMSCoreModule2")};
 
     std::vector<std::unique_ptr<LogEvent>> events;
 
@@ -464,13 +464,13 @@
         EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
         EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
-        std::vector<AttributionNode> attributions1 = {CreateAttribution(111, "App1"),
-                                                      CreateAttribution(222, "GMSCoreModule1"),
-                                                      CreateAttribution(222, "GMSCoreModule2")};
+        std::vector<AttributionNodeInternal> attributions1 = {
+                CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+                CreateAttribution(222, "GMSCoreModule2")};
 
-        std::vector<AttributionNode> attributions2 = {CreateAttribution(333, "App2"),
-                                                      CreateAttribution(222, "GMSCoreModule1"),
-                                                      CreateAttribution(555, "GMSCoreModule2")};
+        std::vector<AttributionNodeInternal> attributions2 = {
+                CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+                CreateAttribution(555, "GMSCoreModule2")};
 
         std::vector<std::unique_ptr<LogEvent>> events;
 
@@ -629,13 +629,13 @@
         EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
         EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
-        std::vector<AttributionNode> attributions1 = {CreateAttribution(111, "App1"),
-                                                      CreateAttribution(222, "GMSCoreModule1"),
-                                                      CreateAttribution(222, "GMSCoreModule2")};
+        std::vector<AttributionNodeInternal> attributions1 = {
+                CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+                CreateAttribution(222, "GMSCoreModule2")};
 
-        std::vector<AttributionNode> attributions2 = {CreateAttribution(333, "App2"),
-                                                      CreateAttribution(222, "GMSCoreModule1"),
-                                                      CreateAttribution(555, "GMSCoreModule2")};
+        std::vector<AttributionNodeInternal> attributions2 = {
+                CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+                CreateAttribution(555, "GMSCoreModule2")};
 
         std::vector<std::unique_ptr<LogEvent>> events;
 
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index 1b51780..c874d92 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -134,8 +134,8 @@
         CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
                                       bucketStartTimeNs + 2 * bucketSizeNs - 100);
 
-    std::vector<AttributionNode> attributions =
-        {CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")};
+    std::vector<AttributionNodeInternal> attributions = {
+            CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")};
     auto syncOnEvent1 =
         CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50);
     auto syncOffEvent1 =
@@ -249,8 +249,8 @@
             CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
                                           bucketStartTimeNs + 2 * bucketSizeNs - 100);
 
-    std::vector<AttributionNode> attributions = {CreateAttribution(appUid, "App1"),
-                                                 CreateAttribution(appUid + 1, "GMSCoreModule1")};
+    std::vector<AttributionNodeInternal> attributions = {
+            CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")};
     auto syncOnEvent1 = CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50);
     auto syncOffEvent1 =
             CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index efdab98..9153795 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -60,13 +60,13 @@
     return config;
 }
 
-std::vector<AttributionNode> attributions1 = {CreateAttribution(111, "App1"),
-                                              CreateAttribution(222, "GMSCoreModule1"),
-                                              CreateAttribution(222, "GMSCoreModule2")};
+std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
+                                                      CreateAttribution(222, "GMSCoreModule1"),
+                                                      CreateAttribution(222, "GMSCoreModule2")};
 
-std::vector<AttributionNode> attributions2 = {CreateAttribution(111, "App2"),
-                                              CreateAttribution(222, "GMSCoreModule1"),
-                                              CreateAttribution(222, "GMSCoreModule2")};
+std::vector<AttributionNodeInternal> attributions2 = {CreateAttribution(111, "App2"),
+                                                      CreateAttribution(222, "GMSCoreModule1"),
+                                                      CreateAttribution(222, "GMSCoreModule2")};
 
 /*
 Events:
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index b7acef7..7568348 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -291,8 +291,8 @@
 }
 
 std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(
-    const std::vector<AttributionNode>& attributions, const string& wakelockName,
-    const WakelockStateChanged::State state, uint64_t timestampNs) {
+        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
+        const WakelockStateChanged::State state, uint64_t timestampNs) {
     auto event = std::make_unique<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, timestampNs);
     event->write(attributions);
     event->write(android::os::WakeLockLevelEnum::PARTIAL_WAKE_LOCK);
@@ -303,15 +303,15 @@
 }
 
 std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
-    const std::vector<AttributionNode>& attributions,
-    const string& wakelockName, uint64_t timestampNs) {
+        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
+        uint64_t timestampNs) {
     return CreateWakelockStateChangedEvent(
         attributions, wakelockName, WakelockStateChanged::ACQUIRE, timestampNs);
 }
 
 std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
-    const std::vector<AttributionNode>& attributions,
-    const string& wakelockName, uint64_t timestampNs) {
+        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
+        uint64_t timestampNs) {
     return CreateWakelockStateChangedEvent(
         attributions, wakelockName, WakelockStateChanged::RELEASE, timestampNs);
 }
@@ -339,8 +339,8 @@
 }
 
 std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
-    const std::vector<AttributionNode>& attributions,
-    const string& name, const SyncStateChanged::State state, uint64_t timestampNs) {
+        const std::vector<AttributionNodeInternal>& attributions, const string& name,
+        const SyncStateChanged::State state, uint64_t timestampNs) {
     auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs);
     event->write(attributions);
     event->write(name);
@@ -350,12 +350,14 @@
 }
 
 std::unique_ptr<LogEvent> CreateSyncStartEvent(
-    const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs){
+        const std::vector<AttributionNodeInternal>& attributions, const string& name,
+        uint64_t timestampNs) {
     return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::ON, timestampNs);
 }
 
 std::unique_ptr<LogEvent> CreateSyncEndEvent(
-    const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs) {
+        const std::vector<AttributionNodeInternal>& attributions, const string& name,
+        uint64_t timestampNs) {
     return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs);
 }
 
@@ -396,8 +398,8 @@
     return processor;
 }
 
-AttributionNode CreateAttribution(const int& uid, const string& tag) {
-    AttributionNode attribution;
+AttributionNodeInternal CreateAttribution(const int& uid, const string& tag) {
+    AttributionNodeInternal attribution;
     attribution.set_uid(uid);
     attribution.set_tag(tag);
     return attribution;
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 5d83ed7..1708cc3 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -15,10 +15,11 @@
 #pragma once
 
 #include <gtest/gtest.h>
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "statslog.h"
-#include "src/logd/LogEvent.h"
 #include "src/StatsLogProcessor.h"
+#include "src/logd/LogEvent.h"
+#include "statslog.h"
 
 namespace android {
 namespace os {
@@ -119,11 +120,13 @@
 
 // Create log event when the app sync starts.
 std::unique_ptr<LogEvent> CreateSyncStartEvent(
-    const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs);
+        const std::vector<AttributionNodeInternal>& attributions, const string& name,
+        uint64_t timestampNs);
 
 // Create log event when the app sync ends.
 std::unique_ptr<LogEvent> CreateSyncEndEvent(
-    const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs);
+        const std::vector<AttributionNodeInternal>& attributions, const string& name,
+        uint64_t timestampNs);
 
 // Create log event when the app sync ends.
 std::unique_ptr<LogEvent> CreateAppCrashEvent(
@@ -131,20 +134,20 @@
 
 // Create log event for acquiring wakelock.
 std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
-    const std::vector<AttributionNode>& attributions,
-    const string& wakelockName, uint64_t timestampNs);
+        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
+        uint64_t timestampNs);
 
 // Create log event for releasing wakelock.
 std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
-    const std::vector<AttributionNode>& attributions,
-    const string& wakelockName, uint64_t timestampNs);
+        const std::vector<AttributionNodeInternal>& attributions, const string& wakelockName,
+        uint64_t timestampNs);
 
 // Create log event for releasing wakelock.
 std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(
     int isolatedUid, int hostUid, bool is_create, uint64_t timestampNs);
 
-// Helper function to create an AttributionNode proto.
-AttributionNode CreateAttribution(const int& uid, const string& tag);
+// Helper function to create an AttributionNodeInternal proto.
+AttributionNodeInternal CreateAttribution(const int& uid, const string& tag);
 
 // Create a statsd log event processor upon the start time in seconds, config and key.
 sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
diff --git a/cmds/statsd/tools/Android.mk b/cmds/statsd/tools/Android.mk
index faf2d2c..7253c96 100644
--- a/cmds/statsd/tools/Android.mk
+++ b/cmds/statsd/tools/Android.mk
@@ -16,5 +16,5 @@
 
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
-# Include the sub-makefiles
+#Include the sub-makefiles
 include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk
index a65095f..c7e4c7b 100644
--- a/cmds/statsd/tools/dogfood/Android.mk
+++ b/cmds/statsd/tools/dogfood/Android.mk
@@ -24,7 +24,6 @@
 LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite \
                                statsdprotolite
 
-LOCAL_PROTOC_OPTIMIZE_TYPE := lite
 LOCAL_PRIVILEGED_MODULE := true
 LOCAL_DEX_PREOPT := false
 LOCAL_CERTIFICATE := platform
diff --git a/cmds/statsd/tools/loadtest/Android.mk b/cmds/statsd/tools/loadtest/Android.mk
index f5722c2..091f184 100644
--- a/cmds/statsd/tools/loadtest/Android.mk
+++ b/cmds/statsd/tools/loadtest/Android.mk
@@ -19,15 +19,11 @@
 LOCAL_PACKAGE_NAME := StatsdLoadtest
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SRC_FILES += ../../src/stats_log.proto \
-                   ../../src/atoms.proto \
-                   ../../src/perfetto/perfetto_config.proto \
-                   ../../src/statsd_config.proto
-LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite
 
-LOCAL_PROTOC_OPTIMIZE_TYPE := lite
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite \
+                               statsdprotolite
+
 LOCAL_CERTIFICATE := platform
 LOCAL_PRIVILEGED_MODULE := true
 LOCAL_DEX_PREOPT := false
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
index bed4d98..a12def0 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
@@ -50,7 +50,7 @@
 
 import com.android.os.StatsLog.ConfigMetricsReport;
 import com.android.os.StatsLog.ConfigMetricsReportList;
-import com.android.os.StatsLog.StatsdStatsReport;
+import com.android.os.StatsLogCommon.StatsdStatsReport;
 import com.android.internal.os.StatsdConfigProto.TimeUnit;
 
 import java.util.ArrayList;
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java
index e63150f..58cbcd8 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java
@@ -16,11 +16,8 @@
 package com.android.statsd.loadtest;
 
 import android.content.Context;
-import android.util.Log;
-import com.android.os.StatsLog.StatsdStatsReport;
+import com.android.os.StatsLogCommon.StatsdStatsReport;
 import com.android.internal.os.StatsdConfigProto.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 public class StatsdStatsRecorder extends PerfDataRecorder {
     private static final String TAG = "loadtest.StatsdStatsRecorder";
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index f3997a4..dac03cd 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1822,7 +1822,7 @@
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(IILandroid/view/textclassifier/TextSelection;)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionStarted(I)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
 Landroid/view/textservice/TextServicesManager;->isSpellCheckerEnabled()Z
-Landroid/view/TextureView;->mLayer:Landroid/view/HardwareLayer;
+Landroid/view/TextureView;->mLayer:Landroid/view/TextureLayer;
 Landroid/view/TextureView;->mNativeWindow:J
 Landroid/view/TextureView;->mSurface:Landroid/graphics/SurfaceTexture;
 Landroid/view/TextureView;->mUpdateListener:Landroid/graphics/SurfaceTexture$OnFrameAvailableListener;
@@ -2403,11 +2403,45 @@
 Lcom/android/okhttp/OkHttpClient;->setProtocols(Ljava/util/List;)Lcom/android/okhttp/OkHttpClient;
 Lcom/android/okhttp/okio/ByteString;->readObject(Ljava/io/ObjectInputStream;)V
 Lcom/android/okhttp/okio/ByteString;->writeObject(Ljava/io/ObjectOutputStream;)V
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->getAlpnSelectedProtocol()[B
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->getApplicationProtocol()Ljava/lang/String;
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->getApplicationProtocols()[Ljava/lang/String;
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->getChannelId()[B
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->getHandshakeApplicationProtocol()Ljava/lang/String;
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->getHostname()Ljava/lang/String;
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->getHostnameOrIP()Ljava/lang/String;
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->getNpnSelectedProtocol()[B
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->getSoWriteTimeout()I
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->setAlpnProtocols([Ljava/lang/String;)V
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->setAlpnProtocols([B)V
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->setApplicationProtocols([Ljava/lang/String;)V
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->setChannelIdEnabled(Z)V
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->setChannelIdPrivateKey(Ljava/security/PrivateKey;)V
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->setHandshakeTimeout(I)V
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->setHostname(Ljava/lang/String;)V
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->setNpnProtocols([B)V
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->setSoWriteTimeout(I)V
+Lcom/android/org/conscrypt/AbstractConscryptSocket;->setUseSessionTickets(Z)V
+Lcom/android/org/conscrypt/ConscryptSocketBase;->getHostname()Ljava/lang/String;
+Lcom/android/org/conscrypt/ConscryptSocketBase;->getHostnameOrIP()Ljava/lang/String;
+Lcom/android/org/conscrypt/ConscryptSocketBase;->getSoWriteTimeout()I
+Lcom/android/org/conscrypt/ConscryptSocketBase;->setHandshakeTimeout(I)V
+Lcom/android/org/conscrypt/ConscryptSocketBase;->setHostname(Ljava/lang/String;)V
+Lcom/android/org/conscrypt/ConscryptSocketBase;->setSoWriteTimeout(I)V
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getAlpnSelectedProtocol()[B
+Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getChannelId()[B
+Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getHostname()Ljava/lang/String;
+Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getHostnameOrIP()Ljava/lang/String;
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getNpnSelectedProtocol()[B
+Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getSoWriteTimeout()I
+Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setAlpnProtocols([Ljava/lang/String;)V
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setAlpnProtocols([B)V
+Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setChannelIdEnabled(Z)V
+Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setChannelIdPrivateKey(Ljava/security/PrivateKey;)V
+Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setHandshakeTimeout(I)V
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setHostname(Ljava/lang/String;)V
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setNpnProtocols([B)V
+Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setSoWriteTimeout(I)V
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->setUseSessionTickets(Z)V
 Lcom/android/org/conscrypt/TrustManagerImpl;-><init>(Ljava/security/KeyStore;)V
 Ldalvik/system/BaseDexClassLoader;->getLdLibraryPath()Ljava/lang/String;
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 784c3f8..95c2b2c 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -2289,7 +2289,7 @@
 android.view.Gravity
 android.view.HandlerActionQueue
 android.view.HandlerActionQueue$HandlerAction
-android.view.HardwareLayer
+android.view.TextureLayer
 android.view.IGraphicsStats
 android.view.IGraphicsStats$Stub
 android.view.IGraphicsStats$Stub$Proxy
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 83fe4dd..33deca7 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5553,13 +5553,7 @@
         if (mParent != null) {
             throw new IllegalStateException("Can only be called on top-level activity");
         }
-        if (Looper.myLooper() != mMainThread.getLooper()) {
-            throw new IllegalStateException("Must be called from main thread");
-        }
-        try {
-            ActivityManager.getService().requestActivityRelaunch(mToken);
-        } catch (RemoteException e) {
-        }
+        mMainThread.handleRelaunchActivityLocally(mToken);
     }
 
     /**
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 0c98267..feeb0f2 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -154,8 +154,8 @@
      * Callback for window manager to let activity manager know that we are finally starting the
      * app transition;
      *
-     * @param reasons A map from stack id to a reason integer why the transition was started,, which
-     *                must be one of the APP_TRANSITION_* values.
+     * @param reasons A map from windowing mode to a reason integer why the transition was started,
+     *                which must be one of the APP_TRANSITION_* values.
      * @param timestamp The time at which the app transition started in
      *                  {@link SystemClock#uptimeMillis()} timebase.
      */
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index d5430f0..09dcbf2 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -164,7 +164,7 @@
 
     /**
      * Whether the activity should be launched into LockTask mode.
-     * @see #setLockTaskMode(boolean)
+     * @see #setLockTaskEnabled(boolean)
      */
     private static final String KEY_LOCK_TASK_MODE = "android:activity.lockTaskMode";
 
@@ -1148,7 +1148,7 @@
      * @see Activity#startLockTask()
      * @see android.app.admin.DevicePolicyManager#setLockTaskPackages(ComponentName, String[])
      */
-    public ActivityOptions setLockTaskMode(boolean lockTaskMode) {
+    public ActivityOptions setLockTaskEnabled(boolean lockTaskMode) {
         mLockTaskMode = lockTaskMode;
         return this;
     }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index a69b0ee..302ffed 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4292,19 +4292,15 @@
             View.mDebugViewAttributes = debugViewAttributes;
 
             // request all activities to relaunch for the changes to take place
-            requestRelaunchAllActivities();
+            relaunchAllActivities();
         }
     }
 
-    private void requestRelaunchAllActivities() {
+    private void relaunchAllActivities() {
         for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
             final Activity activity = entry.getValue().activity;
             if (!activity.mFinished) {
-                try {
-                    ActivityManager.getService().requestActivityRelaunch(entry.getKey());
-                } catch (RemoteException e) {
-                    throw e.rethrowFromSystemServer();
-                }
+                handleRelaunchActivityLocally(entry.getKey());
             }
         }
     }
@@ -4679,42 +4675,81 @@
             throw e.rethrowFromSystemServer();
         }
 
-        // Need to ensure state is saved.
-        if (!r.paused) {
-            performPauseActivity(r, false, "handleRelaunchActivity",
-                    null /* pendingActions */);
+        handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
+                pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
+
+        if (pendingActions != null) {
+            // Only report a successful relaunch to WindowManager.
+            pendingActions.setReportRelaunchToWindowManager(true);
         }
-        if (!r.stopped) {
-            callActivityOnStop(r, true /* saveState */, "handleRelaunchActivity");
+    }
+
+    /** Performs the activity relaunch locally vs. requesting from system-server. */
+    void handleRelaunchActivityLocally(IBinder token) {
+        if (Looper.myLooper() != getLooper()) {
+            throw new IllegalStateException("Must be called from main thread");
         }
 
-        handleDestroyActivity(r.token, false, configChanges, true, "handleRelaunchActivity");
+        final ActivityClientRecord r = mActivities.get(token);
+        if (r == null) {
+            return;
+        }
+
+        final int prevState = r.getLifecycleState();
+
+        if (prevState < ON_RESUME) {
+            Log.w(TAG, "Activity needs to be already resumed in other to be relaunched.");
+            return;
+        }
+
+        // TODO(b/73747058): Investigate converting this to use transaction to relaunch.
+        handleRelaunchActivityInner(r, 0 /* configChanges */, null /* pendingResults */,
+                null /* pendingIntents */, null /* pendingActions */, prevState != ON_RESUME,
+                r.overrideConfig, "handleRelaunchActivityLocally");
+
+        // Restore back to the previous state before relaunch if needed.
+        if (prevState != r.getLifecycleState()) {
+            mTransactionExecutor.cycleToPath(r, prevState);
+        }
+    }
+
+    private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
+            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
+            PendingTransactionActions pendingActions, boolean startsNotResumed,
+            Configuration overrideConfig, String reason) {
+        // Need to ensure state is saved.
+        if (!r.paused) {
+            performPauseActivity(r, false, reason, null /* pendingActions */);
+        }
+        if (!r.stopped) {
+            callActivityOnStop(r, true /* saveState */, reason);
+        }
+
+        handleDestroyActivity(r.token, false, configChanges, true, reason);
 
         r.activity = null;
         r.window = null;
         r.hideForNow = false;
         r.nextIdle = null;
         // Merge any pending results and pending intents; don't just replace them
-        if (tmp.pendingResults != null) {
+        if (pendingResults != null) {
             if (r.pendingResults == null) {
-                r.pendingResults = tmp.pendingResults;
+                r.pendingResults = pendingResults;
             } else {
-                r.pendingResults.addAll(tmp.pendingResults);
+                r.pendingResults.addAll(pendingResults);
             }
         }
-        if (tmp.pendingIntents != null) {
+        if (pendingIntents != null) {
             if (r.pendingIntents == null) {
-                r.pendingIntents = tmp.pendingIntents;
+                r.pendingIntents = pendingIntents;
             } else {
-                r.pendingIntents.addAll(tmp.pendingIntents);
+                r.pendingIntents.addAll(pendingIntents);
             }
         }
-        r.startsNotResumed = tmp.startsNotResumed;
-        r.overrideConfig = tmp.overrideConfig;
+        r.startsNotResumed = startsNotResumed;
+        r.overrideConfig = overrideConfig;
 
         handleLaunchActivity(r, pendingActions);
-        // Only report a successful relaunch to WindowManager.
-        pendingActions.setReportRelaunchToWindowManager(true);
     }
 
     @Override
@@ -5117,7 +5152,7 @@
         newConfig.assetsSeq = (mConfiguration != null ? mConfiguration.assetsSeq : 0) + 1;
         handleConfigurationChanged(newConfig, null);
 
-        requestRelaunchAllActivities();
+        relaunchAllActivities();
     }
 
     static void freeTextLayoutCachesIfNeeded(int configDiff) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 13117bc..f8f50a2 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2802,4 +2802,13 @@
             return mArtManager;
         }
     }
+
+    @Override
+    public String getSystemTextClassifierPackageName() {
+        try {
+            return mPM.getSystemTextClassifierPackageName();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
 }
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 54fd0c4..ac301b3 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -630,7 +630,6 @@
     void setHasTopUi(boolean hasTopUi);
 
     // Start of O transactions
-    void requestActivityRelaunch(in IBinder token);
     /**
      * Updates override configuration applied to specific display.
      * @param values Update values for display configuration. If null is passed it will request the
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 28ecd98..233e09d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1825,6 +1825,7 @@
              * @param label the label to display while the action is being prepared to execute
              * @return this object for method chaining
              */
+            @Deprecated
             public WearableExtender setInProgressLabel(CharSequence label) {
                 mInProgressLabel = label;
                 return this;
@@ -1836,6 +1837,7 @@
              *
              * @return the label to display while the action is being prepared to execute
              */
+            @Deprecated
             public CharSequence getInProgressLabel() {
                 return mInProgressLabel;
             }
@@ -1847,6 +1849,7 @@
              * @param label the label to confirm the action should be executed
              * @return this object for method chaining
              */
+            @Deprecated
             public WearableExtender setConfirmLabel(CharSequence label) {
                 mConfirmLabel = label;
                 return this;
@@ -1858,6 +1861,7 @@
              *
              * @return the label to confirm the action should be executed
              */
+            @Deprecated
             public CharSequence getConfirmLabel() {
                 return mConfirmLabel;
             }
@@ -1869,6 +1873,7 @@
              * @param label the label to display to cancel the action
              * @return this object for method chaining
              */
+            @Deprecated
             public WearableExtender setCancelLabel(CharSequence label) {
                 mCancelLabel = label;
                 return this;
@@ -1880,6 +1885,7 @@
              *
              * @return the label to display to cancel the action
              */
+            @Deprecated
             public CharSequence getCancelLabel() {
                 return mCancelLabel;
             }
@@ -4587,7 +4593,8 @@
             big.setViewVisibility(R.id.notification_material_reply_text_3, View.GONE);
             big.setTextViewText(R.id.notification_material_reply_text_3, null);
 
-            big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
+            big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
+                    R.dimen.notification_content_margin);
         }
 
         private RemoteViews applyStandardTemplateWithActions(int layoutId) {
@@ -4608,16 +4615,7 @@
             if (N > 0) {
                 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
                 big.setViewVisibility(R.id.actions, View.VISIBLE);
-                if (p.ambient) {
-                    big.setInt(R.id.actions, "setBackgroundColor", Color.TRANSPARENT);
-                } else if (isColorized()) {
-                    big.setInt(R.id.actions, "setBackgroundColor", getActionBarColor());
-                } else {
-                    big.setInt(R.id.actions, "setBackgroundColor", mContext.getColor(
-                            R.color.notification_action_list));
-                }
-                big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
-                        R.dimen.notification_action_list_height);
+                big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
                 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
                 for (int i=0; i<N; i++) {
                     Action action = mActions.get(i);
@@ -5226,6 +5224,7 @@
             if (mStyle != null) {
                 mStyle.reduceImageSizes(mContext);
                 mStyle.purgeResources();
+                mStyle.validate(mContext);
                 mStyle.buildStyled(mN);
             }
             mN.reduceImageSizes(mContext);
@@ -5795,6 +5794,13 @@
          */
         public void reduceImageSizes(Context context) {
         }
+
+        /**
+         * Validate that this style was properly composed. This is called at build time.
+         * @hide
+         */
+        public void validate(Context context) {
+        }
     }
 
     /**
@@ -6185,12 +6191,23 @@
          * @param user Required - The person displayed for any messages that are sent by the
          * user. Any messages added with {@link #addMessage(Notification.MessagingStyle.Message)}
          * who don't have a Person associated with it will be displayed as if they were sent
-         * by this user. The user also needs to have a valid name associated with it.
+         * by this user. The user also needs to have a valid name associated with it, which will
+         * be enforced starting in Android P.
          */
         public MessagingStyle(@NonNull Person user) {
             mUser = user;
-            if (user == null || user.getName() == null) {
-                throw new RuntimeException("user must be valid and have a name");
+        }
+
+        /**
+         * Validate that this style was properly composed. This is called at build time.
+         * @hide
+         */
+        @Override
+        public void validate(Context context) {
+            super.validate(context);
+            if (context.getApplicationInfo().targetSdkVersion
+                    >= Build.VERSION_CODES.P && (mUser == null || mUser.getName() == null)) {
+                throw new RuntimeException("User must be valid and have a name.");
             }
         }
 
@@ -8071,6 +8088,7 @@
         /**
          * Set an icon that goes with the content of this notification.
          */
+        @Deprecated
         public WearableExtender setContentIcon(int icon) {
             mContentIcon = icon;
             return this;
@@ -8079,6 +8097,7 @@
         /**
          * Get an icon that goes with the content of this notification.
          */
+        @Deprecated
         public int getContentIcon() {
             return mContentIcon;
         }
@@ -8089,6 +8108,7 @@
          * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
          * @see #setContentIcon
          */
+        @Deprecated
         public WearableExtender setContentIconGravity(int contentIconGravity) {
             mContentIconGravity = contentIconGravity;
             return this;
@@ -8100,6 +8120,7 @@
          * {@link android.view.Gravity#END}. The default value is {@link android.view.Gravity#END}.
          * @see #getContentIcon
          */
+        @Deprecated
         public int getContentIconGravity() {
             return mContentIconGravity;
         }
@@ -8147,6 +8168,7 @@
          * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
          * The default value is {@link android.view.Gravity#BOTTOM}.
          */
+        @Deprecated
         public WearableExtender setGravity(int gravity) {
             mGravity = gravity;
             return this;
@@ -8158,6 +8180,7 @@
          * {@link android.view.Gravity#CENTER_VERTICAL} and {@link android.view.Gravity#BOTTOM}.
          * The default value is {@link android.view.Gravity#BOTTOM}.
          */
+        @Deprecated
         public int getGravity() {
             return mGravity;
         }
@@ -8171,6 +8194,7 @@
          * documentation for the preset in question. See also
          * {@link #setCustomContentHeight} and {@link #getCustomSizePreset}.
          */
+        @Deprecated
         public WearableExtender setCustomSizePreset(int sizePreset) {
             mCustomSizePreset = sizePreset;
             return this;
@@ -8184,6 +8208,7 @@
          * using {@link #setDisplayIntent}. Check the documentation for the preset in question.
          * See also {@link #setCustomContentHeight} and {@link #setCustomSizePreset}.
          */
+        @Deprecated
         public int getCustomSizePreset() {
             return mCustomSizePreset;
         }
@@ -8195,6 +8220,7 @@
          * {@link android.app.Notification.WearableExtender#setCustomSizePreset} and
          * {@link #getCustomContentHeight}.
          */
+        @Deprecated
         public WearableExtender setCustomContentHeight(int height) {
             mCustomContentHeight = height;
             return this;
@@ -8206,6 +8232,7 @@
          * using {@link #setDisplayIntent}. See also {@link #setCustomSizePreset} and
          * {@link #setCustomContentHeight}.
          */
+        @Deprecated
         public int getCustomContentHeight() {
             return mCustomContentHeight;
         }
@@ -8256,6 +8283,7 @@
          * @param hintHideIcon {@code true} to hide the icon, {@code false} otherwise.
          * @return this object for method chaining
          */
+        @Deprecated
         public WearableExtender setHintHideIcon(boolean hintHideIcon) {
             setFlag(FLAG_HINT_HIDE_ICON, hintHideIcon);
             return this;
@@ -8266,6 +8294,7 @@
          * @return {@code true} if this icon should not be displayed, false otherwise.
          * The default value is {@code false} if this was never set.
          */
+        @Deprecated
         public boolean getHintHideIcon() {
             return (mFlags & FLAG_HINT_HIDE_ICON) != 0;
         }
@@ -8275,6 +8304,7 @@
          * displayed, and other semantic content should be hidden. This hint is only applicable
          * to sub-pages added using {@link #addPage}.
          */
+        @Deprecated
         public WearableExtender setHintShowBackgroundOnly(boolean hintShowBackgroundOnly) {
             setFlag(FLAG_HINT_SHOW_BACKGROUND_ONLY, hintShowBackgroundOnly);
             return this;
@@ -8285,6 +8315,7 @@
          * displayed, and other semantic content should be hidden. This hint is only applicable
          * to sub-pages added using {@link android.app.Notification.WearableExtender#addPage}.
          */
+        @Deprecated
         public boolean getHintShowBackgroundOnly() {
             return (mFlags & FLAG_HINT_SHOW_BACKGROUND_ONLY) != 0;
         }
@@ -8296,6 +8327,7 @@
          * @param hintAvoidBackgroundClipping {@code true} to avoid clipping if possible.
          * @return this object for method chaining
          */
+        @Deprecated
         public WearableExtender setHintAvoidBackgroundClipping(
                 boolean hintAvoidBackgroundClipping) {
             setFlag(FLAG_HINT_AVOID_BACKGROUND_CLIPPING, hintAvoidBackgroundClipping);
@@ -8309,6 +8341,7 @@
          * @return {@code true} if it's ok if the background is clipped on the screen, false
          * otherwise. The default value is {@code false} if this was never set.
          */
+        @Deprecated
         public boolean getHintAvoidBackgroundClipping() {
             return (mFlags & FLAG_HINT_AVOID_BACKGROUND_CLIPPING) != 0;
         }
@@ -8320,6 +8353,7 @@
          *     {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
          * @return this object for method chaining
          */
+        @Deprecated
         public WearableExtender setHintScreenTimeout(int timeout) {
             mHintScreenTimeout = timeout;
             return this;
@@ -8331,6 +8365,7 @@
          * @return the duration in milliseconds if > 0, or either one of the sentinel values
          *     {@link #SCREEN_TIMEOUT_SHORT} or {@link #SCREEN_TIMEOUT_LONG}.
          */
+        @Deprecated
         public int getHintScreenTimeout() {
             return mHintScreenTimeout;
         }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 14b2119..9d4d943 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -25,6 +25,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.StringDef;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -58,6 +59,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.ContactsContract.Directory;
+import android.provider.Settings;
 import android.security.AttestedKeyPair;
 import android.security.Credentials;
 import android.security.KeyChain;
@@ -1644,11 +1646,15 @@
     public static final int LOCK_TASK_FEATURE_HOME = 1 << 2;
 
     /**
-     * Enable the Recents button and the Recents screen during LockTask mode.
+     * Enable the Overview button and the Overview screen during LockTask mode. This feature flag
+     * can only be used in combination with {@link #LOCK_TASK_FEATURE_HOME}, and
+     * {@link #setLockTaskFeatures(ComponentName, int)} will throw an
+     * {@link IllegalArgumentException} if this feature flag is defined without
+     * {@link #LOCK_TASK_FEATURE_HOME}.
      *
      * @see #setLockTaskFeatures(ComponentName, int)
      */
-    public static final int LOCK_TASK_FEATURE_RECENTS = 1 << 3;
+    public static final int LOCK_TASK_FEATURE_OVERVIEW = 1 << 3;
 
     /**
      * Enable the global actions dialog during LockTask mode. This is the dialog that shows up when
@@ -1680,7 +1686,7 @@
             LOCK_TASK_FEATURE_SYSTEM_INFO,
             LOCK_TASK_FEATURE_NOTIFICATIONS,
             LOCK_TASK_FEATURE_HOME,
-            LOCK_TASK_FEATURE_RECENTS,
+            LOCK_TASK_FEATURE_OVERVIEW,
             LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
             LOCK_TASK_FEATURE_KEYGUARD
     })
@@ -2852,8 +2858,8 @@
      * When called by a profile owner of a managed profile returns true if the profile uses unified
      * challenge with its parent user.
      *
-     * <strong>Note: This method is not concerned with password quality and will return false if
-     * the profile has empty password as a separate challenge.
+     * <strong>Note</strong>: This method is not concerned with password quality and will return
+     * false if the profile has empty password as a separate challenge.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @throws SecurityException if {@code admin} is not a profile owner of a managed profile.
@@ -7191,7 +7197,7 @@
      *              {@link #LOCK_TASK_FEATURE_SYSTEM_INFO},
      *              {@link #LOCK_TASK_FEATURE_NOTIFICATIONS},
      *              {@link #LOCK_TASK_FEATURE_HOME},
-     *              {@link #LOCK_TASK_FEATURE_RECENTS},
+     *              {@link #LOCK_TASK_FEATURE_OVERVIEW},
      *              {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS},
      *              {@link #LOCK_TASK_FEATURE_KEYGUARD}
      * @throws SecurityException if {@code admin} is not the device owner, the profile owner of an
@@ -7281,6 +7287,15 @@
         }
     }
 
+    /** @hide */
+    @StringDef({
+            Settings.System.SCREEN_BRIGHTNESS_MODE,
+            Settings.System.SCREEN_BRIGHTNESS,
+            Settings.System.SCREEN_OFF_TIMEOUT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SystemSettingsWhitelist {}
+
     /**
      * Called by device owner to update {@link android.provider.Settings.System} settings.
      * Validation that the value of the setting is in the correct form for the setting type should
@@ -7300,8 +7315,8 @@
      * @param value The value to update the setting to.
      * @throws SecurityException if {@code admin} is not a device owner.
      */
-    public void setSystemSetting(@NonNull ComponentName admin, @NonNull String setting,
-            String value) {
+    public void setSystemSetting(@NonNull ComponentName admin,
+            @NonNull @SystemSettingsWhitelist String setting, String value) {
         throwIfParentInstance("setSystemSetting");
         if (mService != null) {
             try {
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index faaa004..38b4f8f 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -295,7 +295,7 @@
      * <li> [1] admin user ID ({@code Integer})
      * <li> [2] target user ID ({@code Integer})
      * <li> [3] new maximum number of failed password attempts ({@code Integer})
-     * @see DevicePolicyManager#setMaximumTimeToLock(ComponentName, long)
+     * @see DevicePolicyManager#setMaximumFailedPasswordsForWipe(ComponentName, int)
      */
     public static final int TAG_MAX_PASSWORD_ATTEMPTS_SET =
             SecurityLogTags.SECURITY_MAX_PASSWORD_ATTEMPTS_SET;
@@ -370,7 +370,7 @@
             SecurityLogTags.SECURITY_CERT_AUTHORITY_INSTALLED;
 
     /**
-     * Indicates that a new oot certificate has been removed from system's trusted credential
+     * Indicates that a new root certificate has been removed from system's trusted credential
      * storage. The log entry contains the following information about the event, encapsulated in an
      * {@link Object} array and accessible via {@link SecurityEvent#getData()}:
      * <li> [0] result ({@code Integer}, 0 if operation failed, 1 if succeeded)
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index 6ec0969..debc32b 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -715,6 +716,92 @@
         }
     }
 
+    /**
+     * Returns an {@link Intent} for the specified transport's configuration UI.
+     * This value is set by {@link #updateTransportAttributes(ComponentName, String, Intent, String,
+     * Intent, String)}.
+     * @param transportName The name of the registered transport.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.BACKUP)
+    public Intent getConfigurationIntent(String transportName) {
+        if (sService != null) {
+            try {
+                return sService.getConfigurationIntent(transportName);
+            } catch (RemoteException e) {
+                Log.e(TAG, "getConfigurationIntent() couldn't connect");
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns a {@link String} describing where the specified transport is sending data.
+     * This value is set by {@link #updateTransportAttributes(ComponentName, String, Intent, String,
+     * Intent, String)}.
+     * @param transportName The name of the registered transport.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.BACKUP)
+    public String getDestinationString(String transportName) {
+        if (sService != null) {
+            try {
+                return sService.getDestinationString(transportName);
+            } catch (RemoteException e) {
+                Log.e(TAG, "getDestinationString() couldn't connect");
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns an {@link Intent} for the specified transport's data management UI.
+     * This value is set by {@link #updateTransportAttributes(ComponentName, String, Intent, String,
+     * Intent, String)}.
+     * @param transportName The name of the registered transport.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.BACKUP)
+    public Intent getDataManagementIntent(String transportName) {
+        if (sService != null) {
+            try {
+                return sService.getDataManagementIntent(transportName);
+            } catch (RemoteException e) {
+                Log.e(TAG, "getDataManagementIntent() couldn't connect");
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns a {@link String} describing what the specified transport's data management intent is
+     * used for.
+     * This value is set by {@link #updateTransportAttributes(ComponentName, String, Intent, String,
+     * Intent, String)}.
+     *
+     * @param transportName The name of the registered transport.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.BACKUP)
+    public String getDataManagementLabel(String transportName) {
+        if (sService != null) {
+            try {
+                return sService.getDataManagementLabel(transportName);
+            } catch (RemoteException e) {
+                Log.e(TAG, "getDataManagementLabel() couldn't connect");
+            }
+        }
+        return null;
+    }
+
     /*
      * We wrap incoming binder calls with a private class implementation that
      * redirects them into main-thread actions.  This serializes the backup
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 1adc9f5..b2c9edd4 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -37,6 +37,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.server.SystemConfig;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -1384,6 +1385,8 @@
         classLoaderName = orig.classLoaderName;
         splitClassLoaderNames = orig.splitClassLoaderNames;
         appComponentFactory = orig.appComponentFactory;
+        compileSdkVersion = orig.compileSdkVersion;
+        compileSdkVersionCodename = orig.compileSdkVersionCodename;
     }
 
     public String toString() {
@@ -1601,7 +1604,10 @@
      * @hide
      */
     public boolean isAllowedToUseHiddenApi() {
-        return isSystemApp() || isUpdatedSystemApp();
+        boolean whitelisted =
+                SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
+        return isSystemApp() || // TODO get rid of this once the whitelist has been populated
+                (whitelisted && (isSystemApp() || isUpdatedSystemApp()));
     }
 
     /**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 379bff4..36a74a4 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -660,4 +660,6 @@
     boolean hasSigningCertificate(String packageName, in byte[] signingCertificate, int flags);
 
     boolean hasUidSigningCertificate(int uid, in byte[] signingCertificate, int flags);
+
+    String getSystemTextClassifierPackageName();
 }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index d0be6c8..25af1a7 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -448,11 +448,17 @@
 
     /**
      * Uninstall the given package, removing it completely from the device. This
-     * method is only available to the current "installer of record" for the
-     * package.
+     * method is available to:
+     * <ul>
+     * <li>the current "installer of record" for the package
+     * <li>the device owner
+     * <li>the affiliated profile owner
+     * </ul>
      *
      * @param packageName The package to uninstall.
      * @param statusReceiver Where to deliver the result.
+     *
+     * @see android.app.admin.DevicePolicyManager
      */
     @RequiresPermission(anyOf = {
             Manifest.permission.DELETE_PACKAGES,
@@ -480,14 +486,22 @@
 
     /**
      * Uninstall the given package with a specific version code, removing it
-     * completely from the device. This method is only available to the current
-     * "installer of record" for the package. If the version code of the package
+     * completely from the device. If the version code of the package
      * does not match the one passed in the versioned package argument this
      * method is a no-op. Use {@link PackageManager#VERSION_CODE_HIGHEST} to
      * uninstall the latest version of the package.
+     * <p>
+     * This method is available to:
+     * <ul>
+     * <li>the current "installer of record" for the package
+     * <li>the device owner
+     * <li>the affiliated profile owner
+     * </ul>
      *
      * @param versionedPackage The versioned package to uninstall.
      * @param statusReceiver Where to deliver the result.
+     *
+     * @see android.app.admin.DevicePolicyManager
      */
     @RequiresPermission(anyOf = {
             Manifest.permission.DELETE_PACKAGES,
@@ -941,9 +955,14 @@
          * Once this method is called, the session is sealed and no additional
          * mutations may be performed on the session. If the device reboots
          * before the session has been finalized, you may commit the session again.
+         * <p>
+         * If the installer is the device owner or the affiliated profile owner, there will be no
+         * user intervention.
          *
          * @throws SecurityException if streams opened through
          *             {@link #openWrite(String, long, long)} are still open.
+         *
+         * @see android.app.admin.DevicePolicyManager
          */
         public void commit(@NonNull IntentSender statusReceiver) {
             try {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 07a9911..bd7961f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -6022,4 +6022,14 @@
         throw new UnsupportedOperationException(
                 "hasSigningCertificate not implemented in subclass");
     }
+
+    /**
+     * @return the system defined text classifier package name, or null if there's none.
+     *
+     * @hide
+     */
+    public String getSystemTextClassifierPackageName() {
+        throw new UnsupportedOperationException(
+                "getSystemTextClassifierPackageName not implemented in subclass");
+    }
 }
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 6f093ba..41aa9c3 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -43,12 +43,14 @@
     public static final int PACKAGE_INSTALLER = 2;
     public static final int PACKAGE_VERIFIER = 3;
     public static final int PACKAGE_BROWSER = 4;
+    public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 5;
     @IntDef(value = {
         PACKAGE_SYSTEM,
         PACKAGE_SETUP_WIZARD,
         PACKAGE_INSTALLER,
         PACKAGE_VERIFIER,
         PACKAGE_BROWSER,
+        PACKAGE_SYSTEM_TEXT_CLASSIFIER,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KnownPackage {}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 9e4166e..2420b63 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3185,7 +3185,7 @@
 
         perm.info.protectionLevel = PermissionInfo.fixProtectionLevel(perm.info.protectionLevel);
 
-        if ((perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_FLAGS) != 0) {
+        if (perm.info.getProtectionFlags() != 0) {
             if ( (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_INSTANT) == 0
                     && (perm.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) == 0
                     && (perm.info.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) !=
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 21bd7f0..938409a 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -16,12 +16,16 @@
 
 package android.content.pm;
 
+import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Information you can retrieve about a particular security permission
  * known to the system.  This corresponds to information collected from the
@@ -56,6 +60,16 @@
     @Deprecated
     public static final int PROTECTION_SIGNATURE_OR_SYSTEM = 3;
 
+    /** @hide */
+    @IntDef(flag = false, prefix = { "PROTECTION_" }, value = {
+            PROTECTION_NORMAL,
+            PROTECTION_DANGEROUS,
+            PROTECTION_SIGNATURE,
+            PROTECTION_SIGNATURE_OR_SYSTEM,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Protection {}
+
     /**
      * Additional flag for {@link #protectionLevel}, corresponding
      * to the <code>privileged</code> value of
@@ -155,30 +169,71 @@
     public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 0x8000;
 
     /**
-     * Mask for {@link #protectionLevel}: the basic protection type.
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>text_classifier</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
      */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 0x10000;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
+            PROTECTION_FLAG_PRIVILEGED,
+            PROTECTION_FLAG_SYSTEM,
+            PROTECTION_FLAG_DEVELOPMENT,
+            PROTECTION_FLAG_APPOP,
+            PROTECTION_FLAG_PRE23,
+            PROTECTION_FLAG_INSTALLER,
+            PROTECTION_FLAG_VERIFIER,
+            PROTECTION_FLAG_PREINSTALLED,
+            PROTECTION_FLAG_SETUP,
+            PROTECTION_FLAG_INSTANT,
+            PROTECTION_FLAG_RUNTIME_ONLY,
+            PROTECTION_FLAG_OEM,
+            PROTECTION_FLAG_VENDOR_PRIVILEGED,
+            PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProtectionFlags {}
+
+    /**
+     * Mask for {@link #protectionLevel}: the basic protection type.
+     *
+     * @deprecated Use #getProtection() instead.
+     */
+    @Deprecated
     public static final int PROTECTION_MASK_BASE = 0xf;
 
     /**
      * Mask for {@link #protectionLevel}: additional flag bits.
+     *
+     * @deprecated Use #getProtectionFlags() instead.
      */
+    @Deprecated
     public static final int PROTECTION_MASK_FLAGS = 0xfff0;
 
     /**
      * The level of access this permission is protecting, as per
      * {@link android.R.attr#protectionLevel}. Consists of
-     * a base permission type and zero or more flags:
+     * a base permission type and zero or more flags. Use the following functions
+     * to extract them.
      *
      * <pre>
-     * int basePermissionType = protectionLevel & {@link #PROTECTION_MASK_BASE};
-     * int permissionFlags = protectionLevel & {@link #PROTECTION_MASK_FLAGS};
+     * int basePermissionType = permissionInfo.getProtection();
+     * int permissionFlags = permissionInfo.getProtectionFlags();
      * </pre>
      *
      * <p></p>Base permission types are {@link #PROTECTION_NORMAL},
      * {@link #PROTECTION_DANGEROUS}, {@link #PROTECTION_SIGNATURE}
      * and the deprecated {@link #PROTECTION_SIGNATURE_OR_SYSTEM}.
      * Flags are listed under {@link android.R.attr#protectionLevel}.
+     *
+     * @deprecated Use #getProtection() and #getProtectionFlags() instead.
      */
+    @Deprecated
     public int protectionLevel;
 
     /**
@@ -304,6 +359,9 @@
         if ((level & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0) {
             protLevel += "|vendorPrivileged";
         }
+        if ((level & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0) {
+            protLevel += "|textClassifier";
+        }
         return protLevel;
     }
 
@@ -344,6 +402,22 @@
         return null;
     }
 
+    /**
+     * Return the base permission type.
+     */
+    @Protection
+    public int getProtection() {
+        return protectionLevel & PROTECTION_MASK_BASE;
+    }
+
+    /**
+     * Return the additional flags in {@link #protectionLevel}.
+     */
+    @ProtectionFlags
+    public int getProtectionFlags() {
+        return protectionLevel & ~PROTECTION_MASK_BASE;
+    }
+
     @Override
     public String toString() {
         return "PermissionInfo{"
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 3ed533a..a1a14b9 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -24,6 +24,7 @@
 import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.utils.HashCodeHelpers;
 import android.hardware.camera2.utils.TypeReference;
+import android.hardware.camera2.utils.SurfaceUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArraySet;
@@ -643,6 +644,30 @@
                         break;
                     }
                 }
+
+                if (!streamFound) {
+                    // Check if we can match s by native object ID
+                    long reqSurfaceId = SurfaceUtils.getSurfaceId(s);
+                    for (int j = 0; j < configuredOutputs.size(); ++j) {
+                        int streamId = configuredOutputs.keyAt(j);
+                        OutputConfiguration outConfig = configuredOutputs.valueAt(j);
+                        int surfaceId = 0;
+                        for (Surface outSurface : outConfig.getSurfaces()) {
+                            if (reqSurfaceId == SurfaceUtils.getSurfaceId(outSurface)) {
+                                streamFound = true;
+                                mStreamIdxArray[i] = streamId;
+                                mSurfaceIdxArray[i] = surfaceId;
+                                i++;
+                                break;
+                            }
+                            surfaceId++;
+                        }
+                        if (streamFound) {
+                            break;
+                        }
+                    }
+                }
+
                 if (!streamFound) {
                     mStreamIdxArray = null;
                     mSurfaceIdxArray = null;
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index e7f2134..71a361b 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -730,7 +730,7 @@
         LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height));
     }
 
-    static long getSurfaceId(Surface surface) throws BufferQueueAbandonedException {
+    public static long getSurfaceId(Surface surface) throws BufferQueueAbandonedException {
         checkNotNull(surface);
         try {
             return nativeGetSurfaceId(surface);
diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
index e1e1c4f..9247844 100644
--- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java
+++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
@@ -56,6 +56,20 @@
     }
 
     /**
+     * Get the native object id of a surface.
+     *
+     * @param surface The surface to be checked.
+     * @return the native object id of the surface, 0 if surface is not backed by a native object.
+     */
+    public static long getSurfaceId(Surface surface) {
+        try {
+            return LegacyCameraDevice.getSurfaceId(surface);
+        } catch (BufferQueueAbandonedException e) {
+            return 0;
+        }
+    }
+
+    /**
      * Get the Surface size.
      *
      * @param surface The surface to be queried for size.
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 74a36df..572c585 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -454,8 +454,12 @@
     /**
      * Opens a file descriptor for reading and writing data to the USB accessory.
      *
+     * <p>If data is read from the {@link java.io.InputStream} created from this file descriptor all
+     * data of a USB transfer should be read at once. If only a partial request is read the rest of
+     * the transfer is dropped.
+     *
      * @param accessory the USB accessory to open
-     * @return file descriptor, or null if the accessor could not be opened.
+     * @return file descriptor, or null if the accessory could not be opened.
      */
     @RequiresFeature(PackageManager.FEATURE_USB_ACCESSORY)
     public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 6ecf6d5..dc46b73 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -240,8 +240,11 @@
      * New in version 30:
      *   - Uid.PROCESS_STATE_FOREGROUND_SERVICE only tracks
      *   ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE.
+     * New in version 31:
+     *   - New cellular network types.
+     *   - Deferred job metrics.
      */
-    static final int CHECKIN_VERSION = 30;
+    static final int CHECKIN_VERSION = 31;
 
     /**
      * Old version, we hit 9 and ran out of room, need to remove.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2ec9009e..68490ed 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10471,7 +10471,8 @@
 
         /**
          * TextClassifier specific settings.
-         * This is encoded as a key=value list, separated by commas. Ex:
+         * This is encoded as a key=value list, separated by commas. String[] types like
+         * entity_list_default use ":" as delimiter for values. Ex:
          *
          * <pre>
          * smart_selection_dark_launch              (boolean)
@@ -10479,6 +10480,10 @@
          * suggest_selection_max_range_length       (int)
          * classify_text_max_range_length           (int)
          * generate_links_max_text_length           (int)
+         * generate_links_log_sample_rate           (int)
+         * entity_list_default                      (String[])
+         * entity_list_not_editable                 (String[])
+         * entity_list_editable                     (String[])
          * </pre>
          *
          * <p>
diff --git a/core/java/android/security/keystore/RecoveryController.java b/core/java/android/security/keystore/RecoveryController.java
index 98e6a20..786d454 100644
--- a/core/java/android/security/keystore/RecoveryController.java
+++ b/core/java/android/security/keystore/RecoveryController.java
@@ -47,6 +47,7 @@
  * <p>A recovery agent requires the privileged permission
  * {@code android.Manifest.permission#RECOVER_KEYSTORE}.
  *
+ * @deprecated Use {@link android.security.keystore.recovery.RecoveryController}.
  * @hide
  */
 public class RecoveryController {
@@ -259,7 +260,9 @@
             @NonNull String packageName, @Nullable String[] aliases, int status)
             throws NameNotFoundException, InternalRecoveryServiceException {
         try {
-            mBinder.setRecoveryStatus(packageName, aliases, status);
+            for (String alias : aliases) {
+                mBinder.setRecoveryStatus(alias, status);
+            }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 7cd08f7..d57782c 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -65,8 +65,6 @@
     public static final int RECOVERY_STATUS_SYNCED = 0;
     /** Waiting for recovery agent to sync the key. */
     public static final int RECOVERY_STATUS_SYNC_IN_PROGRESS = 1;
-    /** Recovery account is not available. */
-    public static final int RECOVERY_STATUS_MISSING_ACCOUNT = 2;
     /** Key cannot be synced. */
     public static final int RECOVERY_STATUS_PERMANENT_FAILURE = 3;
 
@@ -290,24 +288,33 @@
     }
 
     /**
-     * Updates recovery status for given key. It is used to notify keystore that key was
-     * successfully stored on the server or there were an error. Application can check this value
-     * using {@code getRecoveyStatus}.
-     *
-     * @param packageName Application whose recoverable key's status are to be updated.
-     * @param alias Application-specific key alias.
-     * @param status Status specific to recovery agent.
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
+     * @deprecated Use {@link #setRecoveryStatus(String, int)}
+     * @removed
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public void setRecoveryStatus(
             @NonNull String packageName, String alias, int status)
             throws NameNotFoundException, InternalRecoveryServiceException {
+        setRecoveryStatus(alias, status);
+    }
+
+    /**
+     * Sets the recovery status for given key. It is used to notify the keystore that the key was
+     * successfully stored on the server or that there was an error. An application can check this
+     * value using {@link #getRecoveryStatus(String, String)}.
+     *
+     * @param alias The alias of the key whose status to set.
+     * @param status The status of the key. One of {@link #RECOVERY_STATUS_SYNCED},
+     *     {@link #RECOVERY_STATUS_SYNC_IN_PROGRESS} or {@link #RECOVERY_STATUS_PERMANENT_FAILURE}.
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public void setRecoveryStatus(String alias, int status)
+            throws InternalRecoveryServiceException {
         try {
-            // TODO: update aidl
-            String[] aliases = alias == null ? null : new String[]{alias};
-            mBinder.setRecoveryStatus(packageName, aliases, status);
+            mBinder.setRecoveryStatus(alias, status);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index 1ef6100..cf16749 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -15,12 +15,15 @@
  */
 package android.service.autofill;
 
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Parcel;
@@ -30,9 +33,6 @@
 import android.util.Log;
 import android.view.autofill.AutofillValue;
 
-import com.android.internal.os.HandlerCaller;
-import com.android.internal.os.SomeArgs;
-
 import java.util.Arrays;
 import java.util.List;
 
@@ -55,8 +55,6 @@
 
     private static final String TAG = "AutofillFieldClassificationService";
 
-    private static final int MSG_GET_SCORES = 1;
-
     /**
      * The {@link Intent} action that must be declared as handled by a service
      * in its manifest for the system to recognize it as a quota providing service.
@@ -83,35 +81,18 @@
 
     private AutofillFieldClassificationServiceWrapper mWrapper;
 
-    private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
-        final int action = msg.what;
+    private void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs,
+            List<AutofillValue> actualValues, String[] userDataValues) {
         final Bundle data = new Bundle();
-        final RemoteCallback callback;
-        switch (action) {
-            case MSG_GET_SCORES:
-                final SomeArgs args = (SomeArgs) msg.obj;
-                callback = (RemoteCallback) args.arg1;
-                final String algorithmName = (String) args.arg2;
-                final Bundle algorithmArgs = (Bundle) args.arg3;
-                @SuppressWarnings("unchecked")
-                final List<AutofillValue> actualValues = ((List<AutofillValue>) args.arg4);
-                @SuppressWarnings("unchecked")
-                final String[] userDataValues = (String[]) args.arg5;
-                final float[][] scores = onGetScores(algorithmName, algorithmArgs, actualValues,
-                        Arrays.asList(userDataValues));
-                if (scores != null) {
-                    data.putParcelable(EXTRA_SCORES, new Scores(scores));
-                }
-                break;
-            default:
-                Log.w(TAG, "Handling unknown message: " + action);
-                return;
+        final float[][] scores = onGetScores(algorithmName, algorithmArgs, actualValues,
+                Arrays.asList(userDataValues));
+        if (scores != null) {
+            data.putParcelable(EXTRA_SCORES, new Scores(scores));
         }
         callback.sendResult(data);
-    };
+    }
 
-    private final HandlerCaller mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(),
-            mHandlerCallback, true);
+    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
 
     /** @hide */
     public AutofillFieldClassificationService() {
@@ -160,9 +141,10 @@
         public void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs,
                 List<AutofillValue> actualValues, String[] userDataValues)
                         throws RemoteException {
-            // TODO(b/70939974): refactor to use PooledLambda
-            mHandlerCaller.obtainMessageOOOOO(MSG_GET_SCORES, callback, algorithmName,
-                    algorithmArgs, actualValues, userDataValues).sendToTarget();
+            mHandler.sendMessage(obtainMessage(
+                    AutofillFieldClassificationService::getScores,
+                    AutofillFieldClassificationService.this,
+                    callback, algorithmName, algorithmArgs, actualValues, userDataValues));
         }
     }
 
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 7a304c7..0c5d8bd 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -15,6 +15,8 @@
  */
 package android.service.autofill;
 
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -22,6 +24,7 @@
 import android.app.Service;
 import android.content.Intent;
 import android.os.CancellationSignal;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.ICancellationSignal;
 import android.os.Looper;
@@ -34,9 +37,6 @@
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 
-import com.android.internal.os.HandlerCaller;
-import com.android.internal.os.SomeArgs;
-
 /**
  * An {@code AutofillService} is a service used to automatically fill the contents of the screen
  * on behalf of a given user - for more information about autofill, read
@@ -554,20 +554,12 @@
      */
     public static final String SERVICE_META_DATA = "android.autofill";
 
-    // Handler messages.
-    private static final int MSG_CONNECT = 1;
-    private static final int MSG_DISCONNECT = 2;
-    private static final int MSG_ON_FILL_REQUEST = 3;
-    private static final int MSG_ON_SAVE_REQUEST = 4;
-
     private final IAutoFillService mInterface = new IAutoFillService.Stub() {
         @Override
         public void onConnectedStateChanged(boolean connected) {
-            if (connected) {
-                mHandlerCaller.obtainMessage(MSG_CONNECT).sendToTarget();
-            } else {
-                mHandlerCaller.obtainMessage(MSG_DISCONNECT).sendToTarget();
-            }
+            mHandler.sendMessage(obtainMessage(
+                    connected ? AutofillService::onConnected : AutofillService::onDisconnected,
+                    AutofillService.this));
         }
 
         @Override
@@ -578,56 +570,27 @@
             } catch (RemoteException e) {
                 e.rethrowFromSystemServer();
             }
-            mHandlerCaller.obtainMessageOOO(MSG_ON_FILL_REQUEST, request,
-                    CancellationSignal.fromTransport(transport), callback)
-                    .sendToTarget();
+            mHandler.sendMessage(obtainMessage(
+                    AutofillService::onFillRequest,
+                    AutofillService.this, request, CancellationSignal.fromTransport(transport),
+                    new FillCallback(callback, request.getId())));
         }
 
         @Override
         public void onSaveRequest(SaveRequest request, ISaveCallback callback) {
-            mHandlerCaller.obtainMessageOO(MSG_ON_SAVE_REQUEST, request,
-                    callback).sendToTarget();
+            mHandler.sendMessage(obtainMessage(
+                    AutofillService::onSaveRequest,
+                    AutofillService.this, request, new SaveCallback(callback)));
         }
     };
 
-    private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
-        switch (msg.what) {
-            case MSG_CONNECT: {
-                onConnected();
-                break;
-            } case MSG_ON_FILL_REQUEST: {
-                final SomeArgs args = (SomeArgs) msg.obj;
-                final FillRequest request = (FillRequest) args.arg1;
-                final CancellationSignal cancellation = (CancellationSignal) args.arg2;
-                final IFillCallback callback = (IFillCallback) args.arg3;
-                final FillCallback fillCallback = new FillCallback(callback, request.getId());
-                args.recycle();
-                onFillRequest(request, cancellation, fillCallback);
-                break;
-            } case MSG_ON_SAVE_REQUEST: {
-                final SomeArgs args = (SomeArgs) msg.obj;
-                final SaveRequest request = (SaveRequest) args.arg1;
-                final ISaveCallback callback = (ISaveCallback) args.arg2;
-                final SaveCallback saveCallback = new SaveCallback(callback);
-                args.recycle();
-                onSaveRequest(request, saveCallback);
-                break;
-            } case MSG_DISCONNECT: {
-                onDisconnected();
-                break;
-            } default: {
-                Log.w(TAG, "MyCallbacks received invalid message type: " + msg);
-            }
-        }
-    };
-
-    private HandlerCaller mHandlerCaller;
+    private Handler mHandler;
 
     @CallSuper
     @Override
     public void onCreate() {
         super.onCreate();
-        mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(), mHandlerCallback, true);
+        mHandler = new Handler(Looper.getMainLooper(), null, true);
     }
 
     @Override
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 6c8c8bc..2c8c4ec 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.os.CancellationSignal;
 import android.os.IBinder;
@@ -37,8 +38,6 @@
 import android.view.textclassifier.TextLinks;
 import android.view.textclassifier.TextSelection;
 
-import com.android.internal.R;
-
 /**
  * Abstract base class for the TextClassifier service.
  *
@@ -263,28 +262,33 @@
      */
     @Nullable
     public static ComponentName getServiceComponentName(Context context) {
-        final String str = context.getString(R.string.config_defaultTextClassifierService);
-        if (!TextUtils.isEmpty(str)) {
-            try {
-                final ComponentName componentName = ComponentName.unflattenFromString(str);
-                final Intent intent = new Intent(SERVICE_INTERFACE).setComponent(componentName);
-                final ServiceInfo si = context.getPackageManager()
-                        .getServiceInfo(intent.getComponent(), 0);
-                final String permission = si == null ? null : si.permission;
-                if (Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE.equals(permission)) {
-                    return componentName;
-                }
-                Slog.w(LOG_TAG, String.format(
-                        "Service %s should require %s permission. Found %s permission",
-                        intent.getComponent().flattenToString(),
-                        Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE,
-                        si.permission));
-            } catch (PackageManager.NameNotFoundException e) {
-                Slog.w(LOG_TAG, String.format("Service %s not found", str));
-            }
-        } else {
+        final String packageName = context.getPackageManager().getSystemTextClassifierPackageName();
+        if (TextUtils.isEmpty(packageName)) {
             Slog.d(LOG_TAG, "No configured system TextClassifierService");
+            return null;
         }
+
+        final Intent intent = new Intent(SERVICE_INTERFACE).setPackage(packageName);
+
+        final ResolveInfo ri = context.getPackageManager().resolveService(intent,
+                PackageManager.MATCH_SYSTEM_ONLY);
+
+        if ((ri == null) || (ri.serviceInfo == null)) {
+            Slog.w(LOG_TAG, String.format("Package or service not found in package %s",
+                    packageName));
+            return null;
+        }
+        final ServiceInfo si = ri.serviceInfo;
+
+        final String permission = si.permission;
+        if (Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE.equals(permission)) {
+            return si.getComponentName();
+        }
+        Slog.w(LOG_TAG, String.format(
+                "Service %s should require %s permission. Found %s permission",
+                si.getComponentName(),
+                Manifest.permission.BIND_TEXTCLASSIFIER_SERVICE,
+                si.permission));
         return null;
     }
 }
diff --git a/core/java/android/util/LongArray.java b/core/java/android/util/LongArray.java
index 9b0489c..fa98096 100644
--- a/core/java/android/util/LongArray.java
+++ b/core/java/android/util/LongArray.java
@@ -16,11 +16,15 @@
 
 package android.util;
 
+import android.annotation.Nullable;
+
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
-import java.util.Arrays;
+
 import libcore.util.EmptyArray;
 
+import java.util.Arrays;
+
 /**
  * Implements a growing array of long primitives.
  *
@@ -216,4 +220,18 @@
             throw new ArrayIndexOutOfBoundsException(mSize, index);
         }
     }
+
+    /**
+     * Test if each element of {@code a} equals corresponding element from {@code b}
+     */
+    public static boolean elementsEqual(@Nullable LongArray a, @Nullable LongArray b) {
+        if (a == null || b == null) return a == b;
+        if (a.mSize != b.mSize) return false;
+        for (int i = 0; i < a.mSize; i++) {
+            if (a.get(i) != b.get(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 7b89967..3b8fc5c 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -331,7 +331,7 @@
         buffer.putShort((short) 1);         // meta algorithm, SHA256_MODE == 1
         buffer.putShort((short) 1);         // data algorithm, SHA256_MODE == 1
 
-        buffer.putInt(0x1);                 // flags, 0x1: has extension
+        buffer.putInt(0x0);                 // flags
         buffer.putInt(0);                   // reserved
 
         buffer.putLong(fileSize);           // original file size
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 8f9ae0e..671532c 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -198,8 +198,8 @@
      *
      * @param layer The layer to composite on this canvas
      */
-    void drawHardwareLayer(HardwareLayer layer) {
-        nDrawLayer(mNativeCanvasWrapper, layer.getLayerHandle());
+    void drawTextureLayer(TextureLayer layer) {
+        nDrawTextureLayer(mNativeCanvasWrapper, layer.getLayerHandle());
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -257,7 +257,7 @@
     @CriticalNative
     private static native void nDrawRenderNode(long renderer, long renderNode);
     @CriticalNative
-    private static native void nDrawLayer(long renderer, long layer);
+    private static native void nDrawTextureLayer(long renderer, long layer);
     @CriticalNative
     private static native void nDrawCircle(long renderer, long propCx,
             long propCy, long propRadius, long propPaint);
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/TextureLayer.java
similarity index 88%
rename from core/java/android/view/HardwareLayer.java
rename to core/java/android/view/TextureLayer.java
index 7af1020..35a886f 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/TextureLayer.java
@@ -25,19 +25,17 @@
 import com.android.internal.util.VirtualRefBasePtr;
 
 /**
- * A hardware layer can be used to render graphics operations into a hardware
- * friendly buffer. For instance, with an OpenGL backend a hardware layer
- * would use a Frame Buffer Object (FBO.) The hardware layer can be used as
- * a drawing cache when a complex set of graphics operations needs to be
- * drawn several times.
+ * TextureLayer represents a SurfaceTexture that will be composited by RenderThread into the
+ * frame when drawn in a HW accelerated Canvas. This is backed by a DeferredLayerUpdater on
+ * the native side.
  *
  * @hide
  */
-final class HardwareLayer {
+final class TextureLayer {
     private ThreadedRenderer mRenderer;
     private VirtualRefBasePtr mFinalizer;
 
-    private HardwareLayer(ThreadedRenderer renderer, long deferredUpdater) {
+    private TextureLayer(ThreadedRenderer renderer, long deferredUpdater) {
         if (renderer == null || deferredUpdater == 0) {
             throw new IllegalArgumentException("Either hardware renderer: " + renderer
                     + " or deferredUpdater: " + deferredUpdater + " is invalid");
@@ -141,11 +139,12 @@
         mRenderer.pushLayerUpdate(this);
     }
 
-    static HardwareLayer adoptTextureLayer(ThreadedRenderer renderer, long layer) {
-        return new HardwareLayer(renderer, layer);
+    static TextureLayer adoptTextureLayer(ThreadedRenderer renderer, long layer) {
+        return new TextureLayer(renderer, layer);
     }
 
-    private static native boolean nPrepare(long layerUpdater, int width, int height, boolean isOpaque);
+    private static native boolean nPrepare(long layerUpdater, int width, int height,
+            boolean isOpaque);
     private static native void nSetLayerPaint(long layerUpdater, long paint);
     private static native void nSetTransform(long layerUpdater, long matrix);
     private static native void nSetSurfaceTexture(long layerUpdater, SurfaceTexture surface);
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 25dce99..371794045 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -106,7 +106,7 @@
 public class TextureView extends View {
     private static final String LOG_TAG = "TextureView";
 
-    private HardwareLayer mLayer;
+    private TextureLayer mLayer;
     private SurfaceTexture mSurface;
     private SurfaceTextureListener mListener;
     private boolean mHadSurface;
@@ -336,13 +336,13 @@
         if (canvas.isHardwareAccelerated()) {
             DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
 
-            HardwareLayer layer = getHardwareLayer();
+            TextureLayer layer = getTextureLayer();
             if (layer != null) {
                 applyUpdate();
                 applyTransformMatrix();
 
                 mLayer.setLayerPaint(mLayerPaint); // ensure layer paint is up to date
-                displayListCanvas.drawHardwareLayer(layer);
+                displayListCanvas.drawTextureLayer(layer);
             }
         }
     }
@@ -369,7 +369,7 @@
         }
     }
 
-    HardwareLayer getHardwareLayer() {
+    TextureLayer getTextureLayer() {
         if (mLayer == null) {
             if (mAttachInfo == null || mAttachInfo.mThreadedRenderer == null) {
                 return null;
@@ -602,7 +602,7 @@
             // the layer here thanks to the validate() call at the beginning of
             // this method
             if (mLayer == null && mUpdateSurface) {
-                getHardwareLayer();
+                getTextureLayer();
             }
 
             if (mLayer != null) {
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index e50d40e..6da51d1 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -34,6 +34,7 @@
 import android.util.Log;
 import android.view.Surface.OutOfResourcesException;
 import android.view.View.AttachInfo;
+import android.view.animation.AnimationUtils;
 
 import com.android.internal.R;
 import com.android.internal.util.VirtualRefBasePtr;
@@ -833,9 +834,9 @@
      *
      * @return A hardware layer
      */
-    HardwareLayer createTextureLayer() {
+    TextureLayer createTextureLayer() {
         long layer = nCreateTextureLayer(mNativeProxy);
-        return HardwareLayer.adoptTextureLayer(this, layer);
+        return TextureLayer.adoptTextureLayer(this, layer);
     }
 
 
@@ -844,7 +845,7 @@
     }
 
 
-    boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) {
+    boolean copyLayerInto(final TextureLayer layer, final Bitmap bitmap) {
         return nCopyLayerInto(mNativeProxy,
                 layer.getDeferredLayerUpdater(), bitmap);
     }
@@ -855,7 +856,7 @@
      *
      * @param layer The hardware layer that needs an update
      */
-    void pushLayerUpdate(HardwareLayer layer) {
+    void pushLayerUpdate(TextureLayer layer) {
         nPushLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
     }
 
@@ -863,7 +864,7 @@
      * Tells the HardwareRenderer that the layer is destroyed. The renderer
      * should remove the layer from any update queues.
      */
-    void onLayerDestroyed(HardwareLayer layer) {
+    void onLayerDestroyed(TextureLayer layer) {
         nCancelLayerUpdate(mNativeProxy, layer.getDeferredLayerUpdater());
     }
 
@@ -943,6 +944,109 @@
         }
     }
 
+    /**
+     * Basic synchronous renderer. Currently only used to render the Magnifier, so use with care.
+     * TODO: deduplicate against ThreadedRenderer.
+     *
+     * @hide
+     */
+    public static class SimpleRenderer {
+        private final RenderNode mRootNode;
+        private long mNativeProxy;
+        private final float mLightY, mLightZ;
+        private Surface mSurface;
+        private final FrameInfo mFrameInfo = new FrameInfo();
+
+        public SimpleRenderer(final Context context, final String name, final Surface surface) {
+            final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
+            mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
+            mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
+            final float lightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
+            final int ambientShadowAlpha =
+                    (int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5f);
+            final int spotShadowAlpha =
+                    (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5f);
+            a.recycle();
+
+            final long rootNodePtr = nCreateRootRenderNode();
+            mRootNode = RenderNode.adopt(rootNodePtr);
+            mRootNode.setClipToBounds(false);
+            mNativeProxy = nCreateProxy(true /* translucent */, rootNodePtr);
+            nSetName(mNativeProxy, name);
+
+            ProcessInitializer.sInstance.init(context, mNativeProxy);
+            nLoadSystemProperties(mNativeProxy);
+
+            nSetup(mNativeProxy, lightRadius, ambientShadowAlpha, spotShadowAlpha);
+
+            mSurface = surface;
+            nUpdateSurface(mNativeProxy, surface);
+        }
+
+        /**
+         * Set the light center.
+         */
+        public void setLightCenter(final Display display,
+                final int windowLeft, final int windowTop) {
+            // Adjust light position for window offsets.
+            final Point displaySize = new Point();
+            display.getRealSize(displaySize);
+            final float lightX = displaySize.x / 2f - windowLeft;
+            final float lightY = mLightY - windowTop;
+
+            nSetLightCenter(mNativeProxy, lightX, lightY, mLightZ);
+        }
+
+        public RenderNode getRootNode() {
+            return mRootNode;
+        }
+
+        /**
+         * Draw the surface.
+         */
+        public void draw(final FrameDrawingCallback callback) {
+            final long vsync = AnimationUtils.currentAnimationTimeMillis() * 1000000L;
+            mFrameInfo.setVsync(vsync, vsync);
+            mFrameInfo.addFlags(1 << 2 /* VSYNC */);
+            // TODO: remove this fence
+            nFence(mNativeProxy);
+            if (callback != null) {
+                callback.onFrameDraw(mSurface.getNextFrameNumber());
+            }
+            nSyncAndDrawFrame(mNativeProxy, mFrameInfo.mFrameInfo, mFrameInfo.mFrameInfo.length);
+        }
+
+        /**
+         * Destroy the renderer.
+         */
+        public void destroy() {
+            mSurface = null;
+            nDestroy(mNativeProxy, mRootNode.mNativeRenderNode);
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                nDeleteProxy(mNativeProxy);
+                mNativeProxy = 0;
+            } finally {
+                super.finalize();
+            }
+        }
+    }
+
+    /**
+     * Interface used to receive callbacks when a frame is being drawn.
+     */
+    public interface FrameDrawingCallback {
+        /**
+         * Invoked during a frame drawing.
+         *
+         * @param frame The id of the frame being drawn.
+         */
+        void onFrameDraw(long frame);
+    }
+
     private static class ProcessInitializer {
         static ProcessInitializer sInstance = new ProcessInitializer();
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3ff3c97..1e4f568 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -18055,19 +18055,20 @@
      * currently attached to.
      */
     public WindowId getWindowId() {
-        if (mAttachInfo == null) {
+        AttachInfo ai = mAttachInfo;
+        if (ai == null) {
             return null;
         }
-        if (mAttachInfo.mWindowId == null) {
+        if (ai.mWindowId == null) {
             try {
-                mAttachInfo.mIWindowId = mAttachInfo.mSession.getWindowId(
-                        mAttachInfo.mWindowToken);
-                mAttachInfo.mWindowId = new WindowId(
-                        mAttachInfo.mIWindowId);
+                ai.mIWindowId = ai.mSession.getWindowId(ai.mWindowToken);
+                if (ai.mIWindowId != null) {
+                    ai.mWindowId = new WindowId(ai.mIWindowId);
+                }
             } catch (RemoteException e) {
             }
         }
-        return mAttachInfo.mWindowId;
+        return ai.mWindowId;
     }
 
     /**
diff --git a/core/java/android/view/WindowId.java b/core/java/android/view/WindowId.java
index c4cda2c..12e58f1 100644
--- a/core/java/android/view/WindowId.java
+++ b/core/java/android/view/WindowId.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -35,6 +37,7 @@
  * that doesn't allow the other process to negatively harm your window.
  */
 public class WindowId implements Parcelable {
+    @NonNull
     private final IWindowId mToken;
 
     /**
@@ -74,8 +77,7 @@
             }
         };
 
-        final HashMap<IBinder, WindowId> mRegistrations
-                = new HashMap<IBinder, WindowId>();
+        final HashMap<IBinder, WindowId> mRegistrations = new HashMap<>();
 
         class H extends Handler {
             @Override
@@ -163,10 +165,9 @@
      * same package.
      */
     @Override
-    public boolean equals(Object otherObj) {
+    public boolean equals(@Nullable Object otherObj) {
         if (otherObj instanceof WindowId) {
-            return mToken.asBinder().equals(((WindowId) otherObj)
-                    .mToken.asBinder());
+            return mToken.asBinder().equals(((WindowId) otherObj).mToken.asBinder());
         }
         return false;
     }
@@ -182,7 +183,7 @@
         sb.append("IntentSender{");
         sb.append(Integer.toHexString(System.identityHashCode(this)));
         sb.append(": ");
-        sb.append(mToken != null ? mToken.asBinder() : null);
+        sb.append(mToken.asBinder());
         sb.append('}');
         return sb.toString();
     }
@@ -195,30 +196,32 @@
         out.writeStrongBinder(mToken.asBinder());
     }
 
-    public static final Parcelable.Creator<WindowId> CREATOR
-            = new Parcelable.Creator<WindowId>() {
+    public static final Parcelable.Creator<WindowId> CREATOR = new Parcelable.Creator<WindowId>() {
+        @Override
         public WindowId createFromParcel(Parcel in) {
             IBinder target = in.readStrongBinder();
             return target != null ? new WindowId(target) : null;
         }
 
+        @Override
         public WindowId[] newArray(int size) {
             return new WindowId[size];
         }
     };
 
     /** @hide */
+    @NonNull
     public IWindowId getTarget() {
         return mToken;
     }
 
     /** @hide */
-    public WindowId(IWindowId target) {
+    public WindowId(@NonNull IWindowId target) {
         mToken = target;
     }
 
     /** @hide */
-    public WindowId(IBinder target) {
+    public WindowId(@NonNull IBinder target) {
         mToken = IWindowId.Stub.asInterface(target);
     }
 }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 417a725..5b1dd5c 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3203,7 +3203,7 @@
         fieldIndex++;
         if (mConnectionId != DEFAULT.mConnectionId) nonDefaultFields |= bitAt(fieldIndex);
         fieldIndex++;
-        if (!Objects.equals(mChildNodeIds, DEFAULT.mChildNodeIds)) {
+        if (!LongArray.elementsEqual(mChildNodeIds, DEFAULT.mChildNodeIds)) {
             nonDefaultFields |= bitAt(fieldIndex);
         }
         fieldIndex++;
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 474db12..64686dd 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -206,6 +206,8 @@
      */
     private boolean mDetachWallpaper = false;
 
+    private boolean mShowWallpaper;
+
     private boolean mMore = true;
     private boolean mOneMoreTime = true;
 
@@ -253,7 +255,10 @@
 
         setBackgroundColor(a.getInt(com.android.internal.R.styleable.Animation_background, 0));
 
-        setDetachWallpaper(a.getBoolean(com.android.internal.R.styleable.Animation_detachWallpaper, false));
+        setDetachWallpaper(
+                a.getBoolean(com.android.internal.R.styleable.Animation_detachWallpaper, false));
+        setShowWallpaper(
+                a.getBoolean(com.android.internal.R.styleable.Animation_showWallpaper, false));
 
         final int resID = a.getResourceId(com.android.internal.R.styleable.Animation_interpolator, 0);
 
@@ -661,6 +666,18 @@
     }
 
     /**
+     * If this animation is run as a window animation, this will make the wallpaper visible behind
+     * the animation.
+     *
+     * @param showWallpaper Whether the wallpaper should be shown during the animation.
+     * @attr ref android.R.styleable#Animation_detachWallpaper
+     * @hide
+     */
+    public void setShowWallpaper(boolean showWallpaper) {
+        mShowWallpaper = showWallpaper;
+    }
+
+    /**
      * Gets the acceleration curve type for this animation.
      *
      * @return the {@link Interpolator} associated to this animation
@@ -775,6 +792,16 @@
     }
 
     /**
+     * @return If run as a window animation, returns whether the wallpaper will be shown behind
+     *         during the animation.
+     * @attr ref android.R.styleable#Animation_showWallpaper
+     * @hide
+     */
+    public boolean getShowWallpaper() {
+        return mShowWallpaper;
+    }
+
+    /**
      * <p>Indicates whether or not this animation will affect the transformation
      * matrix. For instance, a fade animation will not affect the matrix whereas
      * a scale animation will.</p>
diff --git a/core/java/android/view/textclassifier/SmartSelection.java b/core/java/android/view/textclassifier/SmartSelection.java
deleted file mode 100644
index 69c38ee..0000000
--- a/core/java/android/view/textclassifier/SmartSelection.java
+++ /dev/null
@@ -1,194 +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.annotation.Nullable;
-import android.content.res.AssetFileDescriptor;
-
-/**
- *  Java wrapper for SmartSelection native library interface.
- *  This library is used for detecting entities in text.
- */
-final class SmartSelection {
-
-    static {
-        System.loadLibrary("textclassifier");
-    }
-
-    /** Hints the classifier that this may be a url. */
-    static final int HINT_FLAG_URL = 0x01;
-    /** Hints the classifier that this may be an email. */
-    static final int HINT_FLAG_EMAIL = 0x02;
-
-    private final long mCtx;
-
-    /**
-     * Creates a new instance of SmartSelect predictor, using the provided model image,
-     * given as a file descriptor.
-     */
-    SmartSelection(int fd) {
-        mCtx = nativeNew(fd);
-    }
-
-    /**
-     * Creates a new instance of SmartSelect predictor, using the provided model image, given as a
-     * file path.
-     */
-    SmartSelection(String path) {
-        mCtx = nativeNewFromPath(path);
-    }
-
-    /**
-     * Creates a new instance of SmartSelect predictor, using the provided model image, given as an
-     * AssetFileDescriptor.
-     */
-    SmartSelection(AssetFileDescriptor afd) {
-        mCtx = nativeNewFromAssetFileDescriptor(afd, afd.getStartOffset(), afd.getLength());
-        if (mCtx == 0L) {
-            throw new IllegalArgumentException(
-                "Couldn't initialize TC from given AssetFileDescriptor");
-        }
-    }
-
-    /**
-     * Given a string context and current selection, computes the SmartSelection suggestion.
-     *
-     * 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.
-     *
-     * 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[] suggest(String context, int selectionBegin, int selectionEnd) {
-        return nativeSuggest(mCtx, context, selectionBegin, selectionEnd);
-    }
-
-    /**
-     * Given a string context and current selection, classifies the type of the selected text.
-     *
-     * The begin and end params are character indices in the context string.
-     *
-     * Returns an array of ClassificationResult objects with the probability
-     * scores for different collections.
-     */
-    public ClassificationResult[] classifyText(
-            String context, int selectionBegin, int selectionEnd, int hintFlags) {
-        return nativeClassifyText(mCtx, context, selectionBegin, selectionEnd, hintFlags);
-    }
-
-    /**
-     * Annotates given input text. Every word of the input is a part of some annotation.
-     * The annotations are sorted by their position in the context string.
-     * The annotations do not overlap.
-     */
-    public AnnotatedSpan[] annotate(String text) {
-        return nativeAnnotate(mCtx, text);
-    }
-
-    /**
-     * Frees up the allocated memory.
-     */
-    public void close() {
-        nativeClose(mCtx);
-    }
-
-    /**
-     * Returns a comma separated list of locales supported by the model as BCP 47 tags.
-     */
-    public static String getLanguages(int fd) {
-        return nativeGetLanguage(fd);
-    }
-
-    /**
-     * Returns the version of the model.
-     */
-    public static int getVersion(int fd) {
-        return nativeGetVersion(fd);
-    }
-
-    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[] nativeSuggest(
-            long context, String text, int selectionBegin, int selectionEnd);
-
-    private static native ClassificationResult[] nativeClassifyText(
-            long context, String text, int selectionBegin, int selectionEnd, int hintFlags);
-
-    private static native AnnotatedSpan[] nativeAnnotate(long context, String text);
-
-    private static native void nativeClose(long context);
-
-    private static native String nativeGetLanguage(int fd);
-
-    private static native int nativeGetVersion(int fd);
-
-    /** Classification result for classifyText method. */
-    static final class ClassificationResult {
-        final String mCollection;
-        /** float range: 0 - 1 */
-        final float mScore;
-        @Nullable final DatetimeParseResult mDatetime;
-
-        ClassificationResult(String collection, float score) {
-            mCollection = collection;
-            mScore = score;
-            mDatetime = null;
-        }
-
-        ClassificationResult(String collection, float score, DatetimeParseResult datetime) {
-            mCollection = collection;
-            mScore = score;
-            mDatetime = datetime;
-        }
-    }
-
-    /** Parsed date information for the classification result. */
-    static final class DatetimeParseResult {
-        long mMsSinceEpoch;
-    }
-
-    /** Represents a result of Annotate call. */
-    public static final class AnnotatedSpan {
-        final int mStartIndex;
-        final int mEndIndex;
-        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;
-        }
-    }
-}
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index cbc3828..1789edf 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -41,11 +41,13 @@
 
     private final ITextClassifierService mManagerService;
     private final TextClassifier mFallback;
+    private final String mPackageName;
 
     SystemTextClassifier(Context context) throws ServiceManager.ServiceNotFoundException {
         mManagerService = ITextClassifierService.Stub.asInterface(
                 ServiceManager.getServiceOrThrow(Context.TEXT_CLASSIFICATION_SERVICE));
         mFallback = new TextClassifierImpl(context);
+        mPackageName = context.getPackageName();
     }
 
     /**
@@ -107,6 +109,11 @@
             @NonNull CharSequence text, @Nullable TextLinks.Options options) {
         Utils.validate(text, false /* allowInMainThread */);
         try {
+            if (options == null) {
+                options = new TextLinks.Options().setCallingPackageName(mPackageName);
+            } else if (!mPackageName.equals(options.getCallingPackageName())) {
+                options.setCallingPackageName(mPackageName);
+            }
             final TextLinksCallback callback = new TextLinksCallback();
             mManagerService.onGenerateLinks(text, options, callback);
             final TextLinks links = callback.mReceiver.get();
diff --git a/core/java/android/view/textclassifier/TextClassifierConstants.java b/core/java/android/view/textclassifier/TextClassifierConstants.java
index efa6948..397473b 100644
--- a/core/java/android/view/textclassifier/TextClassifierConstants.java
+++ b/core/java/android/view/textclassifier/TextClassifierConstants.java
@@ -20,6 +20,11 @@
 import android.util.KeyValueListParser;
 import android.util.Slog;
 
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.StringJoiner;
+
 /**
  * TextClassifier specific settings.
  * This is encoded as a key=value list, separated by commas. Ex:
@@ -27,6 +32,12 @@
  * <pre>
  * smart_selection_dark_launch              (boolean)
  * smart_selection_enabled_for_edit_text    (boolean)
+ * suggest_selection_max_range_length       (int)
+ * classify_text_max_range_length           (int)
+ * generate_links_max_text_length           (int)
+ * entity_list_default                      (String[])
+ * entity_list_not_editable                 (String[])
+ * entity_list_editable                     (String[])
  * </pre>
  *
  * <p>
@@ -34,7 +45,9 @@
  * see also android.provider.Settings.Global.TEXT_CLASSIFIER_CONSTANTS
  *
  * Example of setting the values for testing.
- * adb shell settings put global text_classifier_constants smart_selection_dark_launch=true,smart_selection_enabled_for_edit_text=true
+ * adb shell settings put global text_classifier_constants \
+ *      smart_selection_dark_launch=true,smart_selection_enabled_for_edit_text=true,\
+ *      entity_list_default=phone:address
  * @hide
  */
 public final class TextClassifierConstants {
@@ -53,6 +66,14 @@
             "classify_text_max_range_length";
     private static final String GENERATE_LINKS_MAX_TEXT_LENGTH =
             "generate_links_max_text_length";
+    private static final String GENERATE_LINKS_LOG_SAMPLE_RATE =
+            "generate_links_log_sample_rate";
+    private static final String ENTITY_LIST_DEFAULT =
+            "entity_list_default";
+    private static final String ENTITY_LIST_NOT_EDITABLE =
+            "entity_list_not_editable";
+    private static final String ENTITY_LIST_EDITABLE =
+            "entity_list_editable";
 
     private static final boolean SMART_SELECTION_DARK_LAUNCH_DEFAULT = false;
     private static final boolean SMART_SELECTION_ENABLED_FOR_EDIT_TEXT_DEFAULT = true;
@@ -60,6 +81,16 @@
     private static final int SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
     private static final int CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT = 10 * 1000;
     private static final int GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT = 100 * 1000;
+    private static final int GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT = 100;
+    private static final String ENTITY_LIST_DELIMITER = ":";
+    private static final String ENTITY_LIST_DEFAULT_VALUE = new StringJoiner(ENTITY_LIST_DELIMITER)
+            .add(TextClassifier.TYPE_ADDRESS)
+            .add(TextClassifier.TYPE_EMAIL)
+            .add(TextClassifier.TYPE_PHONE)
+            .add(TextClassifier.TYPE_URL)
+            .add(TextClassifier.TYPE_DATE)
+            .add(TextClassifier.TYPE_DATE_TIME)
+            .add(TextClassifier.TYPE_FLIGHT_NUMBER).toString();
 
     /** Default settings. */
     static final TextClassifierConstants DEFAULT = new TextClassifierConstants();
@@ -70,6 +101,10 @@
     private final int mSuggestSelectionMaxRangeLength;
     private final int mClassifyTextMaxRangeLength;
     private final int mGenerateLinksMaxTextLength;
+    private final int mGenerateLinksLogSampleRate;
+    private final List<String> mEntityListDefault;
+    private final List<String> mEntityListNotEditable;
+    private final List<String> mEntityListEditable;
 
     private TextClassifierConstants() {
         mDarkLaunch = SMART_SELECTION_DARK_LAUNCH_DEFAULT;
@@ -78,6 +113,10 @@
         mSuggestSelectionMaxRangeLength = SUGGEST_SELECTION_MAX_RANGE_LENGTH_DEFAULT;
         mClassifyTextMaxRangeLength = CLASSIFY_TEXT_MAX_RANGE_LENGTH_DEFAULT;
         mGenerateLinksMaxTextLength = GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT;
+        mGenerateLinksLogSampleRate = GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT;
+        mEntityListDefault = parseEntityList(ENTITY_LIST_DEFAULT_VALUE);
+        mEntityListNotEditable = mEntityListDefault;
+        mEntityListEditable = mEntityListDefault;
     }
 
     private TextClassifierConstants(@Nullable String settings) {
@@ -106,9 +145,22 @@
         mGenerateLinksMaxTextLength = parser.getInt(
                 GENERATE_LINKS_MAX_TEXT_LENGTH,
                 GENERATE_LINKS_MAX_TEXT_LENGTH_DEFAULT);
+        mGenerateLinksLogSampleRate = parser.getInt(
+                GENERATE_LINKS_LOG_SAMPLE_RATE,
+                GENERATE_LINKS_LOG_SAMPLE_RATE_DEFAULT);
+        mEntityListDefault = parseEntityList(parser.getString(
+                ENTITY_LIST_DEFAULT,
+                ENTITY_LIST_DEFAULT_VALUE));
+        mEntityListNotEditable = parseEntityList(parser.getString(
+                ENTITY_LIST_NOT_EDITABLE,
+                ENTITY_LIST_DEFAULT_VALUE));
+        mEntityListEditable = parseEntityList(parser.getString(
+                ENTITY_LIST_EDITABLE,
+                ENTITY_LIST_DEFAULT_VALUE));
     }
 
-    static TextClassifierConstants loadFromString(String settings) {
+    /** Load from a settings string. */
+    public static TextClassifierConstants loadFromString(String settings) {
         return new TextClassifierConstants(settings);
     }
 
@@ -135,4 +187,24 @@
     public int getGenerateLinksMaxTextLength() {
         return mGenerateLinksMaxTextLength;
     }
+
+    public int getGenerateLinksLogSampleRate() {
+        return mGenerateLinksLogSampleRate;
+    }
+
+    public List<String> getEntityListDefault() {
+        return mEntityListDefault;
+    }
+
+    public List<String> getEntityListNotEditable() {
+        return mEntityListNotEditable;
+    }
+
+    public List<String> getEntityListEditable() {
+        return mEntityListEditable;
+    }
+
+    private static List<String> parseEntityList(String listStr) {
+        return Collections.unmodifiableList(Arrays.asList(listStr.split(ENTITY_LIST_DELIMITER)));
+    }
 }
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 20467ac..5b7095b 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -35,13 +35,11 @@
 import android.provider.CalendarContract;
 import android.provider.ContactsContract;
 import android.provider.Settings;
-import android.text.util.Linkify;
-import android.util.Patterns;
 import android.view.textclassifier.logging.DefaultLogger;
+import android.view.textclassifier.logging.GenerateLinksLogger;
 import android.view.textclassifier.logging.Logger;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.Preconditions;
 
 import java.io.File;
@@ -81,20 +79,11 @@
     private static final String MODEL_FILE_REGEX = "textclassifier\\.(.*)\\.model";
     private static final String UPDATED_MODEL_FILE_PATH =
             "/data/misc/textclassifier/textclassifier.model";
-    private static final List<String> ENTITY_TYPES_ALL =
-            Collections.unmodifiableList(Arrays.asList(
-                    TextClassifier.TYPE_ADDRESS,
-                    TextClassifier.TYPE_EMAIL,
-                    TextClassifier.TYPE_PHONE,
-                    TextClassifier.TYPE_URL,
-                    TextClassifier.TYPE_DATE,
-                    TextClassifier.TYPE_DATE_TIME,
-                    TextClassifier.TYPE_FLIGHT_NUMBER));
 
     private final Context mContext;
     private final TextClassifier mFallback;
 
-    private final MetricsLogger mMetricsLogger = new MetricsLogger();
+    private final GenerateLinksLogger mGenerateLinksLogger;
 
     private final Object mLock = new Object();
     @GuardedBy("mLock") // Do not access outside this lock.
@@ -102,7 +91,7 @@
     @GuardedBy("mLock") // Do not access outside this lock.
     private ModelFile mModel;
     @GuardedBy("mLock") // Do not access outside this lock.
-    private SmartSelection mSmartSelection;
+    private TextClassifierImplNative mNative;
 
     private final Object mLoggerLock = new Object();
     @GuardedBy("mLoggerLock") // Do not access outside this lock.
@@ -115,6 +104,8 @@
     public TextClassifierImpl(Context context) {
         mContext = Preconditions.checkNotNull(context);
         mFallback = TextClassifier.NO_OP;
+        mGenerateLinksLogger = new GenerateLinksLogger(
+                getSettings().getGenerateLinksLogSampleRate());
     }
 
     /** @inheritDoc */
@@ -128,8 +119,10 @@
             if (text.length() > 0
                     && rangeLength <= getSettings().getSuggestSelectionMaxRangeLength()) {
                 final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
+                final String localesString = concatenateLocales(locales);
+                final Calendar refTime = Calendar.getInstance();
                 final boolean darkLaunchAllowed = options != null && options.isDarkLaunchAllowed();
-                final SmartSelection smartSelection = getSmartSelection(locales);
+                final TextClassifierImplNative nativeImpl = getNative(locales);
                 final String string = text.toString();
                 final int start;
                 final int end;
@@ -137,8 +130,9 @@
                     start = selectionStartIndex;
                     end = selectionEndIndex;
                 } else {
-                    final int[] startEnd = smartSelection.suggest(
-                            string, selectionStartIndex, selectionEndIndex);
+                    final int[] startEnd = nativeImpl.suggestSelection(
+                            string, selectionStartIndex, selectionEndIndex,
+                            new TextClassifierImplNative.SelectionOptions(localesString));
                     start = startEnd[0];
                     end = startEnd[1];
                 }
@@ -146,13 +140,16 @@
                         && start >= 0 && end <= string.length()
                         && start <= selectionStartIndex && end >= selectionEndIndex) {
                     final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end);
-                    final SmartSelection.ClassificationResult[] results =
-                            smartSelection.classifyText(
+                    final TextClassifierImplNative.ClassificationResult[] results =
+                            nativeImpl.classifyText(
                                     string, start, end,
-                                    getHintFlags(string, start, end));
+                                    new TextClassifierImplNative.ClassificationOptions(
+                                            refTime.getTimeInMillis(),
+                                            refTime.getTimeZone().getID(),
+                                            localesString));
                     final int size = results.length;
                     for (int i = 0; i < size; i++) {
-                        tsBuilder.setEntityType(results[i].mCollection, results[i].mScore);
+                        tsBuilder.setEntityType(results[i].getCollection(), results[i].getScore());
                     }
                     return tsBuilder
                             .setSignature(
@@ -185,10 +182,17 @@
             if (text.length() > 0 && rangeLength <= getSettings().getClassifyTextMaxRangeLength()) {
                 final String string = text.toString();
                 final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
-                final Calendar refTime = (options == null) ? null : options.getReferenceTime();
-                final SmartSelection.ClassificationResult[] results = getSmartSelection(locales)
-                        .classifyText(string, startIndex, endIndex,
-                                getHintFlags(string, startIndex, endIndex));
+                final String localesString = concatenateLocales(locales);
+                final Calendar refTime = (options != null && options.getReferenceTime() != null)
+                        ? options.getReferenceTime() : Calendar.getInstance();
+
+                final TextClassifierImplNative.ClassificationResult[] results =
+                        getNative(locales)
+                                .classifyText(string, startIndex, endIndex,
+                                        new TextClassifierImplNative.ClassificationOptions(
+                                                refTime.getTimeInMillis(),
+                                                refTime.getTimeZone().getID(),
+                                                localesString));
                 if (results.length > 0) {
                     return createClassificationResult(
                             results, string, startIndex, endIndex, refTime);
@@ -215,26 +219,45 @@
         }
 
         try {
+            final long startTimeMs = System.currentTimeMillis();
             final LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null;
+            final Calendar refTime = Calendar.getInstance();
             final Collection<String> entitiesToIdentify =
                     options != null && options.getEntityConfig() != null
                             ? options.getEntityConfig().resolveEntityListModifications(
                                     getEntitiesForHints(options.getEntityConfig().getHints()))
-                            : ENTITY_TYPES_ALL;
-            final SmartSelection smartSelection = getSmartSelection(defaultLocales);
-            final SmartSelection.AnnotatedSpan[] annotations = smartSelection.annotate(textString);
-            for (SmartSelection.AnnotatedSpan span : annotations) {
-                final SmartSelection.ClassificationResult[] results = span.getClassification();
-                if (results.length == 0 || !entitiesToIdentify.contains(results[0].mCollection)) {
+                            : getSettings().getEntityListDefault();
+            final TextClassifierImplNative nativeImpl =
+                    getNative(defaultLocales);
+            final TextClassifierImplNative.AnnotatedSpan[] annotations =
+                    nativeImpl.annotate(
+                        textString,
+                        new TextClassifierImplNative.AnnotationOptions(
+                                refTime.getTimeInMillis(),
+                                refTime.getTimeZone().getID(),
+                                concatenateLocales(defaultLocales)));
+            for (TextClassifierImplNative.AnnotatedSpan span : annotations) {
+                final TextClassifierImplNative.ClassificationResult[] results =
+                        span.getClassification();
+                if (results.length == 0
+                        || !entitiesToIdentify.contains(results[0].getCollection())) {
                     continue;
                 }
                 final Map<String, Float> entityScores = new HashMap<>();
                 for (int i = 0; i < results.length; i++) {
-                    entityScores.put(results[i].mCollection, results[i].mScore);
+                    entityScores.put(results[i].getCollection(), results[i].getScore());
                 }
                 builder.addLink(span.getStartIndex(), span.getEndIndex(), entityScores);
             }
-            return builder.build();
+            final TextLinks links = builder.build();
+            final long endTimeMs = System.currentTimeMillis();
+            final String callingPackageName =
+                    options == null || options.getCallingPackageName() == null
+                            ? mContext.getPackageName()  // local (in process) TC.
+                            : options.getCallingPackageName();
+            mGenerateLinksLogger.logGenerateLinks(
+                    text, links, callingPackageName, endTimeMs - startTimeMs);
+            return links;
         } catch (Throwable t) {
             // Avoid throwing from this method. Log the error.
             Log.e(LOG_TAG, "Error getting links info.", t);
@@ -249,7 +272,18 @@
     }
 
     private Collection<String> getEntitiesForHints(Collection<String> hints) {
-        return ENTITY_TYPES_ALL;
+        final boolean editable = hints.contains(HINT_TEXT_IS_EDITABLE);
+        final boolean notEditable = hints.contains(HINT_TEXT_IS_NOT_EDITABLE);
+
+        // Use the default if there is no hint, or conflicting ones.
+        final boolean useDefault = editable == notEditable;
+        if (useDefault) {
+            return getSettings().getEntityListDefault();
+        } else if (editable) {
+            return getSettings().getEntityListEditable();
+        } else {  // notEditable
+            return getSettings().getEntityListNotEditable();
+        }
     }
 
     @Override
@@ -274,23 +308,24 @@
         return mSettings;
     }
 
-    private SmartSelection getSmartSelection(LocaleList localeList) throws FileNotFoundException {
+    private TextClassifierImplNative getNative(LocaleList localeList)
+            throws FileNotFoundException {
         synchronized (mLock) {
             localeList = localeList == null ? LocaleList.getEmptyLocaleList() : localeList;
             final ModelFile bestModel = findBestModelLocked(localeList);
             if (bestModel == null) {
                 throw new FileNotFoundException("No model for " + localeList.toLanguageTags());
             }
-            if (mSmartSelection == null || !Objects.equals(mModel, bestModel)) {
+            if (mNative == null || !Objects.equals(mModel, bestModel)) {
                 Log.d(DEFAULT_LOG_TAG, "Loading " + bestModel);
-                destroySmartSelectionIfExistsLocked();
+                destroyNativeIfExistsLocked();
                 final ParcelFileDescriptor fd = ParcelFileDescriptor.open(
                         new File(bestModel.getPath()), ParcelFileDescriptor.MODE_READ_ONLY);
-                mSmartSelection = new SmartSelection(fd.getFd());
+                mNative = new TextClassifierImplNative(fd.getFd());
                 closeAndLogError(fd);
                 mModel = bestModel;
             }
-            return mSmartSelection;
+            return mNative;
         }
     }
 
@@ -302,13 +337,17 @@
     }
 
     @GuardedBy("mLock") // Do not call outside this lock.
-    private void destroySmartSelectionIfExistsLocked() {
-        if (mSmartSelection != null) {
-            mSmartSelection.close();
-            mSmartSelection = null;
+    private void destroyNativeIfExistsLocked() {
+        if (mNative != null) {
+            mNative.close();
+            mNative = null;
         }
     }
 
+    private static String concatenateLocales(@Nullable LocaleList locales) {
+        return (locales == null) ? "" : locales.toLanguageTags();
+    }
+
     /**
      * Finds the most appropriate model to use for the given target locale list.
      *
@@ -372,20 +411,21 @@
     }
 
     private TextClassification createClassificationResult(
-            SmartSelection.ClassificationResult[] classifications,
+            TextClassifierImplNative.ClassificationResult[] classifications,
             String text, int start, int end, @Nullable Calendar referenceTime) {
         final String classifiedText = text.substring(start, end);
         final TextClassification.Builder builder = new TextClassification.Builder()
                 .setText(classifiedText);
 
         final int size = classifications.length;
-        SmartSelection.ClassificationResult highestScoringResult = null;
+        TextClassifierImplNative.ClassificationResult highestScoringResult = null;
         float highestScore = Float.MIN_VALUE;
         for (int i = 0; i < size; i++) {
-            builder.setEntityType(classifications[i].mCollection, classifications[i].mScore);
-            if (classifications[i].mScore > highestScore) {
+            builder.setEntityType(classifications[i].getCollection(),
+                                  classifications[i].getScore());
+            if (classifications[i].getScore() > highestScore) {
                 highestScoringResult = classifications[i];
-                highestScore = classifications[i].mScore;
+                highestScore = classifications[i].getScore();
             }
         }
 
@@ -433,19 +473,6 @@
         }
     }
 
-    private static int getHintFlags(CharSequence text, int start, int end) {
-        int flag = 0;
-        final CharSequence subText = text.subSequence(start, end);
-        if (Patterns.AUTOLINK_EMAIL_ADDRESS.matcher(subText).matches()) {
-            flag |= SmartSelection.HINT_FLAG_EMAIL;
-        }
-        if (Patterns.AUTOLINK_WEB_URL.matcher(subText).matches()
-                && Linkify.sUrlMatchFilter.acceptMatch(text, start, end)) {
-            flag |= SmartSelection.HINT_FLAG_URL;
-        }
-        return flag;
-    }
-
     /**
      * Closes the ParcelFileDescriptor and logs any errors that occur.
      */
@@ -473,8 +500,9 @@
             try {
                 final ParcelFileDescriptor modelFd = ParcelFileDescriptor.open(
                         file, ParcelFileDescriptor.MODE_READ_ONLY);
-                final int version = SmartSelection.getVersion(modelFd.getFd());
-                final String supportedLocalesStr = SmartSelection.getLanguages(modelFd.getFd());
+                final int version = TextClassifierImplNative.getVersion(modelFd.getFd());
+                final String supportedLocalesStr =
+                        TextClassifierImplNative.getLocales(modelFd.getFd());
                 if (supportedLocalesStr.isEmpty()) {
                     Log.d(DEFAULT_LOG_TAG, "Ignoring " + file.getAbsolutePath());
                     return null;
@@ -560,9 +588,9 @@
         public static List<Intent> create(
                 Context context,
                 @Nullable Calendar referenceTime,
-                SmartSelection.ClassificationResult classification,
+                TextClassifierImplNative.ClassificationResult classification,
                 String text) {
-            final String type = classification.mCollection.trim().toLowerCase(Locale.ENGLISH);
+            final String type = classification.getCollection().trim().toLowerCase(Locale.ENGLISH);
             text = text.trim();
             switch (type) {
                 case TextClassifier.TYPE_EMAIL:
@@ -575,9 +603,10 @@
                     return createForUrl(context, text);
                 case TextClassifier.TYPE_DATE:
                 case TextClassifier.TYPE_DATE_TIME:
-                    if (classification.mDatetime != null) {
+                    if (classification.getDatetimeResult() != null) {
                         Calendar eventTime = Calendar.getInstance();
-                        eventTime.setTimeInMillis(classification.mDatetime.mMsSinceEpoch);
+                        eventTime.setTimeInMillis(
+                                classification.getDatetimeResult().getTimeMsUtc());
                         return createForDatetime(type, referenceTime, eventTime);
                     } else {
                         return new ArrayList<>();
diff --git a/core/java/android/view/textclassifier/TextClassifierImplNative.java b/core/java/android/view/textclassifier/TextClassifierImplNative.java
new file mode 100644
index 0000000..3d4c8f2
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextClassifierImplNative.java
@@ -0,0 +1,301 @@
+/*
+ * 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/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index dc24d0c..884cbe8 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -305,6 +305,8 @@
         private @ApplyStrategy int mApplyStrategy;
         private Function<TextLink, TextLinkSpan> mSpanFactory;
 
+        private String mCallingPackageName;
+
         /**
          * Returns a new options object based on the specified link mask.
          */
@@ -377,6 +379,15 @@
         }
 
         /**
+         * Sets the name of the package that requested the links to get generated.
+         * @hide
+         */
+        public Options setCallingPackageName(@Nullable String callingPackageName) {
+            mCallingPackageName = callingPackageName;
+            return this;
+        }
+
+        /**
          * @return ordered list of locale preferences that can be used to disambiguate
          *      the provided text
          */
@@ -417,6 +428,16 @@
             return mSpanFactory;
         }
 
+        /**
+         * @return the name of the package that requested the links to get generated.
+         * TODO: make available as system API
+         * @hide
+         */
+        @Nullable
+        public String getCallingPackageName() {
+            return mCallingPackageName;
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -433,6 +454,7 @@
                 mEntityConfig.writeToParcel(dest, flags);
             }
             dest.writeInt(mApplyStrategy);
+            dest.writeString(mCallingPackageName);
         }
 
         public static final Parcelable.Creator<Options> CREATOR =
@@ -456,6 +478,7 @@
                 mEntityConfig = TextClassifier.EntityConfig.CREATOR.createFromParcel(in);
             }
             mApplyStrategy = in.readInt();
+            mCallingPackageName = in.readString();
         }
     }
 
diff --git a/core/java/android/view/textclassifier/logging/GenerateLinksLogger.java b/core/java/android/view/textclassifier/logging/GenerateLinksLogger.java
new file mode 100644
index 0000000..fb6f205
--- /dev/null
+++ b/core/java/android/view/textclassifier/logging/GenerateLinksLogger.java
@@ -0,0 +1,161 @@
+/*
+ * 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.logging;
+
+import android.annotation.Nullable;
+import android.metrics.LogMaker;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.Preconditions;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.Random;
+import java.util.UUID;
+
+/**
+ * A helper for logging calls to generateLinks.
+ * @hide
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public final class GenerateLinksLogger {
+
+    private static final String LOG_TAG = "GenerateLinksLogger";
+    private static final String ZERO = "0";
+
+    private final MetricsLogger mMetricsLogger;
+    private final Random mRng;
+    private final int mSampleRate;
+
+    /**
+     * @param sampleRate the rate at which log events are written. (e.g. 100 means there is a 0.01
+     *                   chance that a call to logGenerateLinks results in an event being written).
+     *                   To write all events, pass 1.
+     */
+    public GenerateLinksLogger(int sampleRate) {
+        mSampleRate = sampleRate;
+        mRng = new Random(System.nanoTime());
+        mMetricsLogger = new MetricsLogger();
+    }
+
+    @VisibleForTesting
+    public GenerateLinksLogger(int sampleRate, MetricsLogger metricsLogger) {
+        mSampleRate = sampleRate;
+        mRng = new Random(System.nanoTime());
+        mMetricsLogger = metricsLogger;
+    }
+
+    /** Logs statistics about a call to generateLinks. */
+    public void logGenerateLinks(CharSequence text, TextLinks links, String callingPackageName,
+            long latencyMs) {
+        Preconditions.checkNotNull(text);
+        Preconditions.checkNotNull(links);
+        Preconditions.checkNotNull(callingPackageName);
+        if (!shouldLog()) {
+            return;
+        }
+
+        // Always populate the total stats, and per-entity stats for each entity type detected.
+        final LinkifyStats totalStats = new LinkifyStats();
+        final Map<String, LinkifyStats> perEntityTypeStats = new ArrayMap<>();
+        for (TextLinks.TextLink link : links.getLinks()) {
+            if (link.getEntityCount() == 0) continue;
+            final String entityType = link.getEntity(0);
+            if (entityType == null
+                    || TextClassifier.TYPE_OTHER.equals(entityType)
+                    || TextClassifier.TYPE_UNKNOWN.equals(entityType)) {
+                continue;
+            }
+            totalStats.countLink(link);
+            perEntityTypeStats.computeIfAbsent(entityType, k -> new LinkifyStats()).countLink(link);
+        }
+
+        final String callId = UUID.randomUUID().toString();
+        writeStats(callId, callingPackageName, null, totalStats, text, latencyMs);
+        for (Map.Entry<String, LinkifyStats> entry : perEntityTypeStats.entrySet()) {
+            writeStats(callId, callingPackageName, entry.getKey(), entry.getValue(), text,
+                       latencyMs);
+        }
+    }
+
+    /**
+     * Returns whether this particular event should be logged.
+     *
+     * Sampling is used to reduce the amount of logging data generated.
+     **/
+    private boolean shouldLog() {
+        if (mSampleRate <= 1) {
+            return true;
+        } else {
+            return mRng.nextInt(mSampleRate) == 0;
+        }
+    }
+
+    /** Writes a log event for the given stats. */
+    private void writeStats(String callId, String callingPackageName, @Nullable String entityType,
+                            LinkifyStats stats, CharSequence text, long latencyMs) {
+        final LogMaker log = new LogMaker(MetricsEvent.TEXT_CLASSIFIER_GENERATE_LINKS)
+                .setPackageName(callingPackageName)
+                .addTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID, callId)
+                .addTaggedData(MetricsEvent.FIELD_LINKIFY_NUM_LINKS, stats.mNumLinks)
+                .addTaggedData(MetricsEvent.FIELD_LINKIFY_LINK_LENGTH, stats.mNumLinksTextLength)
+                .addTaggedData(MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH, text.length())
+                .addTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY, latencyMs);
+        if (entityType != null) {
+            log.addTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE, entityType);
+        }
+        mMetricsLogger.write(log);
+        debugLog(log);
+    }
+
+    private static void debugLog(LogMaker log) {
+        if (!Logger.DEBUG_LOG_ENABLED) return;
+
+        final String callId = Objects.toString(
+                log.getTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID), "");
+        final String entityType = Objects.toString(
+                log.getTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE), "ANY_ENTITY");
+        final int numLinks = Integer.parseInt(
+                Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_NUM_LINKS), ZERO));
+        final int linkLength = Integer.parseInt(
+                Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LINK_LENGTH), ZERO));
+        final int textLength = Integer.parseInt(
+                Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH), ZERO));
+        final int latencyMs = Integer.parseInt(
+                Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY), ZERO));
+
+        Log.d(LOG_TAG, String.format("%s:%s %d links (%d/%d chars) %dms %s", callId, entityType,
+                numLinks, linkLength, textLength, latencyMs, log.getPackageName()));
+    }
+
+    /** Helper class for storing per-entity type statistics. */
+    private static final class LinkifyStats {
+        int mNumLinks;
+        int mNumLinksTextLength;
+
+        void countLink(TextLinks.TextLink link) {
+            mNumLinks += 1;
+            mNumLinksTextLength += link.getEnd() - link.getStart();
+        }
+    }
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 65deb3b..a8f6b03 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1689,8 +1689,8 @@
     }
 
     /**
-     * Sets the list of domains that are exempt from SafeBrowsing checks. The list is
-     * global for all the WebViews.
+     * Sets the list of hosts (domain names/IP addresses) that are exempt from SafeBrowsing checks.
+     * The list is global for all the WebViews.
      * <p>
      * Each rule should take one of these:
      * <table>
@@ -1702,15 +1702,18 @@
      * </table>
      * <p>
      * All other rules, including wildcards, are invalid.
+     * <p>
+     * The correct syntax for hosts is defined by <a
+     * href="https://tools.ietf.org/html/rfc3986#section-3.2.2">RFC 3986</a>.
      *
-     * @param urls the list of URLs
-     * @param callback will be called with {@code true} if URLs are successfully added to the
-     * whitelist. It will be called with {@code false} if any URLs are malformed. The callback will
-     * be run on the UI thread
+     * @param hosts the list of hosts
+     * @param callback will be called with {@code true} if hosts are successfully added to the
+     * whitelist. It will be called with {@code false} if any hosts are malformed. The callback
+     * will be run on the UI thread
      */
-    public static void setSafeBrowsingWhitelist(@NonNull List<String> urls,
+    public static void setSafeBrowsingWhitelist(@NonNull List<String> hosts,
             @Nullable ValueCallback<Boolean> callback) {
-        getFactory().getStatics().setSafeBrowsingWhitelist(urls, callback);
+        getFactory().getStatics().setSafeBrowsingWhitelist(hosts, callback);
     }
 
     /**
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 4f7cdab..4ff49ea 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -89,7 +89,7 @@
         * {@link android.webkit.WebView#setSafeBrowsingWhitelist(List<String>,
         * ValueCallback<Boolean>)}
         */
-        void setSafeBrowsingWhitelist(List<String> urls, ValueCallback<Boolean> callback);
+        void setSafeBrowsingWhitelist(List<String> hosts, ValueCallback<Boolean> callback);
 
         /**
          * Implement the API method
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 8836561..6427965 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -18,21 +18,32 @@
 
 import android.annotation.FloatRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.annotation.UiThread;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Handler;
-import android.view.Gravity;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.view.Display;
+import android.view.DisplayListCanvas;
 import android.view.LayoutInflater;
 import android.view.PixelCopy;
+import android.view.RenderNode;
 import android.view.Surface;
+import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
+import android.view.SurfaceSession;
 import android.view.SurfaceView;
+import android.view.ThreadedRenderer;
 import android.view.View;
 import android.view.ViewParent;
 import android.view.ViewRootImpl;
@@ -46,32 +57,37 @@
 public final class Magnifier {
     // Use this to specify that a previous configuration value does not exist.
     private static final int NONEXISTENT_PREVIOUS_CONFIG_VALUE = -1;
+    // The callbacks of the pixel copy requests will be invoked on
+    // the Handler of this Thread when the copy is finished.
+    private static final HandlerThread sPixelCopyHandlerThread =
+            new HandlerThread("magnifier pixel copy result handler");
+
     // The view to which this magnifier is attached.
     private final View mView;
     // The coordinates of the view in the surface.
     private final int[] mViewCoordinatesInSurface;
     // The window containing the magnifier.
-    private final PopupWindow mWindow;
+    private InternalPopupWindow mWindow;
     // The center coordinates of the window containing the magnifier.
     private final Point mWindowCoords = new Point();
     // The width of the window containing the magnifier.
     private final int mWindowWidth;
     // The height of the window containing the magnifier.
     private final int mWindowHeight;
-    // The bitmap used to display the contents of the magnifier.
-    private final Bitmap mBitmap;
+    // The width of the bitmaps where the magnifier content is copied.
+    private final int mBitmapWidth;
+    // The height of the bitmaps where the magnifier content is copied.
+    private final int mBitmapHeight;
+    // The elevation of the window containing the magnifier.
+    private final float mWindowElevation;
     // The center coordinates of the content that is to be magnified.
     private final Point mCenterZoomCoords = new Point();
-    // The callback of the pixel copy request will be invoked on this Handler when
-    // the copy is finished.
-    private final Handler mPixelCopyHandler = Handler.getMain();
-    // Current magnification scale.
-    private final float mZoomScale;
     // Variables holding previous states, used for detecting redundant calls and invalidation.
     private final Point mPrevStartCoordsInSurface = new Point(
             NONEXISTENT_PREVIOUS_CONFIG_VALUE, NONEXISTENT_PREVIOUS_CONFIG_VALUE);
     private final PointF mPrevPosInView = new PointF(
             NONEXISTENT_PREVIOUS_CONFIG_VALUE, NONEXISTENT_PREVIOUS_CONFIG_VALUE);
+    // Rectangle defining the view surface area we pixel copy content from.
     private final Rect mPixelCopyRequestRect = new Rect();
 
     /**
@@ -82,8 +98,6 @@
     public Magnifier(@NonNull View view) {
         mView = Preconditions.checkNotNull(view);
         final Context context = mView.getContext();
-        final float elevation = context.getResources().getDimension(
-                com.android.internal.R.dimen.magnifier_elevation);
         final View content = LayoutInflater.from(context).inflate(
                 com.android.internal.R.layout.magnifier, null);
         content.findViewById(com.android.internal.R.id.magnifier_inner).setClipToOutline(true);
@@ -91,23 +105,18 @@
                 com.android.internal.R.dimen.magnifier_width);
         mWindowHeight = context.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.magnifier_height);
-        mZoomScale = context.getResources().getFloat(
+        mWindowElevation = context.getResources().getDimension(
+                com.android.internal.R.dimen.magnifier_elevation);
+        final float zoomScale = context.getResources().getFloat(
                 com.android.internal.R.dimen.magnifier_zoom_scale);
+        mBitmapWidth = Math.round(mWindowWidth / zoomScale);
+        mBitmapHeight = Math.round(mWindowHeight / zoomScale);
         // The view's surface coordinates will not be updated until the magnifier is first shown.
         mViewCoordinatesInSurface = new int[2];
+    }
 
-        mWindow = new PopupWindow(context);
-        mWindow.setContentView(content);
-        mWindow.setWidth(mWindowWidth);
-        mWindow.setHeight(mWindowHeight);
-        mWindow.setElevation(elevation);
-        mWindow.setTouchable(false);
-        mWindow.setBackgroundDrawable(null);
-
-        final int bitmapWidth = Math.round(mWindowWidth / mZoomScale);
-        final int bitmapHeight = Math.round(mWindowHeight / mZoomScale);
-        mBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
-        getImageView().setImageBitmap(mBitmap);
+    static {
+        sPixelCopyHandlerThread.start();
     }
 
     /**
@@ -155,30 +164,45 @@
         }
 
         final int startX = Math.max(zeroScrollXInSurface, Math.min(
-                mCenterZoomCoords.x - mBitmap.getWidth() / 2,
-                zeroScrollXInSurface + actualWidth - mBitmap.getWidth()));
-        final int startY = mCenterZoomCoords.y - mBitmap.getHeight() / 2;
+                mCenterZoomCoords.x - mBitmapWidth / 2,
+                zeroScrollXInSurface + actualWidth - mBitmapWidth));
+        final int startY = mCenterZoomCoords.y - mBitmapHeight / 2;
 
         if (xPosInView != mPrevPosInView.x || yPosInView != mPrevPosInView.y) {
-            performPixelCopy(startX, startY);
-
+            if (mWindow == null) {
+                mWindow = new InternalPopupWindow(mView.getContext(), mView.getDisplay(),
+                        getValidViewSurface(), mWindowWidth, mWindowHeight, mWindowElevation,
+                        Handler.getMain() /* draw the magnifier on the UI thread */, mCallback);
+            }
+            performPixelCopy(startX, startY, true /* update window position */);
             mPrevPosInView.x = xPosInView;
             mPrevPosInView.y = yPosInView;
-
-            if (mWindow.isShowing()) {
-                mWindow.update(mWindowCoords.x, mWindowCoords.y, mWindow.getWidth(),
-                        mWindow.getHeight());
-            } else {
-                mWindow.showAtLocation(mView, Gravity.NO_GRAVITY, mWindowCoords.x, mWindowCoords.y);
-            }
         }
     }
 
+    @Nullable
+    private Surface getValidViewSurface() {
+        // TODO: deduplicate this against the first part of #performPixelCopy
+        final Surface surface;
+        if (mView instanceof SurfaceView) {
+            surface = ((SurfaceView) mView).getHolder().getSurface();
+        } else if (mView.getViewRootImpl() != null) {
+            surface = mView.getViewRootImpl().mSurface;
+        } else {
+            surface = null;
+        }
+
+        return (surface != null && surface.isValid()) ? surface : null;
+    }
+
     /**
      * Dismisses the magnifier from the screen. Calling this on a dismissed magnifier is a no-op.
      */
     public void dismiss() {
-        mWindow.dismiss();
+        if (mWindow != null) {
+            mWindow.destroy();
+            mWindow = null;
+        }
     }
 
     /**
@@ -186,43 +210,40 @@
      * {@link #show(float, float)}. This only happens if the magnifier is currently showing.
      */
     public void update() {
-        if (mWindow.isShowing()) {
-            // Update the contents shown in the magnifier.
-            performPixelCopy(mPrevStartCoordsInSurface.x, mPrevStartCoordsInSurface.y);
+        if (mWindow != null) {
+            // Update the content shown in the magnifier.
+            performPixelCopy(mPrevStartCoordsInSurface.x, mPrevStartCoordsInSurface.y,
+                    false /* update window position */);
         }
     }
 
     private void configureCoordinates(final float xPosInView, final float yPosInView) {
         // Compute the coordinates of the center of the content going to be displayed in the
         // magnifier. These are relative to the surface the content is copied from.
-        final float contentPosX;
-        final float contentPosY;
+        final float posX;
+        final float posY;
         if (mView instanceof SurfaceView) {
             // No offset required if the backing Surface matches the size of the SurfaceView.
-            contentPosX = xPosInView;
-            contentPosY = yPosInView;
+            posX = xPosInView;
+            posY = yPosInView;
         } else {
             mView.getLocationInSurface(mViewCoordinatesInSurface);
-            contentPosX = xPosInView + mViewCoordinatesInSurface[0];
-            contentPosY = yPosInView + mViewCoordinatesInSurface[1];
+            posX = xPosInView + mViewCoordinatesInSurface[0];
+            posY = yPosInView + mViewCoordinatesInSurface[1];
         }
-        mCenterZoomCoords.x = Math.round(contentPosX);
-        mCenterZoomCoords.y = Math.round(contentPosY);
+        mCenterZoomCoords.x = Math.round(posX);
+        mCenterZoomCoords.y = Math.round(posY);
 
-        // Compute the position of the magnifier window. These have to be relative to the window
-        // of the view the magnifier is attached to, as the magnifier popup is a panel window
-        // attached to that window.
-        final int[] viewCoordinatesInWindow = new int[2];
-        mView.getLocationInWindow(viewCoordinatesInWindow);
+        // Compute the position of the magnifier window. Again, this has to be relative to the
+        // surface of the magnified view, as this surface is the parent of the magnifier surface.
         final int verticalOffset = mView.getContext().getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.magnifier_offset);
-        final float magnifierPosX = xPosInView + viewCoordinatesInWindow[0];
-        final float magnifierPosY = yPosInView + viewCoordinatesInWindow[1] - verticalOffset;
-        mWindowCoords.x = Math.round(magnifierPosX - mWindowWidth / 2);
-        mWindowCoords.y = Math.round(magnifierPosY - mWindowHeight / 2);
+        mWindowCoords.x = mCenterZoomCoords.x - mWindowWidth / 2;
+        mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2 - verticalOffset;
     }
 
-    private void performPixelCopy(final int startXInSurface, final int startYInSurface) {
+    private void performPixelCopy(final int startXInSurface, final int startYInSurface,
+            final boolean updateWindowPosition) {
         // Get the view surface where the content will be copied from.
         final Surface surface;
         final int surfaceWidth;
@@ -256,20 +277,293 @@
         // Perform the pixel copy.
         mPixelCopyRequestRect.set(clampedStartXInSurface,
                 clampedStartYInSurface,
-                clampedStartXInSurface + mBitmap.getWidth(),
-                clampedStartYInSurface + mBitmap.getHeight());
-        PixelCopy.request(surface, mPixelCopyRequestRect, mBitmap,
+                clampedStartXInSurface + mBitmapWidth,
+                clampedStartYInSurface + mBitmapHeight);
+        final int windowCoordsX = mWindowCoords.x;
+        final int windowCoordsY = mWindowCoords.y;
+
+        final Bitmap bitmap =
+                Bitmap.createBitmap(mBitmapWidth, mBitmapHeight, Bitmap.Config.ARGB_8888);
+        PixelCopy.request(surface, mPixelCopyRequestRect, bitmap,
                 result -> {
-                    getImageView().invalidate();
-                    mPrevStartCoordsInSurface.x = startXInSurface;
-                    mPrevStartCoordsInSurface.y = startYInSurface;
+                    synchronized (mWindow.mLock) {
+                        if (mWindow != null) {
+                            if (updateWindowPosition) {
+                                // TODO: pull the position update outside #performPixelCopy
+                                mWindow.setContentPositionForNextDraw(windowCoordsX, windowCoordsY);
+                            }
+                            mWindow.updateContent(bitmap);
+                        }
+                    }
                 },
-                mPixelCopyHandler);
+                sPixelCopyHandlerThread.getThreadHandler());
+        mPrevStartCoordsInSurface.x = startXInSurface;
+        mPrevStartCoordsInSurface.y = startYInSurface;
     }
 
-    private ImageView getImageView() {
-        return mWindow.getContentView().findViewById(
-                com.android.internal.R.id.magnifier_image);
+    /**
+     * Magnifier's own implementation of PopupWindow-similar floating window.
+     * This exists to ensure frame-synchronization between window position updates and window
+     * content updates. By using a PopupWindow, these events would happen in different frames,
+     * producing a shakiness effect for the magnifier content.
+     */
+    private static class InternalPopupWindow {
+        // Display associated to the view the magnifier is attached to.
+        private final Display mDisplay;
+        // The size of the content of the magnifier.
+        private final int mContentWidth;
+        private final int mContentHeight;
+        // The size of the allocated surface.
+        private final int mSurfaceWidth;
+        private final int mSurfaceHeight;
+        // The insets of the content inside the allocated surface.
+        private final int mOffsetX;
+        private final int mOffsetY;
+        // The surface we allocate for the magnifier content + shadow.
+        private final SurfaceSession mSurfaceSession;
+        private final SurfaceControl mSurfaceControl;
+        private final Surface mSurface;
+        // The renderer used for the allocated surface.
+        private final ThreadedRenderer.SimpleRenderer mRenderer;
+        // The RenderNode used to draw the magnifier content in the surface.
+        private final RenderNode mBitmapRenderNode;
+        // The job that will be post'd to apply the pending magnifier updates to the surface.
+        private final Runnable mMagnifierUpdater;
+        // The handler where the magnifier updater jobs will be post'd.
+        private final Handler mHandler;
+        // The callback to be run after the next draw. Only used for testing.
+        private Callback mCallback;
+
+        // Members below describe the state of the magnifier. Reads/writes to them
+        // have to be synchronized between the UI thread and the thread that handles
+        // the pixel copy results. This is the purpose of mLock.
+        private final Object mLock = new Object();
+        // Whether a magnifier frame draw is currently pending in the UI thread queue.
+        private boolean mFrameDrawScheduled;
+        // The content bitmap.
+        private Bitmap mBitmap;
+        // Whether the next draw will be the first one for the current instance.
+        private boolean mFirstDraw = true;
+        // The window position in the parent surface. Might be applied during the next draw,
+        // when mPendingWindowPositionUpdate is true.
+        private int mWindowPositionX;
+        private int mWindowPositionY;
+        private boolean mPendingWindowPositionUpdate;
+
+        InternalPopupWindow(final Context context, final Display display,
+                final Surface parentSurface,
+                final int width, final int height, final float elevation,
+                final Handler handler, final Callback callback) {
+            mDisplay = display;
+            mCallback = callback;
+
+            mContentWidth = width;
+            mContentHeight = height;
+            mOffsetX = (int) (0.1f * width);
+            mOffsetY = (int) (0.1f * height);
+            // Setup the surface we will use for drawing the content and shadow.
+            mSurfaceWidth = mContentWidth + 2 * mOffsetX;
+            mSurfaceHeight = mContentHeight + 2 * mOffsetY;
+            mSurfaceSession = new SurfaceSession(parentSurface);
+            mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
+                    .setFormat(PixelFormat.TRANSLUCENT)
+                    .setSize(mSurfaceWidth, mSurfaceHeight)
+                    .setName("magnifier surface")
+                    .setFlags(SurfaceControl.HIDDEN)
+                    .build();
+            mSurface = new Surface();
+            mSurface.copyFrom(mSurfaceControl);
+
+            // Setup the RenderNode tree. The root has only one child, which contains the bitmap.
+            mRenderer = new ThreadedRenderer.SimpleRenderer(
+                    context,
+                    "magnifier renderer",
+                    mSurface
+            );
+            mBitmapRenderNode = createRenderNodeForBitmap(
+                    "magnifier content",
+                    elevation
+            );
+
+            final DisplayListCanvas canvas = mRenderer.getRootNode().start(width, height);
+            try {
+                canvas.insertReorderBarrier();
+                canvas.drawRenderNode(mBitmapRenderNode);
+                canvas.insertInorderBarrier();
+            } finally {
+                mRenderer.getRootNode().end(canvas);
+            }
+
+            // Initialize the update job and the handler where this will be post'd.
+            mHandler = handler;
+            mMagnifierUpdater = this::doDraw;
+            mFrameDrawScheduled = false;
+        }
+
+        private RenderNode createRenderNodeForBitmap(final String name, final float elevation) {
+            final RenderNode bitmapRenderNode = RenderNode.create(name, null);
+
+            // Define the position of the bitmap in the parent render node. The surface regions
+            // outside the bitmap are used to draw elevation.
+            bitmapRenderNode.setLeftTopRightBottom(mOffsetX, mOffsetY,
+                    mOffsetX + mContentWidth, mOffsetY + mContentHeight);
+            bitmapRenderNode.setElevation(elevation);
+
+            final Outline outline = new Outline();
+            outline.setRoundRect(0, 0, mContentWidth, mContentHeight, 3);
+            outline.setAlpha(1.0f);
+            bitmapRenderNode.setOutline(outline);
+            bitmapRenderNode.setClipToOutline(true);
+
+            // Create a dummy draw, which will be replaced later with real drawing.
+            final DisplayListCanvas canvas = bitmapRenderNode.start(mContentWidth, mContentHeight);
+            try {
+                canvas.drawColor(0xFF00FF00);
+            } finally {
+                bitmapRenderNode.end(canvas);
+            }
+
+            return bitmapRenderNode;
+        }
+
+        /**
+         * Sets the position of the magnifier content relative to the parent surface.
+         * The position update will happen in the same frame with the next draw.
+         * The method has to be called in a context that holds {@link #mLock}.
+         *
+         * @param contentX the x coordinate of the content
+         * @param contentY the y coordinate of the content
+         */
+        public void setContentPositionForNextDraw(final int contentX, final int contentY) {
+            mWindowPositionX = contentX - mOffsetX;
+            mWindowPositionY = contentY - mOffsetY;
+            mPendingWindowPositionUpdate = true;
+            requestUpdate();
+        }
+
+        /**
+         * Sets the content that should be displayed in the magnifier.
+         * The update happens immediately, and possibly triggers a pending window movement set
+         * by {@link #setContentPositionForNextDraw(int, int)}.
+         * The method has to be called in a context that holds {@link #mLock}.
+         *
+         * @param bitmap the content bitmap
+         */
+        public void updateContent(final @NonNull Bitmap bitmap) {
+            if (mBitmap != null) {
+                mBitmap.recycle();
+            }
+            mBitmap = bitmap;
+            requestUpdate();
+        }
+
+        private void requestUpdate() {
+            if (mFrameDrawScheduled) {
+                return;
+            }
+            final Message request = Message.obtain(mHandler, mMagnifierUpdater);
+            request.setAsynchronous(true);
+            request.sendToTarget();
+            mFrameDrawScheduled = true;
+        }
+
+        /**
+         * Destroys this instance.
+         */
+        public void destroy() {
+            mRenderer.destroy();
+            mSurface.destroy();
+            mSurfaceControl.destroy();
+            mSurfaceSession.kill();
+            mBitmapRenderNode.destroy();
+            synchronized (mLock) {
+                mHandler.removeCallbacks(mMagnifierUpdater);
+                if (mBitmap != null) {
+                    mBitmap.recycle();
+                }
+            }
+        }
+
+        private void doDraw() {
+            final ThreadedRenderer.FrameDrawingCallback callback;
+
+            // Draw the current bitmap to the surface, and prepare the callback which updates the
+            // surface position. These have to be in the same synchronized block, in order to
+            // guarantee the consistency between the bitmap content and the surface position.
+            synchronized (mLock) {
+                if (!mSurface.isValid()) {
+                    // Probably #destroy() was called for the current instance, so we skip the draw.
+                    return;
+                }
+
+                final DisplayListCanvas canvas =
+                        mBitmapRenderNode.start(mContentWidth, mContentHeight);
+                try {
+                    final Rect srcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+                    final Rect dstRect = new Rect(0, 0, mContentWidth, mContentHeight);
+                    final Paint paint = new Paint();
+                    paint.setFilterBitmap(true);
+                    canvas.drawBitmap(mBitmap, srcRect, dstRect, paint);
+                } finally {
+                    mBitmapRenderNode.end(canvas);
+                }
+
+                if (mPendingWindowPositionUpdate || mFirstDraw) {
+                    // If the window has to be shown or moved, defer this until the next draw.
+                    final boolean firstDraw = mFirstDraw;
+                    mFirstDraw = false;
+                    final boolean updateWindowPosition = mPendingWindowPositionUpdate;
+                    mPendingWindowPositionUpdate = false;
+                    final int pendingX = mWindowPositionX;
+                    final int pendingY = mWindowPositionY;
+
+                    callback = frame -> {
+                        mRenderer.setLightCenter(mDisplay, pendingX, pendingY);
+                        // Show or move the window at the content draw frame.
+                        SurfaceControl.openTransaction();
+                        mSurfaceControl.deferTransactionUntil(mSurface, frame);
+                        if (updateWindowPosition) {
+                            mSurfaceControl.setPosition(pendingX, pendingY);
+                        }
+                        if (firstDraw) {
+                            mSurfaceControl.show();
+                        }
+                        SurfaceControl.closeTransaction();
+                    };
+                } else {
+                    callback = null;
+                }
+
+                mFrameDrawScheduled = false;
+            }
+
+            mRenderer.draw(callback);
+            if (mCallback != null) {
+                mCallback.onOperationComplete();
+            }
+        }
+    }
+
+    // The rest of the file consists of test APIs.
+
+    /**
+     * See {@link #setOnOperationCompleteCallback(Callback)}.
+     */
+    @TestApi
+    private Callback mCallback;
+
+    /**
+     * Sets a callback which will be invoked at the end of the next
+     * {@link #show(float, float)} or {@link #update()} operation.
+     *
+     * @hide
+     */
+    @TestApi
+    public void setOnOperationCompleteCallback(final Callback callback) {
+        mCallback = callback;
+        if (mWindow != null) {
+            mWindow.mCallback = callback;
+        }
     }
 
     /**
@@ -278,8 +572,13 @@
      * @hide
      */
     @TestApi
-    public Bitmap getContent() {
-        return mBitmap;
+    public @Nullable Bitmap getContent() {
+        if (mWindow == null) {
+            return null;
+        }
+        synchronized (mWindow.mLock) {
+            return mWindow.mBitmap;
+        }
     }
 
     /**
@@ -296,7 +595,7 @@
 
         final int left = mWindowCoords.x + viewLocationOnScreen[0] - viewLocationInSurface[0];
         final int top = mWindowCoords.y + viewLocationOnScreen[1] - viewLocationInSurface[1];
-        return new Rect(left, top, left + mWindow.getWidth(), top + mWindow.getHeight());
+        return new Rect(left, top, left + mWindowWidth, top + mWindowHeight);
     }
 
     /**
@@ -313,4 +612,15 @@
         size.y = resources.getDimension(com.android.internal.R.dimen.magnifier_height) / density;
         return size;
     }
+
+    /**
+     * @hide
+     */
+    @TestApi
+    public interface Callback {
+        /**
+         * Callback called after the drawing for a magnifier update has happened.
+         */
+        void onOperationComplete();
+    }
 }
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 7217def..9553cf5 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -2563,7 +2563,9 @@
                     public void onViewDetachedFromWindow(View v) {
                         v.removeOnAttachStateChangeListener(this);
 
-                        TransitionManager.endTransitions(PopupDecorView.this);
+                        if (isAttachedToWindow()) {
+                            TransitionManager.endTransitions(PopupDecorView.this);
+                        }
                     }
                 };
 
diff --git a/core/java/com/android/internal/app/procstats/SparseMappingTable.java b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
index 956ce99..91b2054 100644
--- a/core/java/com/android/internal/app/procstats/SparseMappingTable.java
+++ b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
@@ -18,6 +18,7 @@
 
 import android.os.Build;
 import android.os.Parcel;
+import android.util.EventLog;
 import android.util.Slog;
 import libcore.util.EmptyArray;
 
@@ -529,6 +530,12 @@
             readCompactedLongArray(in, array, size);
             mLongs.add(array);
         }
+        // Verify that last array's length is consistent with writeToParcel
+        if (N > 0 && mLongs.get(N - 1).length != mNextIndex) {
+            EventLog.writeEvent(0x534e4554, "73252178", -1, "");
+            throw new IllegalStateException("Expected array of length " + mNextIndex + " but was "
+                    + mLongs.get(N - 1).length);
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index d1932cf..9b4ea33 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -110,7 +110,10 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -12334,7 +12337,7 @@
     @VisibleForTesting
     public void readKernelUidCpuActiveTimesLocked(boolean onBattery) {
         final long startTimeMs = mClocks.uptimeMillis();
-        mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesUs) -> {
+        mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesMs) -> {
             uid = mapUid(uid);
             if (Process.isIsolated(uid)) {
                 mKernelUidCpuActiveTimeReader.removeUid(uid);
@@ -12347,7 +12350,7 @@
                 return;
             }
             final Uid u = getUidStatsLocked(uid);
-            u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesUs, onBattery);
+            u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesMs, onBattery);
         });
 
         final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
@@ -12363,7 +12366,7 @@
     @VisibleForTesting
     public void readKernelUidCpuClusterTimesLocked(boolean onBattery) {
         final long startTimeMs = mClocks.uptimeMillis();
-        mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesUs) -> {
+        mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesMs) -> {
             uid = mapUid(uid);
             if (Process.isIsolated(uid)) {
                 mKernelUidCpuClusterTimeReader.removeUid(uid);
@@ -12376,7 +12379,7 @@
                 return;
             }
             final Uid u = getUidStatsLocked(uid);
-            u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesUs, onBattery);
+            u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesMs, onBattery);
         });
 
         final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
@@ -13326,17 +13329,20 @@
                 = "read_binary_cpu_time";
         public static final String KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS
                 = "proc_state_cpu_times_read_delay_ms";
+        public static final String KEY_KERNEL_UID_READERS_THROTTLE_TIME
+                = "kernel_uid_readers_throttle_time";
 
         private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true;
         private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
         private static final boolean DEFAULT_READ_BINARY_CPU_TIME = false;
         private static final long DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS = 5_000;
+        private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 10_000;
 
         public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
         public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
-        // Not used right now.
         public boolean READ_BINARY_CPU_TIME = DEFAULT_READ_BINARY_CPU_TIME;
         public long PROC_STATE_CPU_TIMES_READ_DELAY_MS = DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS;
+        public long KERNEL_UID_READERS_THROTTLE_TIME = DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME;
 
         private ContentResolver mResolver;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -13374,11 +13380,14 @@
                                 DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE));
                 TRACK_CPU_ACTIVE_CLUSTER_TIME = mParser.getBoolean(
                         KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME, DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME);
-                READ_BINARY_CPU_TIME = mParser.getBoolean(
-                        KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME);
+                updateReadBinaryCpuTime(READ_BINARY_CPU_TIME,
+                        mParser.getBoolean(KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME));
                 updateProcStateCpuTimesReadDelayMs(PROC_STATE_CPU_TIMES_READ_DELAY_MS,
                         mParser.getLong(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS,
-                        DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS));
+                                DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS));
+                updateKernelUidReadersThrottleTime(KERNEL_UID_READERS_THROTTLE_TIME,
+                        mParser.getLong(KEY_KERNEL_UID_READERS_THROTTLE_TIME,
+                                DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME));
             }
         }
 
@@ -13394,6 +13403,13 @@
             }
         }
 
+        private void updateReadBinaryCpuTime(boolean oldEnabled, boolean isEnabled) {
+            READ_BINARY_CPU_TIME = isEnabled;
+            if (oldEnabled != isEnabled) {
+                mKernelUidCpuFreqTimeReader.setReadBinary(isEnabled);
+            }
+        }
+
         private void updateProcStateCpuTimesReadDelayMs(long oldDelayMillis, long newDelayMillis) {
             PROC_STATE_CPU_TIMES_READ_DELAY_MS = newDelayMillis;
             if (oldDelayMillis != newDelayMillis) {
@@ -13403,6 +13419,16 @@
             }
         }
 
+        private void updateKernelUidReadersThrottleTime(long oldTimeMs, long newTimeMs) {
+            KERNEL_UID_READERS_THROTTLE_TIME = newTimeMs;
+            if (oldTimeMs != newTimeMs) {
+                mKernelUidCpuFreqTimeReader.setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME);
+                mKernelUidCpuActiveTimeReader.setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME);
+                mKernelUidCpuClusterTimeReader
+                        .setThrottleInterval(KERNEL_UID_READERS_THROTTLE_TIME);
+            }
+        }
+
         public void dumpLocked(PrintWriter pw) {
             pw.print(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); pw.print("=");
             pw.println(TRACK_CPU_TIMES_BY_PROC_STATE);
@@ -13412,6 +13438,8 @@
             pw.println(READ_BINARY_CPU_TIME);
             pw.print(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS); pw.print("=");
             pw.println(PROC_STATE_CPU_TIMES_READ_DELAY_MS);
+            pw.print(KEY_KERNEL_UID_READERS_THROTTLE_TIME); pw.print("=");
+            pw.println(KERNEL_UID_READERS_THROTTLE_TIME);
         }
     }
 
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index a34e7f5..101c321 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -50,13 +50,14 @@
                 cpuPowerMaUs += cpuSpeedStepPower;
             }
         }
-        cpuPowerMaUs += u.getCpuActiveTime() * mProfile.getAveragePower(
+        cpuPowerMaUs += u.getCpuActiveTime() * 1000 * mProfile.getAveragePower(
                 PowerProfile.POWER_CPU_ACTIVE);
         long[] cpuClusterTimes = u.getCpuClusterTimes();
         if (cpuClusterTimes != null) {
             if (cpuClusterTimes.length == numClusters) {
                 for (int i = 0; i < numClusters; i++) {
-                    double power = cpuClusterTimes[i] * mProfile.getAveragePowerForCpuCluster(i);
+                    double power =
+                            cpuClusterTimes[i] * 1000 * mProfile.getAveragePowerForCpuCluster(i);
                     cpuPowerMaUs += power;
                     if (DEBUG) {
                         Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + i + " clusterTimeUs="
diff --git a/core/java/com/android/internal/os/KernelCpuProcReader.java b/core/java/com/android/internal/os/KernelCpuProcReader.java
new file mode 100644
index 0000000..4d56905
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelCpuProcReader.java
@@ -0,0 +1,159 @@
+/*
+ * 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.internal.os;
+
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+
+/**
+ * Reads cpu time proc files with throttling (adjustable interval).
+ *
+ * KernelCpuProcReader is implemented as singletons for built-in kernel proc files. Get___Instance()
+ * method will return corresponding reader instance. In order to prevent frequent GC,
+ * KernelCpuProcReader reuses a {@link ByteBuffer} to store data read from proc files.
+ *
+ * A KernelCpuProcReader instance keeps an error counter. When the number of read errors within that
+ * instance accumulates to 5, this instance will reject all further read requests.
+ *
+ * Each KernelCpuProcReader instance also has a throttler. Throttle interval can be adjusted via
+ * {@link #setThrottleInterval(long)} method. Default throttle interval is 3000ms. If current
+ * timestamp based on {@link SystemClock#elapsedRealtime()} is less than throttle interval from
+ * the last read timestamp, {@link #readBytes()} will return previous result.
+ *
+ * A KernelCpuProcReader instance is thread-unsafe. Caller needs to hold a lock on this object while
+ * accessing its instance methods or digesting the return values.
+ */
+public class KernelCpuProcReader {
+    private static final String TAG = "KernelCpuProcReader";
+    private static final int ERROR_THRESHOLD = 5;
+    // Throttle interval in milliseconds
+    private static final long DEFAULT_THROTTLE_INTERVAL = 3000L;
+    private static final int INITIAL_BUFFER_SIZE = 8 * 1024;
+    private static final int MAX_BUFFER_SIZE = 1024 * 1024;
+    private static final String PROC_UID_FREQ_TIME = "/proc/uid_cpupower/time_in_state";
+    private static final String PROC_UID_ACTIVE_TIME = "/proc/uid_cpupower/concurrent_active_time";
+    private static final String PROC_UID_CLUSTER_TIME = "/proc/uid_cpupower/concurrent_policy_time";
+
+    private static final KernelCpuProcReader mFreqTimeReader = new KernelCpuProcReader(
+            PROC_UID_FREQ_TIME);
+    private static final KernelCpuProcReader mActiveTimeReader = new KernelCpuProcReader(
+            PROC_UID_ACTIVE_TIME);
+    private static final KernelCpuProcReader mClusterTimeReader = new KernelCpuProcReader(
+            PROC_UID_CLUSTER_TIME);
+
+    public static KernelCpuProcReader getFreqTimeReaderInstance() {
+        return mFreqTimeReader;
+    }
+
+    public static KernelCpuProcReader getActiveTimeReaderInstance() {
+        return mActiveTimeReader;
+    }
+
+    public static KernelCpuProcReader getClusterTimeReaderInstance() {
+        return mClusterTimeReader;
+    }
+
+    private int mErrors;
+    private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL;
+    private long mLastReadTime = Long.MIN_VALUE;
+    private final Path mProc;
+    private ByteBuffer mBuffer;
+
+    @VisibleForTesting
+    public KernelCpuProcReader(String procFile) {
+        mProc = Paths.get(procFile);
+        mBuffer = ByteBuffer.allocateDirect(INITIAL_BUFFER_SIZE);
+        mBuffer.clear();
+    }
+
+    /**
+     * Reads all bytes from the corresponding proc file.
+     *
+     * If elapsed time since last call to this method is less than the throttle interval, it will
+     * return previous result. When IOException accumulates to 5, it will always return null. This
+     * method is thread-unsafe, so is the return value. Caller needs to hold a lock on this
+     * object while calling this method and digesting its return value.
+     *
+     * @return a {@link ByteBuffer} containing all bytes from the proc file.
+     */
+    public ByteBuffer readBytes() {
+        if (mErrors >= ERROR_THRESHOLD) {
+            return null;
+        }
+        if (SystemClock.elapsedRealtime() < mLastReadTime + mThrottleInterval) {
+            if (mBuffer.limit() > 0 && mBuffer.limit() < mBuffer.capacity()) {
+                // mBuffer has data.
+                return mBuffer.asReadOnlyBuffer().order(ByteOrder.nativeOrder());
+            }
+            return null;
+        }
+        mLastReadTime = SystemClock.elapsedRealtime();
+        mBuffer.clear();
+        final int oldMask = StrictMode.allowThreadDiskReadsMask();
+        try (FileChannel fc = FileChannel.open(mProc, StandardOpenOption.READ)) {
+            while (fc.read(mBuffer) == mBuffer.capacity()) {
+                if (!resize()) {
+                    mErrors++;
+                    Slog.e(TAG, "Proc file is too large: " + mProc);
+                    return null;
+                }
+                fc.position(0);
+            }
+        } catch (IOException e) {
+            mErrors++;
+            Slog.e(TAG, "Error reading: " + mProc, e);
+            return null;
+        } finally {
+            StrictMode.setThreadPolicyMask(oldMask);
+        }
+        mBuffer.flip();
+        return mBuffer.asReadOnlyBuffer().order(ByteOrder.nativeOrder());
+    }
+
+    /**
+     * Sets the throttle interval. Set to 0 will disable throttling. Thread-unsafe, holding a lock
+     * on this object is recommended.
+     *
+     * @param throttleInterval throttle interval in milliseconds
+     */
+    public void setThrottleInterval(long throttleInterval) {
+        if (throttleInterval >= 0) {
+            mThrottleInterval = throttleInterval;
+        }
+    }
+
+    private boolean resize() {
+        if (mBuffer.capacity() >= MAX_BUFFER_SIZE) {
+            return false;
+        }
+        int newSize = Math.min(mBuffer.capacity() << 1, MAX_BUFFER_SIZE);
+        // Slog.i(TAG, "Resize buffer " + mBuffer.capacity() + " => " + newSize);
+        mBuffer = ByteBuffer.allocateDirect(newSize);
+        return true;
+    }
+}
diff --git a/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
index cb96c5c..2519412 100644
--- a/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
@@ -17,53 +17,121 @@
 package com.android.internal.os;
 
 import android.annotation.Nullable;
-import android.os.StrictMode;
 import android.os.SystemClock;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.TimeUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
 
 /**
- * Reads /proc/uid_concurrent_active_time which has the format:
- * active: X (X is # cores)
- * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores)
- * [uid1]: [time-0] [time-1] [time-2] ... ...
+ * Reads binary proc file /proc/uid_cpupower/concurrent_active_time and reports CPU active time to
+ * BatteryStats to compute {@link PowerProfile#POWER_CPU_ACTIVE}.
+ *
+ * concurrent_active_time is an array of u32's in the following format:
+ * [n, uid0, time0a, time0b, ..., time0n,
+ * uid1, time1a, time1b, ..., time1n,
+ * uid2, time2a, time2b, ..., time2n, etc.]
+ * where n is the total number of cpus (num_possible_cpus)
  * ...
- * Time-N means the CPU time a UID spent running concurrently with N other processes.
+ * timeXn means the CPU time that a UID X spent running concurrently with n other processes.
  * The file contains a monotonically increasing count of time for a single boot. This class
  * maintains the previous results of a call to {@link #readDelta} in order to provide a
  * proper delta.
+ *
+ * This class uses a throttler to reject any {@link #readDelta} call within
+ * {@link #mThrottleInterval}. This is different from the throttler in {@link KernelCpuProcReader},
+ * which has a shorter throttle interval and returns cached result from last read when the request
+ * is throttled.
+ *
+ * This class is NOT thread-safe and NOT designed to be accessed by more than one caller (due to
+ * the nature of {@link #readDelta(Callback)}).
  */
 public class KernelUidCpuActiveTimeReader {
-    private static final boolean DEBUG = false;
     private static final String TAG = "KernelUidCpuActiveTimeReader";
-    private static final String UID_TIMES_PROC_FILE = "/proc/uid_concurrent_active_time";
+    // Throttle interval in milliseconds
+    private static final long DEFAULT_THROTTLE_INTERVAL = 10_000L;
 
-    private int mCoreCount;
-    private long mLastTimeReadMs;
-    private long mNowTimeMs;
-    private SparseArray<long[]> mLastUidCpuActiveTimeMs = new SparseArray<>();
+    private final KernelCpuProcReader mProcReader;
+    private long mLastTimeReadMs = Long.MIN_VALUE;
+    private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL;
+    private SparseArray<Double> mLastUidCpuActiveTimeMs = new SparseArray<>();
 
     public interface Callback {
+        /**
+         * Notifies when new data is available.
+         *
+         * @param uid             uid int
+         * @param cpuActiveTimeMs cpu active time spent by this uid in milliseconds
+         */
         void onUidCpuActiveTime(int uid, long cpuActiveTimeMs);
     }
 
+    public KernelUidCpuActiveTimeReader() {
+        mProcReader = KernelCpuProcReader.getActiveTimeReaderInstance();
+    }
+
+    @VisibleForTesting
+    public KernelUidCpuActiveTimeReader(KernelCpuProcReader procReader) {
+        mProcReader = procReader;
+    }
+
     public void readDelta(@Nullable Callback cb) {
-        final int oldMask = StrictMode.allowThreadDiskReadsMask();
-        try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
-            mNowTimeMs = SystemClock.elapsedRealtime();
-            readDeltaInternal(reader, cb);
-            mLastTimeReadMs = mNowTimeMs;
-        } catch (IOException e) {
-            Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
-        } finally {
-            StrictMode.setThreadPolicyMask(oldMask);
+        if (SystemClock.elapsedRealtime() < mLastTimeReadMs + mThrottleInterval) {
+            Slog.w(TAG, "Throttle");
+            return;
+        }
+        synchronized (mProcReader) {
+            final ByteBuffer bytes = mProcReader.readBytes();
+            if (bytes == null || bytes.remaining() <= 4) {
+                // Error already logged in mProcReader.
+                return;
+            }
+            if ((bytes.remaining() & 3) != 0) {
+                Slog.wtf(TAG,
+                        "Cannot parse active time proc bytes to int: " + bytes.remaining());
+                return;
+            }
+            final IntBuffer buf = bytes.asIntBuffer();
+            final int cores = buf.get();
+            if (cores <= 0 || buf.remaining() % (cores + 1) != 0) {
+                Slog.wtf(TAG,
+                        "Cpu active time format error: " + buf.remaining() + " / " + (cores
+                                + 1));
+                return;
+            }
+            int numUids = buf.remaining() / (cores + 1);
+            for (int i = 0; i < numUids; i++) {
+                int uid = buf.get();
+                boolean corrupted = false;
+                double curTime = 0;
+                for (int j = 1; j <= cores; j++) {
+                    int time = buf.get();
+                    if (time < 0) {
+                        Slog.e(TAG, "Corrupted data from active time proc: " + time);
+                        corrupted = true;
+                    } else {
+                        curTime += (double) time * 10 / j; // Unit is 10ms.
+                    }
+                }
+                double delta = curTime - mLastUidCpuActiveTimeMs.get(uid, 0.0);
+                if (delta > 0 && !corrupted) {
+                    mLastUidCpuActiveTimeMs.put(uid, curTime);
+                    if (cb != null) {
+                        cb.onUidCpuActiveTime(uid, (long) delta);
+                    }
+                }
+            }
+            // Slog.i(TAG, "Read uids: " + numUids);
+        }
+        mLastTimeReadMs = SystemClock.elapsedRealtime();
+    }
+
+    public void setThrottleInterval(long throttleInterval) {
+        if (throttleInterval >= 0) {
+            mThrottleInterval = throttleInterval;
         }
     }
 
@@ -82,65 +150,4 @@
         final int lastIndex = mLastUidCpuActiveTimeMs.indexOfKey(endUid);
         mLastUidCpuActiveTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
     }
-
-    @VisibleForTesting
-    public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException {
-        String line = reader.readLine();
-        if (line == null || !line.startsWith("active:")) {
-            Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE));
-            return;
-        }
-        if (mCoreCount == 0) {
-            mCoreCount = Integer.parseInt(line.substring(line.indexOf(' ')+1));
-        }
-        while ((line = reader.readLine()) != null) {
-            final int index = line.indexOf(' ');
-            final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
-            readTimesForUid(uid, line.substring(index + 1), cb);
-        }
-    }
-
-    private void readTimesForUid(int uid, String line, @Nullable Callback cb) {
-        long[] lastActiveTime = mLastUidCpuActiveTimeMs.get(uid);
-        if (lastActiveTime == null) {
-            lastActiveTime = new long[mCoreCount];
-            mLastUidCpuActiveTimeMs.put(uid, lastActiveTime);
-        }
-        final String[] timesStr = line.split(" ");
-        if (timesStr.length != mCoreCount) {
-            Slog.e(TAG, String.format("# readings don't match # cores, readings: %d, CPU cores: %d",
-                    timesStr.length, mCoreCount));
-            return;
-        }
-        long sumDeltas = 0;
-        final long[] curActiveTime = new long[mCoreCount];
-        boolean notify = false;
-        for (int i = 0; i < mCoreCount; i++) {
-            // Times read will be in units of 10ms
-            curActiveTime[i] = Long.parseLong(timesStr[i], 10) * 10;
-            long delta = curActiveTime[i] - lastActiveTime[i];
-            if (delta < 0 || curActiveTime[i] < 0) {
-                if (DEBUG) {
-                    final StringBuilder sb = new StringBuilder();
-                    sb.append(String.format("Malformed cpu active time for UID=%d\n", uid));
-                    sb.append(String.format("data=(%d,%d)\n", lastActiveTime[i], curActiveTime[i]));
-                    sb.append("times=(");
-                    TimeUtils.formatDuration(mLastTimeReadMs, sb);
-                    sb.append(",");
-                    TimeUtils.formatDuration(mNowTimeMs, sb);
-                    sb.append(")");
-                    Slog.e(TAG, sb.toString());
-                }
-                return;
-            }
-            notify |= delta > 0;
-            sumDeltas += delta / (i + 1);
-        }
-        if (notify) {
-            System.arraycopy(curActiveTime, 0, lastActiveTime, 0, mCoreCount);
-            if (cb != null) {
-                cb.onUidCpuActiveTime(uid, sumDeltas);
-            }
-        }
-    }
 }
diff --git a/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
index 85153bc..41ef8f0 100644
--- a/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
@@ -17,64 +17,191 @@
 package com.android.internal.os;
 
 import android.annotation.Nullable;
-import android.os.StrictMode;
 import android.os.SystemClock;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.TimeUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
 
 /**
- * Reads /proc/uid_concurrent_policy_time which has the format:
- * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4)
- * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
- * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
- * ...
- * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes.
+ * Reads binary proc file /proc/uid_cpupower/concurrent_policy_time and reports CPU cluster times
+ * to BatteryStats to compute cluster power. See
+ * {@link PowerProfile#getAveragePowerForCpuCluster(int)}.
+ *
+ * concurrent_policy_time is an array of u32's in the following format:
+ * [n, x0, ..., xn, uid0, time0a, time0b, ..., time0n,
+ * uid1, time1a, time1b, ..., time1n,
+ * uid2, time2a, time2b, ..., time2n, etc.]
+ * where n is the number of policies
+ * xi is the number cpus on a particular policy
+ * Each uidX is followed by x0 time entries corresponding to the time UID X spent on cluster0
+ * running concurrently with 0, 1, 2, ..., x0 - 1 other processes, then followed by x1, ..., xn
+ * time entries.
+ *
  * The file contains a monotonically increasing count of time for a single boot. This class
- * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
- * delta.
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a
+ * proper delta.
+ *
+ * This class uses a throttler to reject any {@link #readDelta} call within
+ * {@link #mThrottleInterval}. This is different from the throttler in {@link KernelCpuProcReader},
+ * which has a shorter throttle interval and returns cached result from last read when the request
+ * is throttled.
+ *
+ * This class is NOT thread-safe and NOT designed to be accessed by more than one caller (due to
+ * the nature of {@link #readDelta(Callback)}).
  */
 public class KernelUidCpuClusterTimeReader {
-
-    private static final boolean DEBUG = false;
     private static final String TAG = "KernelUidCpuClusterTimeReader";
-    private static final String UID_TIMES_PROC_FILE = "/proc/uid_concurrent_policy_time";
+    // Throttle interval in milliseconds
+    private static final long DEFAULT_THROTTLE_INTERVAL = 10_000L;
 
-    // mCoreOnCluster[i] is the # of cores on cluster i
-    private int[] mCoreOnCluster;
-    private int mCores;
-    private long mLastTimeReadMs;
-    private long mNowTimeMs;
-    private SparseArray<long[]> mLastUidPolicyTimeMs = new SparseArray<>();
+    private final KernelCpuProcReader mProcReader;
+    private long mLastTimeReadMs = Long.MIN_VALUE;
+    private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL;
+    private SparseArray<double[]> mLastUidPolicyTimeMs = new SparseArray<>();
+
+    private int mNumClusters = -1;
+    private int mNumCores;
+    private int[] mNumCoresOnCluster;
+
+    private double[] mCurTime; // Reuse to avoid GC.
+    private long[] mDeltaTime; // Reuse to avoid GC.
 
     public interface Callback {
         /**
-         * @param uid
-         * @param cpuActiveTimeMs the first dimension is cluster, the second dimension is the # of
-         *                        processes running concurrently with this uid.
+         * Notifies when new data is available.
+         *
+         * @param uid              uid int
+         * @param cpuClusterTimeMs an array of times spent by this uid on corresponding clusters.
+         *                         The array index is the cluster index.
          */
-        void onUidCpuPolicyTime(int uid, long[] cpuActiveTimeMs);
+        void onUidCpuPolicyTime(int uid, long[] cpuClusterTimeMs);
+    }
+
+    public KernelUidCpuClusterTimeReader() {
+        mProcReader = KernelCpuProcReader.getClusterTimeReaderInstance();
+    }
+
+    @VisibleForTesting
+    public KernelUidCpuClusterTimeReader(KernelCpuProcReader procReader) {
+        mProcReader = procReader;
+    }
+
+    public void setThrottleInterval(long throttleInterval) {
+        if (throttleInterval >= 0) {
+            mThrottleInterval = throttleInterval;
+        }
     }
 
     public void readDelta(@Nullable Callback cb) {
-        final int oldMask = StrictMode.allowThreadDiskReadsMask();
-        try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
-            mNowTimeMs = SystemClock.elapsedRealtime();
-            readDeltaInternal(reader, cb);
-            mLastTimeReadMs = mNowTimeMs;
-        } catch (IOException e) {
-            Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
-        } finally {
-            StrictMode.setThreadPolicyMask(oldMask);
+        if (SystemClock.elapsedRealtime() < mLastTimeReadMs + mThrottleInterval) {
+            Slog.w(TAG, "Throttle");
+            return;
         }
+        synchronized (mProcReader) {
+            ByteBuffer bytes = mProcReader.readBytes();
+            if (bytes == null || bytes.remaining() <= 4) {
+                // Error already logged in mProcReader.
+                return;
+            }
+            if ((bytes.remaining() & 3) != 0) {
+                Slog.wtf(TAG,
+                        "Cannot parse cluster time proc bytes to int: " + bytes.remaining());
+                return;
+            }
+            IntBuffer buf = bytes.asIntBuffer();
+            final int numClusters = buf.get();
+            if (numClusters <= 0) {
+                Slog.wtf(TAG, "Cluster time format error: " + numClusters);
+                return;
+            }
+            if (mNumClusters == -1) {
+                mNumClusters = numClusters;
+            }
+            if (buf.remaining() < numClusters) {
+                Slog.wtf(TAG, "Too few data left in the buffer: " + buf.remaining());
+                return;
+            }
+            if (mNumCores <= 0) {
+                if (!readCoreInfo(buf, numClusters)) {
+                    return;
+                }
+            } else {
+                buf.position(buf.position() + numClusters);
+            }
+
+            if (buf.remaining() % (mNumCores + 1) != 0) {
+                Slog.wtf(TAG,
+                        "Cluster time format error: " + buf.remaining() + " / " + (mNumCores
+                                + 1));
+                return;
+            }
+            int numUids = buf.remaining() / (mNumCores + 1);
+
+            for (int i = 0; i < numUids; i++) {
+                processUidLocked(buf, cb);
+            }
+            // Slog.i(TAG, "Read uids: " + numUids);
+        }
+        mLastTimeReadMs = SystemClock.elapsedRealtime();
+    }
+
+    private void processUidLocked(IntBuffer buf, @Nullable Callback cb) {
+        int uid = buf.get();
+        double[] lastTimes = mLastUidPolicyTimeMs.get(uid);
+        if (lastTimes == null) {
+            lastTimes = new double[mNumClusters];
+            mLastUidPolicyTimeMs.put(uid, lastTimes);
+        }
+
+        boolean notify = false;
+        boolean corrupted = false;
+
+        for (int j = 0; j < mNumClusters; j++) {
+            mCurTime[j] = 0;
+            for (int k = 1; k <= mNumCoresOnCluster[j]; k++) {
+                int time = buf.get();
+                if (time < 0) {
+                    Slog.e(TAG, "Corrupted data from cluster time proc uid: " + uid);
+                    corrupted = true;
+                }
+                mCurTime[j] += (double) time * 10 / k; // Unit is 10ms.
+            }
+            mDeltaTime[j] = (long) (mCurTime[j] - lastTimes[j]);
+            if (mDeltaTime[j] < 0) {
+                Slog.e(TAG, "Unexpected delta from cluster time proc uid: " + uid);
+                corrupted = true;
+            }
+            notify |= mDeltaTime[j] > 0;
+        }
+        if (notify && !corrupted) {
+            System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
+            if (cb != null) {
+                cb.onUidCpuPolicyTime(uid, mDeltaTime);
+            }
+        }
+    }
+
+    // Returns if it has read valid info.
+    private boolean readCoreInfo(IntBuffer buf, int numClusters) {
+        int numCores = 0;
+        int[] numCoresOnCluster = new int[numClusters];
+        for (int i = 0; i < numClusters; i++) {
+            numCoresOnCluster[i] = buf.get();
+            numCores += numCoresOnCluster[i];
+        }
+        if (numCores <= 0) {
+            Slog.e(TAG, "Invalid # cores from cluster time proc file: " + numCores);
+            return false;
+        }
+        mNumCores = numCores;
+        mNumCoresOnCluster = numCoresOnCluster;
+        mCurTime = new double[numClusters];
+        mDeltaTime = new long[numClusters];
+        return true;
     }
 
     public void removeUid(int uid) {
@@ -92,87 +219,4 @@
         final int lastIndex = mLastUidPolicyTimeMs.indexOfKey(endUid);
         mLastUidPolicyTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
     }
-
-    @VisibleForTesting
-    public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException {
-        String line = reader.readLine();
-        if (line == null || !line.startsWith("policy")) {
-            Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE));
-            return;
-        }
-        if (mCoreOnCluster == null) {
-            List<Integer> list = new ArrayList<>();
-            String[] policies = line.split(" ");
-
-            if (policies.length == 0 || policies.length % 2 != 0) {
-                Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE));
-                return;
-            }
-
-            for (int i = 0; i < policies.length; i+=2) {
-                list.add(Integer.parseInt(policies[i+1]));
-            }
-
-            mCoreOnCluster = new int[list.size()];
-            for(int i=0;i<list.size();i++){
-                mCoreOnCluster[i] = list.get(i);
-                mCores += mCoreOnCluster[i];
-            }
-        }
-        while ((line = reader.readLine()) != null) {
-            final int index = line.indexOf(' ');
-            final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
-            readTimesForUid(uid, line.substring(index + 1), cb);
-        }
-    }
-
-    private void readTimesForUid(int uid, String line, @Nullable Callback cb) {
-        long[] lastPolicyTime = mLastUidPolicyTimeMs.get(uid);
-        if (lastPolicyTime == null) {
-            lastPolicyTime = new long[mCores];
-            mLastUidPolicyTimeMs.put(uid, lastPolicyTime);
-        }
-        final String[] timeStr = line.split(" ");
-        if (timeStr.length != mCores) {
-            Slog.e(TAG, String.format("# readings don't match # cores, readings: %d, # CPU cores: %d",
-                    timeStr.length, mCores));
-            return;
-        }
-        final long[] deltaPolicyTime = new long[mCores];
-        final long[] currPolicyTime = new long[mCores];
-        boolean notify = false;
-        for (int i = 0; i < mCores; i++) {
-            // Times read will be in units of 10ms
-            currPolicyTime[i] = Long.parseLong(timeStr[i], 10) * 10;
-            deltaPolicyTime[i] = currPolicyTime[i] - lastPolicyTime[i];
-            if (deltaPolicyTime[i] < 0 || currPolicyTime[i] < 0) {
-                if (DEBUG) {
-                    final StringBuilder sb = new StringBuilder();
-                    sb.append(String.format("Malformed cpu policy time for UID=%d\n", uid));
-                    sb.append(String.format("data=(%d,%d)\n", lastPolicyTime[i], currPolicyTime[i]));
-                    sb.append("times=(");
-                    TimeUtils.formatDuration(mLastTimeReadMs, sb);
-                    sb.append(",");
-                    TimeUtils.formatDuration(mNowTimeMs, sb);
-                    sb.append(")");
-                    Slog.e(TAG, sb.toString());
-                }
-                return;
-            }
-            notify |= deltaPolicyTime[i] > 0;
-        }
-        if (notify) {
-            System.arraycopy(currPolicyTime, 0, lastPolicyTime, 0, mCores);
-            if (cb != null) {
-                final long[] times = new long[mCoreOnCluster.length];
-                int core = 0;
-                for (int i = 0; i < mCoreOnCluster.length; i++) {
-                    for (int j = 0; j < mCoreOnCluster[i]; j++) {
-                        times[i] += deltaPolicyTime[core++] / (j+1);
-                    }
-                }
-                cb.onUidCpuPolicyTime(uid, times);
-            }
-        }
-    }
 }
diff --git a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
index d97538c..a21a70e 100644
--- a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
@@ -32,6 +32,8 @@
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
 
 /**
  * Reads /proc/uid_time_in_state which has the format:
@@ -41,24 +43,45 @@
  * [uid2]: [time in freq1] [time in freq2] [time in freq3] ...
  * ...
  *
+ * Binary variation reads /proc/uid_cpupower/time_in_state in the following format:
+ * [n, uid0, time0a, time0b, ..., time0n,
+ * uid1, time1a, time1b, ..., time1n,
+ * uid2, time2a, time2b, ..., time2n, etc.]
+ * where n is the total number of frequencies.
+ *
  * This provides the times a UID's processes spent executing at each different cpu frequency.
  * The file contains a monotonically increasing count of time for a single boot. This class
  * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
  * delta.
+ *
+ * This class uses a throttler to reject any {@link #readDelta} call within
+ * {@link #mThrottleInterval}. This is different from the throttler in {@link KernelCpuProcReader},
+ * which has a shorter throttle interval and returns cached result from last read when the request
+ * is throttled.
+ *
+ * This class is NOT thread-safe and NOT designed to be accessed by more than one caller (due to
+ * the nature of {@link #readDelta(Callback)}).
  */
 public class KernelUidCpuFreqTimeReader {
     private static final boolean DEBUG = false;
     private static final String TAG = "KernelUidCpuFreqTimeReader";
     static final String UID_TIMES_PROC_FILE = "/proc/uid_time_in_state";
+    // Throttle interval in milliseconds
+    private static final long DEFAULT_THROTTLE_INTERVAL = 10_000L;
 
     public interface Callback {
         void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs);
     }
 
     private long[] mCpuFreqs;
+    private long[] mCurTimes; // Reuse to prevent GC.
+    private long[] mDeltaTimes; // Reuse to prevent GC.
+    private long mThrottleInterval = DEFAULT_THROTTLE_INTERVAL;
     private int mCpuFreqsCount;
-    private long mLastTimeReadMs;
+    private long mLastTimeReadMs = Long.MIN_VALUE;
     private long mNowTimeMs;
+    private boolean mReadBinary = true;
+    private final KernelCpuProcReader mProcReader;
 
     private SparseArray<long[]> mLastUidCpuFreqTimeMs = new SparseArray<>();
 
@@ -69,6 +92,15 @@
     private boolean mPerClusterTimesAvailable;
     private boolean mAllUidTimesAvailable = true;
 
+    public KernelUidCpuFreqTimeReader() {
+        mProcReader = KernelCpuProcReader.getFreqTimeReaderInstance();
+    }
+
+    @VisibleForTesting
+    public KernelUidCpuFreqTimeReader(KernelCpuProcReader procReader) {
+        mProcReader = procReader;
+    }
+
     public boolean perClusterTimesAvailable() {
         return mPerClusterTimesAvailable;
     }
@@ -83,7 +115,6 @@
 
     public long[] readFreqs(@NonNull PowerProfile powerProfile) {
         checkNotNull(powerProfile);
-
         if (mCpuFreqs != null) {
             // No need to read cpu freqs more than once.
             return mCpuFreqs;
@@ -115,15 +146,37 @@
         return readCpuFreqs(line, powerProfile);
     }
 
+    public void setReadBinary(boolean readBinary) {
+        mReadBinary = readBinary;
+    }
+
+    public void setThrottleInterval(long throttleInterval) {
+        if (throttleInterval >= 0) {
+            mThrottleInterval = throttleInterval;
+        }
+    }
+
     public void readDelta(@Nullable Callback callback) {
         if (mCpuFreqs == null) {
             return;
         }
+        if (SystemClock.elapsedRealtime() < mLastTimeReadMs + mThrottleInterval) {
+            Slog.w(TAG, "Throttle");
+            return;
+        }
+        mNowTimeMs = SystemClock.elapsedRealtime();
+        if (mReadBinary) {
+            readDeltaBinary(callback);
+        } else {
+            readDeltaString(callback);
+        }
+        mLastTimeReadMs = mNowTimeMs;
+    }
+
+    private void readDeltaString(@Nullable Callback callback) {
         final int oldMask = StrictMode.allowThreadDiskReadsMask();
         try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
-            mNowTimeMs = SystemClock.elapsedRealtime();
             readDelta(reader, callback);
-            mLastTimeReadMs = mNowTimeMs;
         } catch (IOException e) {
             Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
         } finally {
@@ -131,6 +184,58 @@
         }
     }
 
+    @VisibleForTesting
+    public void readDeltaBinary(@Nullable Callback callback) {
+        synchronized (mProcReader) {
+            ByteBuffer bytes = mProcReader.readBytes();
+            if (bytes == null || bytes.remaining() <= 4) {
+                // Error already logged in mProcReader.
+                return;
+            }
+            if ((bytes.remaining() & 3) != 0) {
+                Slog.wtf(TAG, "Cannot parse cluster time proc bytes to int: " + bytes.remaining());
+                return;
+            }
+            IntBuffer buf = bytes.asIntBuffer();
+            final int freqs = buf.get();
+            if (freqs != mCpuFreqsCount) {
+                Slog.wtf(TAG, "Cpu freqs expect " + mCpuFreqsCount + " , got " + freqs);
+                return;
+            }
+            if (buf.remaining() % (freqs + 1) != 0) {
+                Slog.wtf(TAG, "Freq time format error: " + buf.remaining() + " / " + (freqs + 1));
+                return;
+            }
+            int numUids = buf.remaining() / (freqs + 1);
+            for (int i = 0; i < numUids; i++) {
+                int uid = buf.get();
+                long[] lastTimes = mLastUidCpuFreqTimeMs.get(uid);
+                if (lastTimes == null) {
+                    lastTimes = new long[mCpuFreqsCount];
+                    mLastUidCpuFreqTimeMs.put(uid, lastTimes);
+                }
+                boolean notify = false;
+                boolean corrupted = false;
+                for (int j = 0; j < freqs; j++) {
+                    mCurTimes[j] = (long) buf.get() * 10; // Unit is 10ms.
+                    mDeltaTimes[j] = mCurTimes[j] - lastTimes[j];
+                    if (mCurTimes[j] < 0 || mDeltaTimes[j] < 0) {
+                        Slog.e(TAG, "Unexpected data from freq time proc: " + mCurTimes[j]);
+                        corrupted = true;
+                    }
+                    notify |= mDeltaTimes[j] > 0;
+                }
+                if (notify && !corrupted) {
+                    System.arraycopy(mCurTimes, 0, lastTimes, 0, freqs);
+                    if (callback != null) {
+                        callback.onUidCpuFreqTime(uid, mDeltaTimes);
+                    }
+                }
+            }
+            // Slog.i(TAG, "Read uids: "+numUids);
+        }
+    }
+
     public void removeUid(int uid) {
         mLastUidCpuFreqTimeMs.delete(uid);
     }
@@ -212,6 +317,8 @@
         // First item would be "uid: " which needs to be ignored.
         mCpuFreqsCount = freqStr.length - 1;
         mCpuFreqs = new long[mCpuFreqsCount];
+        mCurTimes = new long[mCpuFreqsCount];
+        mDeltaTimes = new long[mCpuFreqsCount];
         for (int i = 0; i < mCpuFreqsCount; ++i) {
             mCpuFreqs[i] = Long.parseLong(freqStr[i + 1], 10);
         }
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 732534c..17e498c 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -72,7 +72,7 @@
     void setSnapshotCreatedPendingIntent(in PendingIntent intent);
     Map getRecoverySnapshotVersions();
     void setServerParams(in byte[] serverParams);
-    void setRecoveryStatus(in String packageName, in String[] aliases, int status);
+    void setRecoveryStatus(in String alias, int status);
     Map getRecoveryStatus(in String packageName);
     void setRecoverySecretTypes(in int[] secretTypes);
     int[] getRecoverySecretTypes();
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 111934f..8b1de2f 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -63,6 +63,7 @@
     private static final int ALLOW_APP_CONFIGS = 0x08;
     private static final int ALLOW_PRIVAPP_PERMISSIONS = 0x10;
     private static final int ALLOW_OEM_PERMISSIONS = 0x20;
+    private static final int ALLOW_HIDDENAPI_WHITELISTING = 0x40;
     private static final int ALLOW_ALL = ~0;
 
     // Group-ids that are given to all packages as read from etc/permissions/*.xml.
@@ -137,6 +138,9 @@
     // These are the permitted backup transport service components
     final ArraySet<ComponentName> mBackupTransportWhitelist = new ArraySet<>();
 
+    // Package names that are exempted from private API blacklisting
+    final ArraySet<String> mHiddenApiPackageWhitelist = new ArraySet<>();
+
     // These are the packages of carrier-associated apps which should be disabled until used until
     // a SIM is inserted which grants carrier privileges to that carrier app.
     final ArrayMap<String, List<String>> mDisabledUntilUsedPreinstalledCarrierAssociatedApps =
@@ -215,6 +219,10 @@
         return mSystemUserBlacklistedApps;
     }
 
+    public ArraySet<String> getHiddenApiWhitelistedApps() {
+        return mHiddenApiPackageWhitelist;
+    }
+
     public ArraySet<ComponentName> getDefaultVrComponents() {
         return mDefaultVrComponents;
     }
@@ -376,6 +384,7 @@
             boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
             boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS) != 0;
             boolean allowOemPermissions = (permissionFlag & ALLOW_OEM_PERMISSIONS) != 0;
+            boolean allowApiWhitelisting = (permissionFlag & ALLOW_HIDDENAPI_WHITELISTING) != 0;
             while (true) {
                 XmlUtils.nextElement(parser);
                 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -637,6 +646,15 @@
                     }
                 } else if ("oem-permissions".equals(name) && allowOemPermissions) {
                     readOemPermissions(parser);
+                } else if ("hidden-api-whitelisted-app".equals(name) && allowApiWhitelisting) {
+                    String pkgname = parser.getAttributeValue(null, "package");
+                    if (pkgname == null) {
+                        Slog.w(TAG, "<hidden-api-whitelisted-app> without package in " + permFile
+                                + " at " + parser.getPositionDescription());
+                    } else {
+                        mHiddenApiPackageWhitelist.add(pkgname);
+                    }
+                    XmlUtils.skipCurrentTag(parser);
                 } else {
                     XmlUtils.skipCurrentTag(parser);
                     continue;
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 33f80ce..b048977 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -64,7 +64,7 @@
         "android_graphics_drawable_VectorDrawable.cpp",
         "android_view_DisplayEventReceiver.cpp",
         "android_view_DisplayListCanvas.cpp",
-        "android_view_HardwareLayer.cpp",
+        "android_view_TextureLayer.cpp",
         "android_view_InputChannel.cpp",
         "android_view_InputDevice.cpp",
         "android_view_InputEventReceiver.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index d202173..f280c7a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -142,7 +142,7 @@
 extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
 extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
 extern int register_android_view_DisplayListCanvas(JNIEnv* env);
-extern int register_android_view_HardwareLayer(JNIEnv* env);
+extern int register_android_view_TextureLayer(JNIEnv* env);
 extern int register_android_view_RenderNode(JNIEnv* env);
 extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
 extern int register_android_view_Surface(JNIEnv* env);
@@ -1370,7 +1370,7 @@
     REG_JNI(register_android_view_RenderNode),
     REG_JNI(register_android_view_RenderNodeAnimator),
     REG_JNI(register_android_view_DisplayListCanvas),
-    REG_JNI(register_android_view_HardwareLayer),
+    REG_JNI(register_android_view_TextureLayer),
     REG_JNI(register_android_view_ThreadedRenderer),
     REG_JNI(register_android_view_Surface),
     REG_JNI(register_android_view_SurfaceControl),
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index 98f4733..7956bf4 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -159,7 +159,7 @@
     canvas->drawRenderNode(renderNode);
 }
 
-static void android_view_DisplayListCanvas_drawLayer(jlong canvasPtr, jlong layerPtr) {
+static void android_view_DisplayListCanvas_drawTextureLayer(jlong canvasPtr, jlong layerPtr) {
     Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
     DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr);
     canvas->drawLayer(layer);
@@ -210,7 +210,7 @@
     { "nInsertReorderBarrier",    "(JZ)V",      (void*) android_view_DisplayListCanvas_insertReorderBarrier },
     { "nFinishRecording",         "(J)J",       (void*) android_view_DisplayListCanvas_finishRecording },
     { "nDrawRenderNode",          "(JJ)V",      (void*) android_view_DisplayListCanvas_drawRenderNode },
-    { "nDrawLayer",               "(JJ)V",      (void*) android_view_DisplayListCanvas_drawLayer },
+    { "nDrawTextureLayer",        "(JJ)V",      (void*) android_view_DisplayListCanvas_drawTextureLayer },
     { "nDrawCircle",              "(JJJJJ)V",   (void*) android_view_DisplayListCanvas_drawCircleProps },
     { "nDrawRoundRect",           "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps },
 };
diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_TextureLayer.cpp
similarity index 73%
rename from core/jni/android_view_HardwareLayer.cpp
rename to core/jni/android_view_TextureLayer.cpp
index d934870..e14c46f 100644
--- a/core/jni/android_view_HardwareLayer.cpp
+++ b/core/jni/android_view_TextureLayer.cpp
@@ -40,7 +40,7 @@
 
 using namespace uirenderer;
 
-static jboolean android_view_HardwareLayer_prepare(JNIEnv* env, jobject clazz,
+static jboolean TextureLayer_prepare(JNIEnv* env, jobject clazz,
         jlong layerUpdaterPtr, jint width, jint height, jboolean isOpaque) {
     DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
     bool changed = false;
@@ -49,7 +49,7 @@
     return changed;
 }
 
-static void android_view_HardwareLayer_setLayerPaint(JNIEnv* env, jobject clazz,
+static void TextureLayer_setLayerPaint(JNIEnv* env, jobject clazz,
         jlong layerUpdaterPtr, jlong paintPtr) {
     DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
     if (layer) {
@@ -58,21 +58,21 @@
     }
 }
 
-static void android_view_HardwareLayer_setTransform(JNIEnv* env, jobject clazz,
+static void TextureLayer_setTransform(JNIEnv* env, jobject clazz,
         jlong layerUpdaterPtr, jlong matrixPtr) {
     DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
     SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
     layer->setTransform(matrix);
 }
 
-static void android_view_HardwareLayer_setSurfaceTexture(JNIEnv* env, jobject clazz,
+static void TextureLayer_setSurfaceTexture(JNIEnv* env, jobject clazz,
         jlong layerUpdaterPtr, jobject surface) {
     DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
     sp<GLConsumer> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, surface));
     layer->setSurfaceTexture(surfaceTexture);
 }
 
-static void android_view_HardwareLayer_updateSurfaceTexture(JNIEnv* env, jobject clazz,
+static void TextureLayer_updateSurfaceTexture(JNIEnv* env, jobject clazz,
         jlong layerUpdaterPtr) {
     DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
     layer->updateTexImage();
@@ -82,18 +82,18 @@
 // JNI Glue
 // ----------------------------------------------------------------------------
 
-const char* const kClassPathName = "android/view/HardwareLayer";
+const char* const kClassPathName = "android/view/TextureLayer";
 
 static const JNINativeMethod gMethods[] = {
-    { "nPrepare",                "(JIIZ)Z",    (void*) android_view_HardwareLayer_prepare },
-    { "nSetLayerPaint",          "(JJ)V",      (void*) android_view_HardwareLayer_setLayerPaint },
-    { "nSetTransform",           "(JJ)V",      (void*) android_view_HardwareLayer_setTransform },
+    { "nPrepare",                "(JIIZ)Z",    (void*) TextureLayer_prepare },
+    { "nSetLayerPaint",          "(JJ)V",      (void*) TextureLayer_setLayerPaint },
+    { "nSetTransform",           "(JJ)V",      (void*) TextureLayer_setTransform },
     { "nSetSurfaceTexture",      "(JLandroid/graphics/SurfaceTexture;)V",
-            (void*) android_view_HardwareLayer_setSurfaceTexture },
-    { "nUpdateSurfaceTexture",   "(J)V",       (void*) android_view_HardwareLayer_updateSurfaceTexture },
+            (void*) TextureLayer_setSurfaceTexture },
+    { "nUpdateSurfaceTexture",   "(J)V",       (void*) TextureLayer_updateSurfaceTexture },
 };
 
-int register_android_view_HardwareLayer(JNIEnv* env) {
+int register_android_view_TextureLayer(JNIEnv* env) {
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
 }
 
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index 9f9fd05..9915174 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -226,7 +226,11 @@
       LTE = 13;
       EHRPD = 14;
       HSPAP = 15;
-      OTHER = 16;
+      GSM = 16;
+      TD_SCDMA = 17;
+      IWLAN = 18;
+      LTE_CA = 19;
+      OTHER = 20;
     };
     optional Name name = 1;
     optional TimerProto total = 2;
diff --git a/core/proto/android/os/pagetypeinfo.proto b/core/proto/android/os/pagetypeinfo.proto
index f5d77d6..0b8a5da 100644
--- a/core/proto/android/os/pagetypeinfo.proto
+++ b/core/proto/android/os/pagetypeinfo.proto
@@ -58,7 +58,7 @@
     }
     repeated MigrateType migrate_types = 3;
 
-    // Next tag: 9
+    // Next tag: 10
     message Block {
         option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
@@ -77,6 +77,8 @@
         optional int32 reserve = 7;
 
         optional int32 isolate = 8;
+
+        optional int32 highatomic = 9;
     }
     repeated Block blocks = 4;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index caeca592..2f78345 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3810,9 +3810,15 @@
     <permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE"
         android:protectionLevel="signature|development|instant|appop" />
 
-    <!-- @hide Allows system components to access all app shortcuts. -->
+    <!-- @SystemApi Allows to access all app shortcuts.
+         @hide -->
     <permission android:name="android.permission.ACCESS_SHORTCUTS"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|textClassifier" />
+
+    <!-- @SystemApi Allows unlimited calls to shortcut mutation APIs.
+         @hide -->
+    <permission android:name="android.permission.UNLIMITED_SHORTCUTS_API_CALLS"
+        android:protectionLevel="signature|textClassifier" />
 
     <!-- @SystemApi Allows an application to read the runtime profiles of other apps.
          @hide <p>Not for use by third-party applications. -->
diff --git a/core/res/res/anim/task_close_enter.xml b/core/res/res/anim/task_close_enter.xml
index 81d1300..c298b80 100644
--- a/core/res/res/anim/task_close_enter.xml
+++ b/core/res/res/anim/task_close_enter.xml
@@ -17,7 +17,8 @@
 -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="false"
-    android:zAdjustment="top">
+    android:zAdjustment="top"
+    android:showWallpaper="true">
 
     <alpha
         android:fromAlpha="1"
diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml
index ab8b89c..9394c57 100644
--- a/core/res/res/anim/task_close_exit.xml
+++ b/core/res/res/anim/task_close_exit.xml
@@ -17,7 +17,8 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false">
+    android:shareInterpolator="false"
+    android:showWallpaper="true">
 
     <alpha
         android:fromAlpha="1.0"
diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index 0e66eda..e23201f 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -19,7 +19,8 @@
 <!-- This should in sync with cross_profile_apps_thumbnail_enter.xml -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="false"
-    android:zAdjustment="top">
+    android:zAdjustment="top"
+    android:showWallpaper="true">
 
     <alpha
         android:fromAlpha="1"
diff --git a/core/res/res/anim/task_open_enter_cross_profile_apps.xml b/core/res/res/anim/task_open_enter_cross_profile_apps.xml
index a92425e..defea08 100644
--- a/core/res/res/anim/task_open_enter_cross_profile_apps.xml
+++ b/core/res/res/anim/task_open_enter_cross_profile_apps.xml
@@ -19,7 +19,8 @@
 <!-- This should in sync with task_open_enter.xml -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
     android:shareInterpolator="false"
-    android:zAdjustment="top">
+    android:zAdjustment="top"
+    android:showWallpaper="true">
 
     <alpha
         android:fromAlpha="1"
diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml
index ecb98ce..c9ade22 100644
--- a/core/res/res/anim/task_open_exit.xml
+++ b/core/res/res/anim/task_open_exit.xml
@@ -17,7 +17,8 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shareInterpolator="false">
+    android:shareInterpolator="false"
+    android:showWallpaper="true">
 
     <alpha
         android:fromAlpha="1.0"
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index 49b0ee7..07559f4 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -24,11 +24,9 @@
             android:layout_width="match_parent"
             android:layout_height="@dimen/notification_action_list_height"
             android:paddingEnd="12dp"
-            android:paddingStart="8dp"
             android:orientation="horizontal"
             android:gravity="center_vertical"
             android:visibility="gone"
-            android:background="@color/notification_action_list"
             >
         <!-- actions will be added here -->
     </com.android.internal.widget.NotificationActionListLayout>
diff --git a/core/res/res/layout/notification_material_reply_text.xml b/core/res/res/layout/notification_material_reply_text.xml
index bc22eb4..84603b0 100644
--- a/core/res/res/layout/notification_material_reply_text.xml
+++ b/core/res/res/layout/notification_material_reply_text.xml
@@ -28,7 +28,8 @@
             android:layout_width="match_parent"
             android:layout_height="1dip"
             android:id="@+id/action_divider"
-            android:layout_marginBottom="15dp"
+            android:layout_marginTop="@dimen/notification_content_margin"
+            android:layout_marginBottom="@dimen/notification_content_margin"
             android:background="@drawable/notification_template_divider" />
 
     <TextView
@@ -53,7 +54,6 @@
             android:id="@+id/notification_material_reply_text_1"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginBottom="15dp"
             android:layout_marginEnd="@dimen/notification_content_margin_end"
             android:textAppearance="@style/TextAppearance.Material.Notification.Reply"
             android:singleLine="true" />
diff --git a/core/res/res/layout/notification_template_material_ambient.xml b/core/res/res/layout/notification_template_material_ambient.xml
index 19c4d23..525d493 100644
--- a/core/res/res/layout/notification_template_material_ambient.xml
+++ b/core/res/res/layout/notification_template_material_ambient.xml
@@ -88,7 +88,6 @@
             android:orientation="horizontal"
             android:gravity="center"
             android:visibility="gone"
-            android:background="@color/notification_action_list"
         />
     </FrameLayout>
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 445b19b..221bcf6 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -30,7 +30,7 @@
         android:layout_marginStart="@dimen/notification_content_margin_start"
         android:layout_marginEnd="@dimen/notification_content_margin_end"
         android:layout_marginTop="@dimen/notification_content_margin_top"
-        android:layout_marginBottom="@dimen/notification_content_margin_bottom"
+        android:layout_marginBottom="@dimen/notification_content_margin"
         android:orientation="vertical" >
         <include layout="@layout/notification_template_part_line1" />
         <include layout="@layout/notification_template_text" />
@@ -42,7 +42,7 @@
         <include layout="@layout/notification_template_smart_reply_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginTop="@dimen/notification_content_margin_bottom" />
+            android:layout_marginTop="@dimen/notification_content_margin" />
     </LinearLayout>
     <include layout="@layout/notification_template_right_icon" />
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index d47bff6..2106890 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -20,6 +20,7 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
+    android:clipChildren="false"
     android:tag="big" >
     <LinearLayout
             android:id="@+id/notification_action_list_margin_target"
@@ -30,6 +31,7 @@
         <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:layout_weight="1"
             android:layout_gravity="top" >
             <include layout="@layout/notification_template_header" />
             <LinearLayout
@@ -39,7 +41,6 @@
                 android:layout_marginStart="@dimen/notification_content_margin_start"
                 android:layout_marginEnd="@dimen/notification_content_margin_end"
                 android:layout_marginTop="@dimen/notification_content_margin_top"
-                android:layout_marginBottom="@dimen/notification_content_margin_bottom"
                 android:orientation="vertical" >
                 <include layout="@layout/notification_template_part_line1" />
                 <include layout="@layout/notification_template_text" />
@@ -61,7 +62,7 @@
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_content_margin_start"
             android:layout_marginEnd="@dimen/notification_content_margin_end"
-            android:layout_marginBottom="@dimen/notification_content_margin_bottom" />
+            android:layout_marginTop="@dimen/notification_content_margin" />
+        <include layout="@layout/notification_material_action_list" />
     </LinearLayout>
-    <include layout="@layout/notification_material_action_list" />
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml
index cc4dd387..b4e26483 100644
--- a/core/res/res/layout/notification_template_material_big_media.xml
+++ b/core/res/res/layout/notification_template_material_big_media.xml
@@ -46,7 +46,7 @@
             android:layout_height="wrap_content"
             android:layout_marginTop="@dimen/notification_content_margin_top"
             android:layout_marginStart="@dimen/notification_content_margin_start"
-            android:layout_marginBottom="@dimen/notification_content_margin_bottom"
+            android:layout_marginBottom="@dimen/notification_content_margin"
             android:layout_marginEnd="@dimen/notification_content_margin_end"
             android:minHeight="@dimen/notification_min_content_height"
             android:orientation="vertical"
diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml
index 76c0a67..7a1cc1e 100644
--- a/core/res/res/layout/notification_template_material_big_picture.xml
+++ b/core/res/res/layout/notification_template_material_big_picture.xml
@@ -20,17 +20,18 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:tag="bigPicture"
+    android:clipChildren="false"
     >
     <include layout="@layout/notification_template_header" />
     <include layout="@layout/notification_template_right_icon" />
     <LinearLayout
+            android:id="@+id/notification_action_list_margin_target"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_gravity="top"
             android:layout_marginTop="@dimen/notification_content_margin_top"
             android:clipToPadding="false"
             android:orientation="vertical"
-            android:id="@+id/notification_action_list_margin_target"
             >
         <LinearLayout
             android:id="@+id/notification_main_column"
@@ -53,7 +54,6 @@
                 android:adjustViewBounds="true"
                 android:layout_weight="1"
                 android:layout_marginTop="13dp"
-                android:layout_marginBottom="16dp"
                 android:layout_marginStart="@dimen/notification_content_margin_start"
                 android:layout_marginEnd="@dimen/notification_content_margin_end"
                 android:scaleType="centerCrop"
@@ -69,7 +69,7 @@
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_content_margin_start"
             android:layout_marginEnd="@dimen/notification_content_margin_end"
-            android:layout_marginBottom="@dimen/notification_content_margin_bottom" />
+            android:layout_marginTop="@dimen/notification_content_margin" />
+        <include layout="@layout/notification_material_action_list" />
     </LinearLayout>
-    <include layout="@layout/notification_material_action_list" />
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index ac4c052..3a6f573 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -19,6 +19,7 @@
     android:id="@+id/status_bar_latest_event_content"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:clipChildren="false"
     android:tag="bigText"
     >
     <include layout="@layout/notification_template_header" />
@@ -43,6 +44,7 @@
             android:clipToPadding="false"
             android:minHeight="@dimen/notification_min_content_height"
             android:orientation="vertical"
+            android:layout_weight="1"
             >
             <include layout="@layout/notification_template_part_line1" />
             <include layout="@layout/notification_template_progress"
@@ -54,7 +56,6 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/notification_text_margin_top"
-                android:paddingBottom="@dimen/notification_content_margin_bottom"
                 android:textAppearance="@style/TextAppearance.Material.Notification"
                 android:singleLine="false"
                 android:gravity="top"
@@ -68,12 +69,12 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content" />
         <include layout="@layout/notification_template_smart_reply_container"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginStart="@dimen/notification_content_margin_start"
-            android:layout_marginEnd="@dimen/notification_content_margin_end"
-            android:layout_marginBottom="@dimen/notification_content_margin_bottom" />
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="@dimen/notification_content_margin_start"
+                android:layout_marginEnd="@dimen/notification_content_margin_end"
+                android:layout_marginTop="@dimen/notification_content_margin" />
+        <include layout="@layout/notification_material_action_list" />
     </LinearLayout>
-    <include layout="@layout/notification_material_action_list" />
     <include layout="@layout/notification_template_right_icon" />
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index 718cf16..23d8799 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -20,6 +20,7 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:tag="inbox"
+    android:clipChildren="false"
     >
     <include layout="@layout/notification_template_header" />
     <LinearLayout
@@ -38,8 +39,8 @@
             android:layout_gravity="top"
             android:paddingStart="@dimen/notification_content_margin_start"
             android:paddingEnd="@dimen/notification_content_margin_end"
-            android:paddingBottom="@dimen/notification_content_margin_bottom"
             android:minHeight="@dimen/notification_min_content_height"
+            android:layout_weight="1"
             android:clipToPadding="false"
             android:orientation="vertical"
             >
@@ -124,8 +125,8 @@
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/notification_content_margin_start"
             android:layout_marginEnd="@dimen/notification_content_margin_end"
-            android:layout_marginBottom="@dimen/notification_content_margin_bottom" />
+            android:layout_marginTop="@dimen/notification_content_margin" />
+        <include layout="@layout/notification_material_action_list" />
     </LinearLayout>
-    <include layout="@layout/notification_material_action_list" />
     <include layout="@layout/notification_template_right_icon" />
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 2c69b90..3a0912b 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -49,7 +49,7 @@
             android:layout_gravity="fill_vertical"
             android:layout_weight="1"
             android:minHeight="@dimen/notification_min_content_height"
-            android:paddingBottom="@dimen/notification_content_margin_bottom"
+            android:paddingBottom="@dimen/notification_content_margin"
             android:orientation="vertical"
             >
             <include layout="@layout/notification_template_part_line1"/>
diff --git a/core/res/res/layout/notification_template_material_messaging.xml b/core/res/res/layout/notification_template_material_messaging.xml
index 34f5ae8..53514a3 100644
--- a/core/res/res/layout/notification_template_material_messaging.xml
+++ b/core/res/res/layout/notification_template_material_messaging.xml
@@ -19,10 +19,11 @@
     android:id="@+id/status_bar_latest_event_content"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:clipChildren="false"
     android:tag="messaging"
     >
     <include layout="@layout/notification_template_header"/>
-    <LinearLayout
+    <com.android.internal.widget.RemeasuringLinearLayout
             android:id="@+id/notification_action_list_margin_target"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -31,15 +32,15 @@
             android:clipToPadding="false"
             android:orientation="vertical">
 
-        <LinearLayout
+        <com.android.internal.widget.RemeasuringLinearLayout
             android:id="@+id/notification_main_column"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_gravity="top"
+            android:layout_weight="1"
             android:layout_marginStart="@dimen/notification_content_margin_start"
             android:layout_marginEnd="@dimen/notification_content_margin_end"
             android:minHeight="@dimen/notification_min_content_height"
-            android:layout_marginBottom="@dimen/notification_content_margin_bottom"
             android:orientation="vertical"
             >
             <com.android.internal.widget.MessagingLinearLayout
@@ -47,12 +48,12 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:spacing="@dimen/notification_messaging_spacing" />
-            <include layout="@layout/notification_template_smart_reply_container"
+        </com.android.internal.widget.RemeasuringLinearLayout>
+        <include layout="@layout/notification_template_smart_reply_container"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_marginTop="@dimen/notification_content_margin_bottom" />
-        </LinearLayout>
-    </LinearLayout>
-    <include layout="@layout/notification_material_action_list" />
+                android:layout_marginTop="@dimen/notification_content_margin" />
+        <include layout="@layout/notification_material_action_list" />
+    </com.android.internal.widget.RemeasuringLinearLayout>
     <include layout="@layout/notification_template_right_icon"/>
 </com.android.internal.widget.MessagingLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d26567e..9d7432e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6328,6 +6328,9 @@
         <!-- Special option for window animations: if this window is on top
              of a wallpaper, don't animate the wallpaper with it. -->
         <attr name="detachWallpaper" format="boolean" />
+        <!-- Special option for window animations: show the wallpaper behind when running this
+             animation. -->
+        <attr name="showWallpaper" format="boolean" />
     </declare-styleable>
 
     <declare-styleable name="AnimationSet">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 32fe2b2..cfb5784 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -173,10 +173,10 @@
          values listed below. If no protectionLevel is defined for a custom
          permission, the system assigns the default ("normal").
          <p>Each protection level consists of a base permission type and zero or
-         more flags:
+         more flags. Use the following functions to extract those.
          <pre>
-         int basePermissionType = protectionLevel & {@link android.content.pm.PermissionInfo#PROTECTION_MASK_BASE};
-         int permissionFlags = protectionLevel & {@link android.content.pm.PermissionInfo#PROTECTION_MASK_FLAGS};
+         int basePermissionType = permissionInfo.getProtection();
+         int permissionFlags = permissionInfo.getProtectionFlags();
          </pre>
          -->
     <attr name="protectionLevel">
@@ -265,6 +265,9 @@
         <!-- Additional flag from base permission type: this permission can be granted to
              privileged apps in vendor partition. -->
         <flag name="vendorPrivileged" value="0x8000" />
+        <!-- Additional flag from base permission type: this permission can be automatically
+            granted to the system default text classifier -->
+        <flag name="textClassifier" value="0x10000" />
     </attr>
 
     <!-- Flags indicating more context for a permission group. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 01da14f..3697f3e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2372,7 +2372,7 @@
          obtain user consent to access their location through other means. -->
     <string-array name="config_disabledUntilUsedPreinstalledCarrierApps" translatable="false" />
 
-    <!-- The list of classes that should be added to the notification ranking pipline.
+    <!-- The list of classes that should be added to the notification ranking pipeline.
      See {@link com.android.server.notification.NotificationSignalExtractor}
       If you add a new extractor to this list make sure to update
       NotificationManagerService.handleRankingSort()-->
@@ -2384,11 +2384,14 @@
         <!-- depends on AdjustmentExtractor-->
         <item>com.android.server.notification.ValidateNotificationPeople</item>
         <item>com.android.server.notification.PriorityExtractor</item>
+        <!-- depends on PriorityExtractor -->
+        <item>com.android.server.notification.ZenModeExtractor</item>
         <item>com.android.server.notification.ImportanceExtractor</item>
         <!-- depends on ImportanceExtractor-->
         <item>com.android.server.notification.NotificationIntrusivenessExtractor</item>
         <item>com.android.server.notification.VisibilityExtractor</item>
         <item>com.android.server.notification.BadgeExtractor</item>
+
     </string-array>
 
     <!-- Flag indicating that this device does not rotate and will always remain in its default
@@ -3158,14 +3161,14 @@
     -->
     <string name="config_defaultAutofillService" translatable="false"></string>
 
-    <!-- The component name, flattened to a string, for the default system textclassifier service.
+    <!-- The package name for the default system textclassifier service.
          This service must be trusted, as it can be activated without explicit consent of the user.
-         (e.g. com.android.textclassifier/.TextClassifierServiceImpl).
+         Example: "com.android.textclassifier"
          If no textclassifier service with the specified name exists on the device (or if this is
          set to empty string), a default textclassifier will be loaded in the calling app's process.
          See android.view.textclassifier.TextClassificationManager.
     -->
-    <string name="config_defaultTextClassifierService" translatable="false"></string>
+    <string name="config_defaultTextClassifierPackage" translatable="false"></string>
 
     <!-- Whether the device uses the default focus highlight when focus state isn't specified. -->
     <bool name="config_useDefaultFocusHighlight">true</bool>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index cfaab6a..2ce08eb 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -176,13 +176,13 @@
     <dimen name="notification_extra_margin_ambient">16dp</dimen>
 
     <!-- The height of the notification action list -->
-    <dimen name="notification_action_list_height">56dp</dimen>
+    <dimen name="notification_action_list_height">60dp</dimen>
 
     <!-- height of the content margin to accomodate for the header -->
     <dimen name="notification_content_margin_top">46dp</dimen>
 
-    <!-- height of the content margin on the bottom -->
-    <dimen name="notification_content_margin_bottom">20dp</dimen>
+    <!-- height of the content margin that is applied at the end of the notification content -->
+    <dimen name="notification_content_margin">20dp</dimen>
 
     <!-- The height of the progress bar. -->
     <dimen name="notification_progress_bar_height">15dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 863a8cd..0493c2b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2646,7 +2646,7 @@
   <java-symbol type="dimen" name="notification_content_margin_end" />
   <java-symbol type="dimen" name="notification_content_picture_margin" />
   <java-symbol type="dimen" name="notification_content_margin_top" />
-  <java-symbol type="dimen" name="notification_content_margin_bottom" />
+  <java-symbol type="dimen" name="notification_content_margin" />
   <java-symbol type="dimen" name="notification_header_background_height" />
   <java-symbol type="dimen" name="notification_header_height" />
   <java-symbol type="dimen" name="notification_header_expand_icon_size" />
@@ -3111,7 +3111,7 @@
   <java-symbol type="string" name="notification_channel_heavy_weight_app" />
   <java-symbol type="string" name="notification_channel_system_changes" />
   <java-symbol type="string" name="config_defaultAutofillService" />
-  <java-symbol type="string" name="config_defaultTextClassifierService" />
+  <java-symbol type="string" name="config_defaultTextClassifierPackage" />
 
   <java-symbol type="string" name="notification_channel_foreground_service" />
   <java-symbol type="string" name="foreground_service_app_in_background" />
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index b7869d0..5407ce6 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -41,7 +41,7 @@
 @RunWith(AndroidJUnit4.class)
 public class TextClassificationManagerTest {
 
-    private static final LocaleList LOCALES = LocaleList.forLanguageTags("en");
+    private static final LocaleList LOCALES = LocaleList.forLanguageTags("en-US");
     private static final String NO_TYPE = null;
 
     private TextClassificationManager mTcm;
@@ -181,6 +181,42 @@
     }
 
     @Test
+    public void testTextClassifyText_date() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "Let's meet on January 9, 2018.";
+        String classifiedText = "January 9, 2018";
+        int startIndex = text.indexOf(classifiedText);
+        int endIndex = startIndex + classifiedText.length();
+
+        TextClassification classification = mClassifier.classifyText(
+                text, startIndex, endIndex, mClassificationOptions);
+        assertThat(classification,
+                isTextClassification(
+                        classifiedText,
+                        TextClassifier.TYPE_DATE,
+                        null));
+    }
+
+    @Test
+    public void testTextClassifyText_datetime() {
+        if (isTextClassifierDisabled()) return;
+
+        String text = "Let's meet 2018/01/01 10:30:20.";
+        String classifiedText = "2018/01/01 10:30:20";
+        int startIndex = text.indexOf(classifiedText);
+        int endIndex = startIndex + classifiedText.length();
+
+        TextClassification classification = mClassifier.classifyText(
+                text, startIndex, endIndex, mClassificationOptions);
+        assertThat(classification,
+                isTextClassification(
+                        classifiedText,
+                        TextClassifier.TYPE_DATE_TIME,
+                        null));
+    }
+
+    @Test
     public void testGenerateLinks_phone() {
         if (isTextClassifierDisabled()) return;
         String text = "The number is +12122537077. See you tonight!";
@@ -334,7 +370,8 @@
                             && text.equals(result.getText())
                             && result.getEntityCount() > 0
                             && type.equals(result.getEntity(0))
-                            && intentUri.equals(result.getIntent().getDataString());
+                            && (intentUri == null
+                                || intentUri.equals(result.getIntent().getDataString()));
                     // TODO: Include other properties.
                 }
                 return false;
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassifierConstantsTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierConstantsTest.java
new file mode 100644
index 0000000..984eede
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierConstantsTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextClassifierConstantsTest {
+
+    @Test
+    public void testEntityListParsing() {
+        final TextClassifierConstants constants = TextClassifierConstants.loadFromString(
+                "entity_list_default=phone,"
+                        + "entity_list_not_editable=address:flight,"
+                        + "entity_list_editable=date:datetime");
+        assertEquals(1, constants.getEntityListDefault().size());
+        assertEquals("phone", constants.getEntityListDefault().get(0));
+        assertEquals(2, constants.getEntityListNotEditable().size());
+        assertEquals("address", constants.getEntityListNotEditable().get(0));
+        assertEquals("flight", constants.getEntityListNotEditable().get(1));
+        assertEquals(2, constants.getEntityListEditable().size());
+        assertEquals("date", constants.getEntityListEditable().get(0));
+        assertEquals("datetime", constants.getEntityListEditable().get(1));
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java
new file mode 100644
index 0000000..b920ca3
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/logging/GenerateLinksLoggerTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.logging;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+
+import android.metrics.LogMaker;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.ArrayMap;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLinks;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class GenerateLinksLoggerTest {
+
+    private static final String PACKAGE_NAME = "packageName";
+    private static final String ZERO = "0";
+    private static final int LATENCY_MS = 123;
+
+    @Test
+    public void testLogGenerateLinks() {
+        final String phoneText = "+12122537077";
+        final String addressText = "1600 Amphitheater Parkway, Mountain View, CA";
+        final String testText = "The number is " + phoneText + ", the address is " + addressText;
+        final int phoneOffset = testText.indexOf(phoneText);
+        final int addressOffset = testText.indexOf(addressText);
+
+        final Map<String, Float> phoneEntityScores = new ArrayMap<>();
+        phoneEntityScores.put(TextClassifier.TYPE_PHONE, 0.9f);
+        phoneEntityScores.put(TextClassifier.TYPE_OTHER, 0.1f);
+        final Map<String, Float> addressEntityScores = new ArrayMap<>();
+        addressEntityScores.put(TextClassifier.TYPE_ADDRESS, 1f);
+
+        TextLinks links = new TextLinks.Builder(testText)
+                .addLink(phoneOffset, phoneOffset + phoneText.length(), phoneEntityScores)
+                .addLink(addressOffset, addressOffset + addressText.length(), addressEntityScores)
+                .build();
+
+        // Set up mock.
+        MetricsLogger metricsLogger = mock(MetricsLogger.class);
+        ArgumentCaptor<LogMaker> logMakerCapture = ArgumentCaptor.forClass(LogMaker.class);
+        doNothing().when(metricsLogger).write(logMakerCapture.capture());
+
+        // Generate the log.
+        GenerateLinksLogger logger = new GenerateLinksLogger(1 /* sampleRate */, metricsLogger);
+        logger.logGenerateLinks(testText, links, PACKAGE_NAME, LATENCY_MS);
+
+        // Validate.
+        List<LogMaker> logs = logMakerCapture.getAllValues();
+        assertEquals(3, logs.size());
+        assertHasLog(logs, "" /* entityType */, 2, phoneText.length() + addressText.length(),
+                testText.length());
+        assertHasLog(logs, TextClassifier.TYPE_ADDRESS, 1, addressText.length(),
+                testText.length());
+        assertHasLog(logs, TextClassifier.TYPE_PHONE, 1, phoneText.length(),
+                testText.length());
+    }
+
+    private void assertHasLog(List<LogMaker> logs, String entityType, int numLinks,
+            int linkTextLength, int textLength) {
+        for (LogMaker log : logs) {
+            if (!entityType.equals(getEntityType(log))) {
+                continue;
+            }
+            assertEquals(PACKAGE_NAME, log.getPackageName());
+            assertNotNull(Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID)));
+            assertEquals(numLinks, getIntValue(log, MetricsEvent.FIELD_LINKIFY_NUM_LINKS));
+            assertEquals(linkTextLength, getIntValue(log, MetricsEvent.FIELD_LINKIFY_LINK_LENGTH));
+            assertEquals(textLength, getIntValue(log, MetricsEvent.FIELD_LINKIFY_TEXT_LENGTH));
+            assertEquals(LATENCY_MS, getIntValue(log, MetricsEvent.FIELD_LINKIFY_LATENCY));
+            return;
+        }
+        fail("No log for entity type \"" + entityType + "\"");
+    }
+
+    private static String getEntityType(LogMaker log) {
+        return Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_ENTITY_TYPE), "");
+    }
+
+    private static int getIntValue(LogMaker log, int eventField) {
+        return Integer.parseInt(Objects.toString(log.getTaggedData(eventField), ZERO));
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 702f4b8..98b7a3f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -36,6 +36,7 @@
         BatteryStatsTimerTest.class,
         BatteryStatsUidTest.class,
         BatteryStatsUserLifecycleTests.class,
+        KernelCpuProcReaderTest.class,
         KernelMemoryBandwidthStatsTest.class,
         KernelSingleUidTimeReaderTest.class,
         KernelUidCpuFreqTimeReaderTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java
new file mode 100644
index 0000000..efdd7e9
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java
@@ -0,0 +1,198 @@
+/*
+ * 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.internal.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelCpuProcReader}.
+ *
+ * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuProcReader
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuProcReaderTest {
+
+    private File mRoot;
+    private File mTestDir;
+    private File mTestFile;
+    private Random mRand = new Random();
+
+    private KernelCpuProcReader mKernelCpuProcReader;
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Before
+    public void setUp() {
+        mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
+        mRoot = getContext().getFilesDir();
+        mTestFile = new File(mTestDir, "test.file");
+        mKernelCpuProcReader = new KernelCpuProcReader(mTestFile.getAbsolutePath());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(mTestDir);
+        FileUtils.deleteContents(mRoot);
+    }
+
+
+    /**
+     * Tests that reading will return null if the file does not exist.
+     */
+    @Test
+    public void testReadInvalidFile() throws Exception {
+        assertEquals(null, mKernelCpuProcReader.readBytes());
+    }
+
+    /**
+     * Tests that reading will always return null after 5 failures.
+     */
+    @Test
+    public void testReadErrorsLimit() throws Exception {
+        mKernelCpuProcReader.setThrottleInterval(0);
+        for (int i = 0; i < 3; i++) {
+            assertNull(mKernelCpuProcReader.readBytes());
+            SystemClock.sleep(50);
+        }
+
+        final byte[] data = new byte[1024];
+        mRand.nextBytes(data);
+        try (OutputStream os = Files.newOutputStream(mTestFile.toPath())) {
+            os.write(data);
+        }
+        assertTrue(Arrays.equals(data, toArray(mKernelCpuProcReader.readBytes())));
+
+        assertTrue(mTestFile.delete());
+        for (int i = 0; i < 3; i++) {
+            assertNull(mKernelCpuProcReader.readBytes());
+            SystemClock.sleep(50);
+        }
+        try (OutputStream os = Files.newOutputStream(mTestFile.toPath())) {
+            os.write(data);
+        }
+        assertNull(mKernelCpuProcReader.readBytes());
+    }
+
+    /**
+     * Tests reading functionality.
+     */
+    @Test
+    public void testSimpleRead() throws Exception {
+        final byte[] data = new byte[1024];
+        mRand.nextBytes(data);
+        try (OutputStream os = Files.newOutputStream(mTestFile.toPath())) {
+            os.write(data);
+        }
+        assertTrue(Arrays.equals(data, toArray(mKernelCpuProcReader.readBytes())));
+    }
+
+    /**
+     * Tests multiple reading functionality.
+     */
+    @Test
+    public void testMultipleRead() throws Exception {
+        mKernelCpuProcReader.setThrottleInterval(0);
+        for (int i = 0; i < 100; i++) {
+            final byte[] data = new byte[mRand.nextInt(102400) + 4];
+            mRand.nextBytes(data);
+            try (OutputStream os = Files.newOutputStream(mTestFile.toPath())) {
+                os.write(data);
+            }
+            assertTrue(Arrays.equals(data, toArray(mKernelCpuProcReader.readBytes())));
+            assertTrue(mTestFile.delete());
+        }
+    }
+
+    /**
+     * Tests reading with resizing.
+     */
+    @Test
+    public void testReadWithResize() throws Exception {
+        final byte[] data = new byte[128001];
+        mRand.nextBytes(data);
+        try (OutputStream os = Files.newOutputStream(mTestFile.toPath())) {
+            os.write(data);
+        }
+        assertTrue(Arrays.equals(data, toArray(mKernelCpuProcReader.readBytes())));
+    }
+
+    /**
+     * Tests that reading a file over the limit (1MB) will return null.
+     */
+    @Test
+    public void testReadOverLimit() throws Exception {
+        final byte[] data = new byte[1228800];
+        mRand.nextBytes(data);
+        try (OutputStream os = Files.newOutputStream(mTestFile.toPath())) {
+            os.write(data);
+        }
+        assertNull(mKernelCpuProcReader.readBytes());
+    }
+
+    /**
+     * Tests throttling. Deleting underlying file should not affect cache.
+     */
+    @Test
+    public void testThrottle() throws Exception {
+        mKernelCpuProcReader.setThrottleInterval(3000);
+        final byte[] data = new byte[20001];
+        mRand.nextBytes(data);
+        try (OutputStream os = Files.newOutputStream(mTestFile.toPath())) {
+            os.write(data);
+        }
+        assertTrue(Arrays.equals(data, toArray(mKernelCpuProcReader.readBytes())));
+        assertTrue(mTestFile.delete());
+        for (int i = 0; i < 5; i++) {
+            assertTrue(Arrays.equals(data, toArray(mKernelCpuProcReader.readBytes())));
+            SystemClock.sleep(10);
+        }
+        SystemClock.sleep(5000);
+        assertNull(mKernelCpuProcReader.readBytes());
+    }
+
+    private byte[] toArray(ByteBuffer buffer) {
+        assertNotNull(buffer);
+        byte[] arr = new byte[buffer.remaining()];
+        buffer.get(arr);
+        return arr;
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
index 1ac82bd..312af16 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
@@ -16,8 +16,6 @@
 
 package com.android.internal.os;
 
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
@@ -25,18 +23,15 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
-import java.io.BufferedReader;
-import java.util.Arrays;
-import java.util.List;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.util.Random;
 
 /**
@@ -44,75 +39,64 @@
  *
  * To run it:
  * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuActiveTimeReaderTest
- *
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class KernelUidCpuActiveTimeReaderTest {
-    @Mock private BufferedReader mBufferedReader;
-    @Mock private KernelUidCpuActiveTimeReader.Callback mCallback;
-
+    @Mock
+    private KernelCpuProcReader mProcReader;
+    @Mock
+    private KernelUidCpuActiveTimeReader.Callback mCallback;
     private KernelUidCpuActiveTimeReader mReader;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mReader = new KernelUidCpuActiveTimeReader();
-    }
-
-    public class Temp {
-
-        public void method() {
-            method1(new long[][]{{1,2,3}, {2,3,4}});
-            method1(new long[][]{{2,2,3}, {2,3,4}});
-        }
-        public int method1(long[][] array) {
-            return array.length * array[0].length;
-        }
+        mReader = new KernelUidCpuActiveTimeReader(mProcReader);
+        mReader.setThrottleInterval(0);
     }
 
     @Test
     public void testReadDelta() throws Exception {
         final int cores = 8;
-        final String info = "active: 8";
         final int[] uids = {1, 22, 333, 4444, 5555};
 
         final long[][] times = increaseTime(new long[uids.length][cores]);
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        for(int i=0;i<uids.length;i++){
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < uids.length; i++) {
             verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i]));
         }
         verifyNoMoreInteractions(mCallback);
 
         // Verify that a second call will only return deltas.
-        Mockito.reset(mCallback, mBufferedReader);
+        Mockito.reset(mCallback);
         final long[][] times1 = increaseTime(times);
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        for(int i=0;i<uids.length;i++){
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times1));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < uids.length; i++) {
             verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times1[i], times[i])));
         }
         verifyNoMoreInteractions(mCallback);
 
         // Verify that there won't be a callback if the proc file values didn't change.
-        Mockito.reset(mCallback, mBufferedReader);
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        Mockito.reset(mCallback);
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times1));
+        mReader.readDelta(mCallback);
         verifyNoMoreInteractions(mCallback);
 
         // Verify that calling with a null callback doesn't result in any crashes
-        Mockito.reset(mCallback, mBufferedReader);
+        Mockito.reset(mCallback);
         final long[][] times2 = increaseTime(times1);
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2));
-        mReader.readDeltaInternal(mBufferedReader, null);
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times2));
+        mReader.readDelta(null);
 
         // Verify that the readDelta call will only return deltas when
         // the previous call had null callback.
-        Mockito.reset(mCallback, mBufferedReader);
+        Mockito.reset(mCallback);
         final long[][] times3 = increaseTime(times2);
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times3));
+        mReader.readDelta(mCallback);
         for (int i = 0; i < uids.length; ++i) {
             verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times3[i], times2[i])));
         }
@@ -122,78 +106,76 @@
     @Test
     public void testReadDelta_malformedData() throws Exception {
         final int cores = 8;
-        final String info = "active: 8";
         final int[] uids = {1, 22, 333, 4444, 5555};
         final long[][] times = increaseTime(new long[uids.length][cores]);
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        for(int i=0;i<uids.length;i++){
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < uids.length; i++) {
             verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i]));
         }
         verifyNoMoreInteractions(mCallback);
 
-        // Verify that there is no callback if subsequent call provides wrong # of entries.
-        Mockito.reset(mCallback, mBufferedReader);
-        final long[][] temp = increaseTime(times);
-        final long[][] times1 = new long[uids.length][];
-        for(int i=0;i<temp.length;i++){
-            times1[i] = Arrays.copyOfRange(temp[i], 0, 6);
-        }
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        // Verify that there is no callback if subsequent call is in wrong format.
+        Mockito.reset(mCallback);
+        final long[][] times1 = increaseTime(times);
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times1).putInt(0, 5));
+        mReader.readDelta(mCallback);
         verifyNoMoreInteractions(mCallback);
 
         // Verify that the internal state was not modified if the given core count does not match
         // the following # of entries.
-        Mockito.reset(mCallback, mBufferedReader);
+        Mockito.reset(mCallback);
         final long[][] times2 = increaseTime(times);
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        for(int i=0;i<uids.length;i++){
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < uids.length; i++) {
             verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times2[i], times[i])));
         }
         verifyNoMoreInteractions(mCallback);
 
         // Verify that there is no callback if any value in the proc file is -ve.
-        Mockito.reset(mCallback, mBufferedReader);
+        Mockito.reset(mCallback);
         final long[][] times3 = increaseTime(times2);
         times3[uids.length - 1][cores - 1] *= -1;
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times3));
+        mReader.readDelta(mCallback);
         for (int i = 0; i < uids.length - 1; ++i) {
             verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times3[i], times2[i])));
         }
         verifyNoMoreInteractions(mCallback);
 
         // Verify that the internal state was not modified when the proc file had -ve value.
-        Mockito.reset(mCallback, mBufferedReader);
+        Mockito.reset(mCallback);
         for (int i = 0; i < cores; i++) {
-            times3[uids.length - 1][i] = times2[uids.length - 1][i] + uids[uids.length - 1] * 1000;
+            times3[uids.length - 1][i] = times2[uids.length - 1][i] + uids[uids.length - 1] * 2520;
         }
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1], getTotal(subtract(times3[uids.length - 1], times2[uids.length - 1])));
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times3));
+        mReader.readDelta(mCallback);
+        verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1],
+                getTotal(subtract(times3[uids.length - 1], times2[uids.length - 1])));
         verifyNoMoreInteractions(mCallback);
 
         // Verify that there is no callback if the values in the proc file are decreased.
-        Mockito.reset(mCallback, mBufferedReader);
+        Mockito.reset(mCallback);
         final long[][] times4 = increaseTime(times3);
-        times4[uids.length - 1][cores - 1] = times3[uids.length - 1][cores - 1] - 1;
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        System.arraycopy(times3[uids.length - 1], 0, times4[uids.length - 1], 0, cores);
+        times4[uids.length - 1][cores - 1] -= 100;
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times4));
+        mReader.readDelta(mCallback);
         for (int i = 0; i < uids.length - 1; ++i) {
             verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times4[i], times3[i])));
         }
         verifyNoMoreInteractions(mCallback);
 
         // Verify that the internal state was not modified when the proc file had decreased values.
-        Mockito.reset(mCallback, mBufferedReader);
+        Mockito.reset(mCallback);
         for (int i = 0; i < cores; i++) {
-            times4[uids.length - 1][i] = times3[uids.length - 1][i] + uids[uids.length - 1] * 1000;
+            times4[uids.length - 1][i] = times3[uids.length - 1][i] + uids[uids.length - 1] * 2520;
         }
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1], getTotal(subtract(times4[uids.length - 1], times3[uids.length - 1])));
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times4));
+        mReader.readDelta(mCallback);
+        verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1],
+                getTotal(subtract(times4[uids.length - 1], times3[uids.length - 1])));
         verifyNoMoreInteractions(mCallback);
     }
 
@@ -205,36 +187,50 @@
         return val;
     }
 
-    private String[] formatTime(int[] uids, long[][] times) {
-        String[] lines = new String[uids.length + 1];
-        for (int i=0;i<uids.length;i++){
-            StringBuilder sb = new StringBuilder();
-            sb.append(uids[i]).append(':');
-            for(int j=0;j<times[i].length;j++){
-                sb.append(' ').append(times[i][j]);
-            }
-            lines[i] = sb.toString();
-        }
-        lines[uids.length] = null;
-        return lines;
-    }
-
+    /**
+     * Unit of original and return value is 10ms. What's special about 2520? 2520 is LCM of 1, 2, 3,
+     * ..., 10. So that when wedivide shared cpu time by concurrent thread count, we always get a
+     * nice integer, avoiding rounding errors.
+     */
     private long[][] increaseTime(long[][] original) {
         long[][] newTime = new long[original.length][original[0].length];
         Random rand = new Random();
-        for(int i = 0;i<original.length;i++){
-            for(int j=0;j<original[0].length;j++){
-                newTime[i][j] = original[i][j] + rand.nextInt(1000_000) + 10000;
+        for (int i = 0; i < original.length; i++) {
+            for (int j = 0; j < original[0].length; j++) {
+                newTime[i][j] = original[i][j] + rand.nextInt(1000) * 2520 + 2520;
             }
         }
         return newTime;
     }
 
+    // Unit of times is 10ms
     private long getTotal(long[] times) {
         long sum = 0;
-        for(int i=0;i<times.length;i++){
-            sum+=times[i] * 10 / (i+1);
+        for (int i = 0; i < times.length; i++) {
+            sum += times[i] * 10 / (i + 1);
         }
         return sum;
     }
+
+    /**
+     * Format uids and times (in 10ms) into the following format:
+     * [n, uid0, time0a, time0b, ..., time0n,
+     * uid1, time1a, time1b, ..., time1n,
+     * uid2, time2a, time2b, ..., time2n, etc.]
+     * where n is the total number of cpus (num_possible_cpus)
+     */
+    private ByteBuffer getUidTimesBytes(int[] uids, long[][] times) {
+        int size = (1 + uids.length * (times[0].length + 1)) * 4;
+        ByteBuffer buf = ByteBuffer.allocate(size);
+        buf.order(ByteOrder.nativeOrder());
+        buf.putInt(times[0].length);
+        for (int i = 0; i < uids.length; i++) {
+            buf.putInt(uids[i]);
+            for (int j = 0; j < times[i].length; j++) {
+                buf.putInt((int) times[i][j]);
+            }
+        }
+        buf.flip();
+        return buf.order(ByteOrder.nativeOrder());
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
index 0d1f852..d21f541 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
@@ -16,26 +16,25 @@
 
 package com.android.internal.os;
 
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.mockito.Mockito.when;
 
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
 
-import static org.junit.Assert.*;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
-import java.io.BufferedReader;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.util.Arrays;
-import java.util.List;
 import java.util.Random;
 
 /**
@@ -43,147 +42,163 @@
  *
  * To run it:
  * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuClusterTimeReaderTest
- *
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class KernelUidCpuClusterTimeReaderTest {
-    @Mock private BufferedReader mBufferedReader;
-    @Mock private KernelUidCpuClusterTimeReader.Callback mCallback;
-
+    @Mock
+    private KernelCpuProcReader mProcReader;
     private KernelUidCpuClusterTimeReader mReader;
+    private VerifiableCallback mCallback;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mReader = new KernelUidCpuClusterTimeReader();
+        mReader = new KernelUidCpuClusterTimeReader(mProcReader);
+        mCallback = new VerifiableCallback();
+        mReader.setThrottleInterval(0);
     }
 
     @Test
     public void testReadDelta() throws Exception {
-        final String info = "policy0: 2 policy4: 4";
+        VerifiableCallback cb = new VerifiableCallback();
         final int cores = 6;
-        final int[] cluster = {2, 4};
+        final int[] clusters = {2, 4};
         final int[] uids = {1, 22, 333, 4444, 5555};
 
         // Verify initial call
         final long[][] times = increaseTime(new long[uids.length][cores]);
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        for (int i=0;i<uids.length;i++){
-            verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, times[i]));
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times));
+        mReader.readDelta(cb);
+        for (int i = 0; i < uids.length; i++) {
+            cb.verify(uids[i], getTotal(clusters, times[i]));
         }
+        cb.verifyNoMoreInteractions();
 
         // Verify that a second call will only return deltas.
-        Mockito.reset(mCallback, mBufferedReader);
+        cb.clear();
+        Mockito.reset(mProcReader);
         final long[][] times1 = increaseTime(times);
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        for (int i=0;i<uids.length;i++){
-            verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times1[i], times[i])));
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times1));
+        mReader.readDelta(cb);
+        for (int i = 0; i < uids.length; i++) {
+            cb.verify(uids[i], getTotal(clusters, subtract(times1[i], times[i])));
         }
+        cb.verifyNoMoreInteractions();
 
         // Verify that there won't be a callback if the proc file values didn't change.
-        Mockito.reset(mCallback, mBufferedReader);
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        verifyNoMoreInteractions(mCallback);
+        cb.clear();
+        Mockito.reset(mProcReader);
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times1));
+        mReader.readDelta(cb);
+        cb.verifyNoMoreInteractions();
 
         // Verify that calling with a null callback doesn't result in any crashes
-        Mockito.reset(mCallback, mBufferedReader);
+        Mockito.reset(mProcReader);
         final long[][] times2 = increaseTime(times1);
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2));
-        mReader.readDeltaInternal(mBufferedReader, null);
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times2));
+        mReader.readDelta(null);
 
         // Verify that the readDelta call will only return deltas when
         // the previous call had null callback.
-        Mockito.reset(mCallback, mBufferedReader);
+        cb.clear();
+        Mockito.reset(mProcReader);
         final long[][] times3 = increaseTime(times2);
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        for (int i=0;i<uids.length;i++){
-            verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times3[i], times2[i])));
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times3));
+        mReader.readDelta(cb);
+        for (int i = 0; i < uids.length; i++) {
+            cb.verify(uids[i], getTotal(clusters, subtract(times3[i], times2[i])));
         }
+        cb.verifyNoMoreInteractions();
 
     }
 
     @Test
     public void testReadDelta_malformedData() throws Exception {
-        final String info = "policy0: 2 policy4: 4";
         final int cores = 6;
-        final int[] cluster = {2, 4};
+        final int[] clusters = {2, 4};
         final int[] uids = {1, 22, 333, 4444, 5555};
 
         // Verify initial call
         final long[][] times = increaseTime(new long[uids.length][cores]);
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        for (int i=0;i<uids.length;i++){
-            verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, times[i]));
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < uids.length; i++) {
+            mCallback.verify(uids[i], getTotal(clusters, times[i]));
         }
+        mCallback.verifyNoMoreInteractions();
 
-        // Verify that there is no callback if subsequent call provides wrong # of entries.
-        Mockito.reset(mCallback, mBufferedReader);
+        // Verify that there is no callback if a call has wrong format
+        mCallback.clear();
+        Mockito.reset(mProcReader);
         final long[][] temp = increaseTime(times);
         final long[][] times1 = new long[uids.length][];
-        for(int i=0;i<temp.length;i++){
+        for (int i = 0; i < temp.length; i++) {
             times1[i] = Arrays.copyOfRange(temp[i], 0, 4);
         }
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        verifyNoMoreInteractions(mCallback);
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times1));
+        mReader.readDelta(mCallback);
+        mCallback.verifyNoMoreInteractions();
 
         // Verify that the internal state was not modified if the given core count does not match
         // the following # of entries.
-        Mockito.reset(mCallback, mBufferedReader);
+        mCallback.clear();
+        Mockito.reset(mProcReader);
         final long[][] times2 = increaseTime(times);
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        for (int i=0;i<uids.length;i++){
-            verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times2[i], times[i])));
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times2));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < uids.length; i++) {
+            mCallback.verify(uids[i], getTotal(clusters, subtract(times2[i], times[i])));
         }
+        mCallback.verifyNoMoreInteractions();
 
         // Verify that there is no callback if any value in the proc file is -ve.
-        Mockito.reset(mCallback, mBufferedReader);
+        mCallback.clear();
+        Mockito.reset(mProcReader);
         final long[][] times3 = increaseTime(times2);
         times3[uids.length - 1][cores - 1] *= -1;
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        for (int i=0;i<uids.length-1;i++){
-            verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times3[i], times2[i])));
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times3));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < uids.length - 1; i++) {
+            mCallback.verify(uids[i], getTotal(clusters, subtract(times3[i], times2[i])));
         }
-        verifyNoMoreInteractions(mCallback);
+        mCallback.verifyNoMoreInteractions();
 
         // Verify that the internal state was not modified when the proc file had -ve value.
-        Mockito.reset(mCallback, mBufferedReader);
-        for(int i=0;i<cores;i++){
-            times3[uids.length -1][i] = times2[uids.length -1][i] + uids[uids.length -1]*1000;
+        mCallback.clear();
+        Mockito.reset(mProcReader);
+        for (int i = 0; i < cores; i++) {
+            times3[uids.length - 1][i] = times2[uids.length - 1][i] + uids[uids.length - 1] * 2520;
         }
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        verify(mCallback, times(1)).onUidCpuPolicyTime(uids[uids.length-1], getTotal(cluster, subtract(times3[uids.length -1], times2[uids.length -1])));
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times3));
+        mReader.readDelta(mCallback);
+        mCallback.verify(uids[uids.length - 1],
+                getTotal(clusters, subtract(times3[uids.length - 1], times2[uids.length - 1])));
 
-        // // Verify that there is no callback if the values in the proc file are decreased.
-        Mockito.reset(mCallback, mBufferedReader);
+        // Verify that there is no callback if the values in the proc file are decreased.
+        mCallback.clear();
+        Mockito.reset(mProcReader);
         final long[][] times4 = increaseTime(times3);
-        times4[uids.length - 1][cores - 1] = times3[uids.length - 1][cores - 1] - 1;
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        for (int i=0;i<uids.length-1;i++){
-            verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times4[i], times3[i])));
+        System.arraycopy(times3[uids.length - 1], 0, times4[uids.length - 1], 0, cores);
+        times4[uids.length - 1][cores - 1] -= 100;
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times4));
+        mReader.readDelta(mCallback);
+        for (int i = 0; i < uids.length - 1; i++) {
+            mCallback.verify(uids[i], getTotal(clusters, subtract(times4[i], times3[i])));
         }
-        verifyNoMoreInteractions(mCallback);
+        mCallback.verifyNoMoreInteractions();
 
         // Verify that the internal state was not modified when the proc file had decreased values.
-        Mockito.reset(mCallback, mBufferedReader);
-        for(int i=0;i<cores;i++){
-            times4[uids.length -1][i] = times3[uids.length -1][i] + uids[uids.length -1]*1000;
+        mCallback.clear();
+        Mockito.reset(mProcReader);
+        for (int i = 0; i < cores; i++) {
+            times4[uids.length - 1][i] = times3[uids.length - 1][i] + uids[uids.length - 1] * 2520;
         }
-        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4));
-        mReader.readDeltaInternal(mBufferedReader, mCallback);
-        verify(mCallback, times(1))
-                .onUidCpuPolicyTime(uids[uids.length-1], getTotal(cluster, subtract(times3[uids.length -1], times2[uids.length -1])));
-
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, clusters, times4));
+        mReader.readDelta(mCallback);
+        mCallback.verify(uids[uids.length - 1],
+                getTotal(clusters, subtract(times3[uids.length - 1], times2[uids.length - 1])));
+        mCallback.verifyNoMoreInteractions();
     }
 
 
@@ -195,26 +210,17 @@
         return val;
     }
 
-    private String[] formatTime(int[] uids, long[][] times) {
-        String[] lines = new String[uids.length + 1];
-        for (int i=0;i<uids.length;i++){
-            StringBuilder sb = new StringBuilder();
-            sb.append(uids[i]).append(':');
-            for(int j=0;j<times[i].length;j++){
-                sb.append(' ').append(times[i][j]);
-            }
-            lines[i] = sb.toString();
-        }
-        lines[uids.length] = null;
-        return lines;
-    }
-
+    /**
+     * Unit is 10ms. What's special about 2520? 2520 is LCM of 1, 2, 3, ..., 10. So that when we
+     * divide shared cpu time by concurrent thread count, we always get a nice integer, avoiding
+     * rounding errors.
+     */
     private long[][] increaseTime(long[][] original) {
         long[][] newTime = new long[original.length][original[0].length];
         Random rand = new Random();
-        for(int i = 0;i<original.length;i++){
-            for(int j=0;j<original[0].length;j++){
-                newTime[i][j] = original[i][j] + rand.nextInt(1000_000) + 10000;
+        for (int i = 0; i < original.length; i++) {
+            for (int j = 0; j < original[0].length; j++) {
+                newTime[i][j] = original[i][j] + rand.nextInt(1000) * 2520 + 2520;
             }
         }
         return newTime;
@@ -223,23 +229,69 @@
     // Format an array of cluster times according to the algorithm in KernelUidCpuClusterTimeReader
     private long[] getTotal(int[] cluster, long[] times) {
         int core = 0;
-        long[] sum = new long[cluster.length];
-        for(int i=0;i<cluster.length;i++){
-            for(int j=0;j<cluster[i];j++){
-                sum[i] += times[core++] * 10 / (j+1);
+        long[] sumTimes = new long[cluster.length];
+        for (int i = 0; i < cluster.length; i++) {
+            double sum = 0;
+            for (int j = 0; j < cluster[i]; j++) {
+                sum += (double) times[core++] * 10 / (j + 1);
             }
+            sumTimes[i] = (long) sum;
         }
-        return sum;
+        return sumTimes;
     }
 
-    // Compare array1 against flattened 2d array array2 element by element
-    private boolean testEqual(long[] array1, long[][] array2) {
-        int k=0;
-        for(int i=0;i<array2.length;i++){
-            for(int j=0;j<array2[i].length;j++){
-                if (k >= array1.length || array1[k++]!=array2[i][j])return false;
+    private class VerifiableCallback implements KernelUidCpuClusterTimeReader.Callback {
+
+        SparseArray<long[]> mData = new SparseArray<>();
+        int count = 0;
+
+        public void verify(int uid, long[] cpuClusterTimeMs) {
+            long[] array = mData.get(uid);
+            assertNotNull(array);
+            assertArrayEquals(cpuClusterTimeMs, array);
+            count++;
+        }
+
+        public void clear() {
+            mData.clear();
+            count = 0;
+        }
+
+        @Override
+        public void onUidCpuPolicyTime(int uid, long[] cpuClusterTimeMs) {
+            long[] array = new long[cpuClusterTimeMs.length];
+            System.arraycopy(cpuClusterTimeMs, 0, array, 0, array.length);
+            mData.put(uid, array);
+        }
+
+        public void verifyNoMoreInteractions() {
+            assertEquals(mData.size(), count);
+        }
+    }
+
+    /**
+     * Format uids and times (in 10ms) into the following format:
+     * [n, x0, ..., xn, uid0, time0a, time0b, ..., time0n,
+     * uid1, time1a, time1b, ..., time1n,
+     * uid2, time2a, time2b, ..., time2n, etc.]
+     * where n is the number of policies
+     * xi is the number cpus on a particular policy
+     */
+    private ByteBuffer getUidTimesBytes(int[] uids, int[] clusters, long[][] times) {
+        int size = (1 + clusters.length + uids.length * (times[0].length + 1)) * 4;
+        ByteBuffer buf = ByteBuffer.allocate(size);
+        buf.order(ByteOrder.nativeOrder());
+        buf.putInt(clusters.length);
+        for (int i = 0; i < clusters.length; i++) {
+            buf.putInt(clusters[i]);
+        }
+        for (int i = 0; i < uids.length; i++) {
+            buf.putInt(uids[i]);
+            for (int j = 0; j < times[i].length; j++) {
+                buf.putInt((int) (times[i][j]));
             }
         }
-        return k == array1.length;
+        buf.flip();
+        return buf.order(ByteOrder.nativeOrder());
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
index 95b0b29..0950721 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
@@ -19,15 +19,15 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -37,6 +37,8 @@
 import org.mockito.MockitoAnnotations;
 
 import java.io.BufferedReader;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.util.Arrays;
 
 /**
@@ -50,9 +52,9 @@
  *
  * Build: m FrameworksCoreTests
  * Install: adb install -r \
- *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
+ * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
  * Run: adb shell am instrument -e class com.android.internal.os.KernelUidCpuFreqTimeReaderTest -w \
- *     com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
  *
  * or
  *
@@ -61,16 +63,22 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class KernelUidCpuFreqTimeReaderTest {
-    @Mock private BufferedReader mBufferedReader;
-    @Mock private KernelUidCpuFreqTimeReader.Callback mCallback;
-    @Mock private PowerProfile mPowerProfile;
+    @Mock
+    private BufferedReader mBufferedReader;
+    @Mock
+    private KernelUidCpuFreqTimeReader.Callback mCallback;
+    @Mock
+    private PowerProfile mPowerProfile;
+    @Mock
+    private KernelCpuProcReader mProcReader;
 
     private KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mKernelUidCpuFreqTimeReader = new KernelUidCpuFreqTimeReader();
+        mKernelUidCpuFreqTimeReader = new KernelUidCpuFreqTimeReader(mProcReader);
+        mKernelUidCpuFreqTimeReader.setThrottleInterval(0);
     }
 
     @Test
@@ -154,7 +162,7 @@
                 .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, times));
         mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
         for (int i = 0; i < uids.length; ++i) {
-            verify(mCallback).onUidCpuFreqTime(uids[i], times[i]);
+            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i], times[i]);
         }
         verifyNoMoreInteractions(mCallback);
 
@@ -170,7 +178,7 @@
                 .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes1));
         mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
         for (int i = 0; i < uids.length; ++i) {
-            verify(mCallback).onUidCpuFreqTime(uids[i], subtract(newTimes1[i], times[i]));
+            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i], subtract(newTimes1[i], times[i]));
         }
         verifyNoMoreInteractions(mCallback);
 
@@ -206,12 +214,89 @@
                 .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes3));
         mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
         for (int i = 0; i < uids.length; ++i) {
-            verify(mCallback).onUidCpuFreqTime(uids[i], subtract(newTimes3[i], newTimes2[i]));
+            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i],
+                    subtract(newTimes3[i], newTimes2[i]));
         }
         verifyNoMoreInteractions(mCallback);
     }
 
     @Test
+    public void testReadDelta_Binary() throws Exception {
+        VerifiableCallback cb = new VerifiableCallback();
+        final long[] freqs = {110, 123, 145, 167, 289, 997};
+        final int[] uids = {1, 22, 333, 444, 555};
+        final long[][] times = new long[uids.length][freqs.length];
+        for (int i = 0; i < uids.length; ++i) {
+            for (int j = 0; j < freqs.length; ++j) {
+                times[i][j] = uids[i] * freqs[j] * 10;
+            }
+        }
+        when(mBufferedReader.readLine()).thenReturn(getFreqsLine(freqs));
+        long[] actualFreqs = mKernelUidCpuFreqTimeReader.readFreqs(mBufferedReader, mPowerProfile);
+
+        assertArrayEquals(freqs, actualFreqs);
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times));
+        mKernelUidCpuFreqTimeReader.readDeltaBinary(cb);
+        for (int i = 0; i < uids.length; ++i) {
+            cb.verify(uids[i], times[i]);
+        }
+        cb.verifyNoMoreInteractions();
+
+        // Verify that a second call will only return deltas.
+        cb.clear();
+        Mockito.reset(mProcReader);
+        final long[][] newTimes1 = new long[uids.length][freqs.length];
+        for (int i = 0; i < uids.length; ++i) {
+            for (int j = 0; j < freqs.length; ++j) {
+                newTimes1[i][j] = times[i][j] + (uids[i] + freqs[j]) * 50;
+            }
+        }
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes1));
+        mKernelUidCpuFreqTimeReader.readDeltaBinary(cb);
+        for (int i = 0; i < uids.length; ++i) {
+            cb.verify(uids[i], subtract(newTimes1[i], times[i]));
+        }
+        cb.verifyNoMoreInteractions();
+
+        // Verify that there won't be a callback if the proc file values didn't change.
+        cb.clear();
+        Mockito.reset(mProcReader);
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes1));
+        mKernelUidCpuFreqTimeReader.readDeltaBinary(cb);
+        cb.verifyNoMoreInteractions();
+
+        // Verify that calling with a null callback doesn't result in any crashes
+        cb.clear();
+        Mockito.reset(mProcReader);
+        final long[][] newTimes2 = new long[uids.length][freqs.length];
+        for (int i = 0; i < uids.length; ++i) {
+            for (int j = 0; j < freqs.length; ++j) {
+                newTimes2[i][j] = newTimes1[i][j] + (uids[i] * freqs[j]) * 30;
+            }
+        }
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes2));
+        mKernelUidCpuFreqTimeReader.readDeltaBinary(null);
+        cb.verifyNoMoreInteractions();
+
+        // Verify that the readDelta call will only return deltas when
+        // the previous call had null callback.
+        cb.clear();
+        Mockito.reset(mProcReader);
+        final long[][] newTimes3 = new long[uids.length][freqs.length];
+        for (int i = 0; i < uids.length; ++i) {
+            for (int j = 0; j < freqs.length; ++j) {
+                newTimes3[i][j] = newTimes2[i][j] + (uids[i] + freqs[j]) * 40;
+            }
+        }
+        when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes3));
+        mKernelUidCpuFreqTimeReader.readDeltaBinary(cb);
+        for (int i = 0; i < uids.length; ++i) {
+            cb.verify(uids[i], subtract(newTimes3[i], newTimes2[i]));
+        }
+        cb.verifyNoMoreInteractions();
+    }
+
+    @Test
     public void testReadDelta_malformedData() throws Exception {
         final long[] freqs = {1, 12, 123, 1234, 12345, 123456};
         final int[] uids = {1, 22, 333, 4444, 5555};
@@ -229,7 +314,7 @@
                 .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, times));
         mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
         for (int i = 0; i < uids.length; ++i) {
-            verify(mCallback).onUidCpuFreqTime(uids[i], times[i]);
+            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i], times[i]);
         }
         verifyNoMoreInteractions(mCallback);
 
@@ -249,7 +334,7 @@
             if (i == uids.length - 1) {
                 continue;
             }
-            verify(mCallback).onUidCpuFreqTime(uids[i], subtract(newTimes1[i], times[i]));
+            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i], subtract(newTimes1[i], times[i]));
         }
         verifyNoMoreInteractions(mCallback);
 
@@ -280,7 +365,8 @@
             if (i == uids.length - 1) {
                 continue;
             }
-            verify(mCallback).onUidCpuFreqTime(uids[i], subtract(newTimes2[i], newTimes1[i]));
+            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i],
+                    subtract(newTimes2[i], newTimes1[i]));
         }
         verifyNoMoreInteractions(mCallback);
 
@@ -327,6 +413,21 @@
         return lines;
     }
 
+    private ByteBuffer getUidTimesBytes(int[] uids, long[][] times) {
+        int size = (1 + uids.length + uids.length * times[0].length) * 4;
+        ByteBuffer buf = ByteBuffer.allocate(size);
+        buf.order(ByteOrder.nativeOrder());
+        buf.putInt(times[0].length);
+        for (int i = 0; i < uids.length; i++) {
+            buf.putInt(uids[i]);
+            for (int j = 0; j < times[i].length; j++) {
+                buf.putInt((int) (times[i][j] / 10));
+            }
+        }
+        buf.flip();
+        return buf.asReadOnlyBuffer().order(ByteOrder.nativeOrder());
+    }
+
     private void setCpuClusterFreqs(int numClusters, int... clusterFreqs) {
         assertEquals(numClusters, clusterFreqs.length);
         when(mPowerProfile.getNumCpuClusters()).thenReturn(numClusters);
@@ -334,4 +435,33 @@
             when(mPowerProfile.getNumSpeedStepsInCpuCluster(i)).thenReturn(clusterFreqs[i]);
         }
     }
+
+    private class VerifiableCallback implements KernelUidCpuFreqTimeReader.Callback {
+
+        SparseArray<long[]> mData = new SparseArray<>();
+        int count = 0;
+
+        public void verify(int uid, long[] cpuFreqTimeMs) {
+            long[] array = mData.get(uid);
+            assertNotNull(array);
+            assertArrayEquals(cpuFreqTimeMs, array);
+            count++;
+        }
+
+        public void clear() {
+            mData.clear();
+            count = 0;
+        }
+
+        @Override
+        public void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs) {
+            long[] array = new long[cpuFreqTimeMs.length];
+            System.arraycopy(cpuFreqTimeMs, 0, array, 0, array.length);
+            mData.put(uid, array);
+        }
+
+        public void verifyNoMoreInteractions() {
+            assertEquals(mData.size(), count);
+        }
+    }
 }
diff --git a/data/etc/Android.mk b/data/etc/Android.mk
index b2c6840..936ad22 100644
--- a/data/etc/Android.mk
+++ b/data/etc/Android.mk
@@ -39,3 +39,11 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 include $(BUILD_PREBUILT)
+
+########################
+include $(CLEAR_VARS)
+LOCAL_MODULE := hiddenapi-package-whitelist.xml
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/sysconfig
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
new file mode 100644
index 0000000..54d8a23
--- /dev/null
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<!--
+This XML file declares which system apps should be exempted from the hidden API blacklisting, i.e.
+which apps should be allowed to access the entire private API.
+-->
+
+<config>
+  <hidden-api-whitelisted-app package="com.android.providers.contacts" />
+</config>
+
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 9d1fdbd..8e76dd3 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -158,6 +158,7 @@
         <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
         <permission name="android.permission.MANAGE_USERS"/>
         <permission name="android.permission.MODIFY_PHONE_STATE"/>
+        <permission name="android.permission.PACKAGE_USAGE_STATS"/>
         <permission name="android.permission.PERFORM_CDMA_PROVISIONING"/>
         <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/graphics/java/android/view/PixelCopy.java b/graphics/java/android/view/PixelCopy.java
index a14609f..2797a4d 100644
--- a/graphics/java/android/view/PixelCopy.java
+++ b/graphics/java/android/view/PixelCopy.java
@@ -263,8 +263,16 @@
                     "Only able to copy windows with decor views");
         }
         Surface surface = null;
-        if (source.peekDecorView().getViewRootImpl() != null) {
-            surface = source.peekDecorView().getViewRootImpl().mSurface;
+        final ViewRootImpl root = source.peekDecorView().getViewRootImpl();
+        if (root != null) {
+            surface = root.mSurface;
+            final Rect surfaceInsets = root.mWindowAttributes.surfaceInsets;
+            if (srcRect == null) {
+                srcRect = new Rect(surfaceInsets.left, surfaceInsets.top,
+                        root.mWidth + surfaceInsets.left, root.mHeight + surfaceInsets.top);
+            } else {
+                srcRect.offset(surfaceInsets.left, surfaceInsets.top);
+            }
         }
         if (surface == null || !surface.isValid()) {
             throw new IllegalArgumentException(
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 3d879f5..86dfc9c 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -123,6 +123,10 @@
      * A device type describing a USB audio headset.
      */
     public static final int TYPE_USB_HEADSET       = 22;
+    /**
+     * A device type describing a Hearing Aid.
+     */
+    public static final int TYPE_HEARING_AID   = 23;
 
     /** @hide */
     @IntDef(flag = false, prefix = "TYPE", value = {
@@ -144,7 +148,8 @@
             TYPE_FM,
             TYPE_AUX_LINE,
             TYPE_IP,
-            TYPE_BUS }
+            TYPE_BUS,
+            TYPE_HEARING_AID }
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface AudioDeviceTypeOut {}
@@ -171,6 +176,7 @@
             case TYPE_AUX_LINE:
             case TYPE_IP:
             case TYPE_BUS:
+            case TYPE_HEARING_AID:
                 return true;
             default:
                 return false;
@@ -367,6 +373,7 @@
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_AUX_LINE, TYPE_AUX_LINE);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_IP, TYPE_IP);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_BUS, TYPE_BUS);
+        INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_OUT_HEARING_AID, TYPE_HEARING_AID);
 
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BUILTIN_MIC, TYPE_BUILTIN_MIC);
         INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET, TYPE_BLUETOOTH_SCO);
@@ -415,6 +422,7 @@
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_AUX_LINE, AudioSystem.DEVICE_OUT_AUX_LINE);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_IP, AudioSystem.DEVICE_OUT_IP);
         EXT_TO_INT_DEVICE_MAPPING.put(TYPE_BUS, AudioSystem.DEVICE_OUT_BUS);
+        EXT_TO_INT_DEVICE_MAPPING.put(TYPE_HEARING_AID, AudioSystem.DEVICE_OUT_HEARING_AID);
     }
 }
 
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index be9fcb8..3885f90 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -401,6 +401,7 @@
     public static final int DEVICE_OUT_BUS = 0x1000000;
     public static final int DEVICE_OUT_PROXY = 0x2000000;
     public static final int DEVICE_OUT_USB_HEADSET = 0x4000000;
+    public static final int DEVICE_OUT_HEARING_AID = 0x8000000;
 
     public static final int DEVICE_OUT_DEFAULT = DEVICE_BIT_DEFAULT;
 
@@ -431,6 +432,7 @@
                                               DEVICE_OUT_BUS |
                                               DEVICE_OUT_PROXY |
                                               DEVICE_OUT_USB_HEADSET |
+                                              DEVICE_OUT_HEARING_AID |
                                               DEVICE_OUT_DEFAULT);
     public static final int DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP |
                                                    DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
@@ -546,6 +548,7 @@
     public static final String DEVICE_OUT_BUS_NAME = "bus";
     public static final String DEVICE_OUT_PROXY_NAME = "proxy";
     public static final String DEVICE_OUT_USB_HEADSET_NAME = "usb_headset";
+    public static final String DEVICE_OUT_HEARING_AID_NAME = "hearing_aid_out";
 
     public static final String DEVICE_IN_COMMUNICATION_NAME = "communication";
     public static final String DEVICE_IN_AMBIENT_NAME = "ambient";
@@ -628,6 +631,8 @@
             return DEVICE_OUT_PROXY_NAME;
         case DEVICE_OUT_USB_HEADSET:
             return DEVICE_OUT_USB_HEADSET_NAME;
+        case DEVICE_OUT_HEARING_AID:
+            return DEVICE_OUT_HEARING_AID_NAME;
         case DEVICE_OUT_DEFAULT:
         default:
             return Integer.toString(device);
diff --git a/media/java/android/media/MediaBrowser2.java b/media/java/android/media/MediaBrowser2.java
index 231a4a5..e1d078d 100644
--- a/media/java/android/media/MediaBrowser2.java
+++ b/media/java/android/media/MediaBrowser2.java
@@ -20,6 +20,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.media.MediaLibraryService2.MediaLibrarySession;
+import android.media.MediaSession2.ControllerInfo;
 import android.media.update.ApiLoader;
 import android.media.update.MediaBrowser2Provider;
 import android.os.Bundle;
@@ -54,11 +56,18 @@
 
         /**
          * Called when there's change in the parent's children.
+         * <p>
+         * This API is called when the library service called
+         * {@link MediaLibrarySession#notifyChildrenChanged(ControllerInfo, String, int, Bundle)} or
+         * {@link MediaLibrarySession#notifyChildrenChanged(String, int, Bundle)} for the parent.
          *
          * @param parentId parent id that you've specified with {@link #subscribe(String, Bundle)}
-         * @param extras extra bundle that you've specified with {@link #subscribe(String, Bundle)}
+         * @param childCount number of children
+         * @param extras extra bundle from the library service. Can be differ from extras that
+         *               you've specified with {@link #subscribe(String, Bundle)}.
          */
-        public void onChildrenChanged(@NonNull String parentId, @Nullable Bundle extras) { }
+        public void onChildrenChanged(@NonNull String parentId, int childCount,
+                @Nullable Bundle extras) { }
 
         /**
          * Called when the list of items has been returned by the library service for the previous
@@ -69,12 +78,11 @@
          *             {@link #getChildren(String, int, int, Bundle)}
          * @param pageSize page size that you've specified with
          *                 {@link #getChildren(String, int, int, Bundle)}
-         * @param extras extra bundle that you've specified with
-         *                {@link #getChildren(String, int, int, Bundle)}
          * @param result result. Can be {@code null}
+         * @param extras extra bundle from the library service
          */
         public void onChildrenLoaded(@NonNull String parentId, int page, int pageSize,
-                @Nullable Bundle extras, @Nullable List<MediaItem2> result) { }
+                @Nullable List<MediaItem2> result, @Nullable Bundle extras) { }
 
         /**
          * Called when the item has been returned by the library service for the previous
@@ -92,11 +100,11 @@
          * {@link MediaBrowser2#search(String, Bundle)}.
          *
          * @param query search query that you've specified with {@link #search(String, Bundle)}
-         * @param extras extra bundle
          * @param itemCount The item count for the search result
+         * @param extras extra bundle from the library service
          */
-        public void onSearchResultChanged(@NonNull String query, @Nullable Bundle extras,
-                int itemCount) { }
+        public void onSearchResultChanged(@NonNull String query, int itemCount,
+                @Nullable Bundle extras) { }
 
         /**
          * Called when the search result has been returned by the library service for the previous
@@ -110,12 +118,11 @@
          *             {@link #getSearchResult(String, int, int, Bundle)}
          * @param pageSize page size that you've specified with
          *                 {@link #getSearchResult(String, int, int, Bundle)}
-         * @param extras extra bundle that you've specified with
-         *                {@link #getSearchResult(String, int, int, Bundle)}
          * @param result result. Can be {@code null}.
+         * @param extras extra bundle from the library service
          */
         public void onSearchResultLoaded(@NonNull String query, int page, int pageSize,
-                @Nullable Bundle extras, @Nullable List<MediaItem2> result) { }
+                @Nullable List<MediaItem2> result, @Nullable Bundle extras) { }
     }
 
     public MediaBrowser2(@NonNull Context context, @NonNull SessionToken2 token,
@@ -144,7 +151,7 @@
 
     /**
      * Subscribe to a parent id for the change in its children. When there's a change,
-     * {@link BrowserCallback#onChildrenChanged(String, Bundle)} will be called with the bundle
+     * {@link BrowserCallback#onChildrenChanged(String, int, Bundle)} will be called with the bundle
      * that you've specified. You should call {@link #getChildren(String, int, int, Bundle)} to get
      * the actual contents for the parent.
      *
@@ -158,17 +165,19 @@
     /**
      * Unsubscribe for changes to the children of the parent, which was previously subscribed with
      * {@link #subscribe(String, Bundle)}.
+     * <p>
+     * This unsubscribes all previous subscription with the parent id, regardless of the extra
+     * that was previously sent to the library service.
      *
      * @param parentId parent id
-     * @param extras extra bundle
      */
-    public void unsubscribe(String parentId, @Nullable Bundle extras) {
-        mProvider.unsubscribe_impl(parentId, extras);
+    public void unsubscribe(String parentId) {
+        mProvider.unsubscribe_impl(parentId);
     }
 
     /**
      * Get list of children under the parent. Result would be sent back asynchronously with the
-     * {@link BrowserCallback#onChildrenLoaded(String, int, int, Bundle, List)}.
+     * {@link BrowserCallback#onChildrenLoaded(String, int, int, List, Bundle)}.
      *
      * @param parentId parent id for getting the children.
      * @param page page number to get the result. Starts from {@code 1}
@@ -191,7 +200,7 @@
 
     /**
      * Send a search request to the library service. When the search result is changed,
-     * {@link BrowserCallback#onSearchResultChanged(String, Bundle, int)} will be called. You should
+     * {@link BrowserCallback#onSearchResultChanged(String, int, Bundle)} will be called. You should
      * call {@link #getSearchResult(String, int, int, Bundle)} to get the actual search result.
      *
      * @param query search query. Should not be an empty string.
@@ -203,7 +212,7 @@
 
     /**
      * Get the search result from lhe library service. Result would be sent back asynchronously with
-     * the {@link BrowserCallback#onSearchResultLoaded(String, int, int, Bundle, List)}.
+     * the {@link BrowserCallback#onSearchResultLoaded(String, int, int, List, Bundle)}.
      *
      * @param query search query that you've specified with {@link #search(String, Bundle)}
      * @param page page number to get search result. Starts from {@code 1}
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
index bfd1fd4..6682e08 100644
--- a/media/java/android/media/MediaController2.java
+++ b/media/java/android/media/MediaController2.java
@@ -521,24 +521,6 @@
     }
 
     /**
-     * Get the rating type supported by the session. One of:
-     * <ul>
-     * <li>{@link Rating2#RATING_NONE}</li>
-     * <li>{@link Rating2#RATING_HEART}</li>
-     * <li>{@link Rating2#RATING_THUMB_UP_DOWN}</li>
-     * <li>{@link Rating2#RATING_3_STARS}</li>
-     * <li>{@link Rating2#RATING_4_STARS}</li>
-     * <li>{@link Rating2#RATING_5_STARS}</li>
-     * <li>{@link Rating2#RATING_PERCENTAGE}</li>
-     * </ul>
-     *
-     * @return The supported rating type
-     */
-    public int getRatingType() {
-        return mProvider.getRatingType_impl();
-    }
-
-    /**
      * Get an intent for launching UI associated with this session if one exists.
      *
      * @return A {@link PendingIntent} to launch UI or null.
@@ -571,7 +553,12 @@
 
     /**
      * Rate the media. This will cause the rating to be set for the current user.
-     * The Rating type must match the type returned by {@link #getRatingType()}.
+     * The rating style must follow the user rating style from the session.
+     * You can get the rating style from the session through the
+     * {@link MediaMetadata#getRating(String)} with the key
+     * {@link MediaMetadata#METADATA_KEY_USER_RATING}.
+     * <p>
+     * If the user rating was {@code null}, the media item does not accept setting user rating.
      *
      * @param mediaId The id of the media
      * @param rating The rating to set
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
index 95d83ee..ec37d0e 100644
--- a/media/java/android/media/MediaLibraryService2.java
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -78,26 +78,36 @@
         }
 
         /**
-         * Notify subscribed controller about change in a parent's children.
+         * Notify the controller of the change in a parent's children.
+         * <p>
+         * If the controller hasn't subscribed to the parent, the API will do nothing.
+         * <p>
+         * Controllers will use {@link MediaBrowser2#getChildren(String, int, int, Bundle)} to get
+         * the list of children.
          *
          * @param controller controller to notify
-         * @param parentId
-         * @param extras
+         * @param parentId parent id with changes in its children
+         * @param childCount number of children.
+         * @param extras extra information from session to controller
          */
         public void notifyChildrenChanged(@NonNull ControllerInfo controller,
-                @NonNull String parentId, @NonNull Bundle extras) {
-            mProvider.notifyChildrenChanged_impl(controller, parentId, extras);
+                @NonNull String parentId, int childCount, @Nullable Bundle extras) {
+            mProvider.notifyChildrenChanged_impl(controller, parentId, childCount, extras);
         }
 
         /**
-         * Notify subscribed controller about change in a parent's children.
+         * Notify all controllers that subscribed to the parent about change in the parent's
+         * children, regardless of the extra bundle supplied by
+         * {@link MediaBrowser2#subscribe(String, Bundle)}.
          *
          * @param parentId parent id
-         * @param extras extra bundle
+         * @param childCount number of children
+         * @param extras extra information from session to controller
          */
         // This is for the backward compatibility.
-        public void notifyChildrenChanged(@NonNull String parentId, @Nullable Bundle extras) {
-            mProvider.notifyChildrenChanged_impl(parentId, extras);
+        public void notifyChildrenChanged(@NonNull String parentId, int childCount,
+                @Nullable Bundle extras) {
+            mProvider.notifyChildrenChanged_impl(parentId, childCount, extras);
         }
 
         /**
@@ -105,12 +115,12 @@
          *
          * @param controller controller to notify
          * @param query previously sent search query from the controller.
-         * @param extras extra bundle
          * @param itemCount the number of items that have been found in the search.
+         * @param extras extra bundle
          */
         public void notifySearchResultChanged(@NonNull ControllerInfo controller,
-                @NonNull String query, @NonNull Bundle extras, int itemCount) {
-            mProvider.notifySearchResultChanged_impl(controller, query, extras, itemCount);
+                @NonNull String query, int itemCount, @NonNull Bundle extras) {
+            mProvider.notifySearchResultChanged_impl(controller, query, itemCount, extras);
         }
     }
 
@@ -176,25 +186,27 @@
         }
 
         /**
-         * Called when a controller subscribes to the parent.
+         * Called when a controller subscribed to the parent.
+         * <p>
+         * It's your responsibility to keep subscriptions by your own and call
+         * {@link MediaLibrarySession#notifyChildrenChanged(ControllerInfo, String, Bundle)} when
+         * the parent is changed.
          *
          * @param controller controller
          * @param parentId parent id
          * @param extras extra bundle
          */
-        public void onSubscribed(@NonNull ControllerInfo controller, String parentId,
+        public void onSubscribed(@NonNull ControllerInfo controller, @NonNull String parentId,
                 @Nullable Bundle extras) {
         }
 
         /**
-         * Called when a controller unsubscribes to the parent.
+         * Called when a controller unsubscribed to the parent.
          *
          * @param controller controller
          * @param parentId parent id
-         * @param extras extra bundle
          */
-        public void onUnsubscribed(@NonNull ControllerInfo controller, String parentId,
-                @Nullable Bundle extras) {
+        public void onUnsubscribed(@NonNull ControllerInfo controller, @NonNull String parentId) {
         }
 
         /**
@@ -206,7 +218,6 @@
          */
         public void onSearch(@NonNull ControllerInfo controllerInfo, @NonNull String query,
                 @Nullable Bundle extras) {
-
         }
 
         /**
@@ -252,11 +263,6 @@
         }
 
         @Override
-        public MediaLibrarySessionBuilder setRatingType(int type) {
-            return super.setRatingType(type);
-        }
-
-        @Override
         public MediaLibrarySessionBuilder setSessionActivity(@Nullable PendingIntent pi) {
             return super.setSessionActivity(pi);
         }
diff --git a/media/java/android/media/MediaMetadata2.java b/media/java/android/media/MediaMetadata2.java
index 54a9057..fabf42b 100644
--- a/media/java/android/media/MediaMetadata2.java
+++ b/media/java/android/media/MediaMetadata2.java
@@ -436,6 +436,9 @@
     /**
      * Return a {@link Rating2} for the given key or null if no rating exists for
      * the given key.
+     * <p>
+     * For the {@link #METADATA_KEY_USER_RATING}, A {@code null} return value means that user rating
+     * cannot be set by {@link MediaController2}.
      *
      * @param key The key the value is stored under
      * @return A {@link Rating2} or {@code null}
diff --git a/media/java/android/media/MediaPlayerInterface.java b/media/java/android/media/MediaPlayerInterface.java
index 78e2391..b81c3d6 100644
--- a/media/java/android/media/MediaPlayerInterface.java
+++ b/media/java/android/media/MediaPlayerInterface.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.media.MediaSession2.PlaylistParams;
@@ -29,13 +30,56 @@
  */
 public interface MediaPlayerInterface {
     /**
-     * Listens change in {@link PlaybackState2}.
+     * Unspecified media player error.
      */
-    interface PlaybackListener {
+    int MEDIA_ERROR_UNKNOWN = MediaPlayer2.MEDIA_ERROR_UNKNOWN;
+
+    /**
+     * The video is streamed and its container is not valid for progressive
+     * playback i.e the video's index (e.g moov atom) is not at the start of the
+     * file.
+     */
+    int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK =
+            MediaPlayer2.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK;
+
+    /**
+     * File or network related operation errors.
+     */
+    int MEDIA_ERROR_IO = MediaPlayer2.MEDIA_ERROR_IO;
+
+    /**
+     * Bitstream is not conforming to the related coding standard or file spec.
+     */
+    int MEDIA_ERROR_MALFORMED = MediaPlayer2.MEDIA_ERROR_MALFORMED;
+
+    /**
+     * Bitstream is conforming to the related coding standard or file spec, but
+     * the media framework does not support the feature.
+     */
+    int MEDIA_ERROR_UNSUPPORTED = MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;
+
+    /**
+     * Some operation takes too long to complete, usually more than 3-5 seconds.
+     */
+    int MEDIA_ERROR_TIMED_OUT = MediaPlayer2.MEDIA_ERROR_TIMED_OUT;
+
+    /**
+     * Callbacks to listens to the changes in {@link PlaybackState2} and error.
+     */
+    interface EventCallback {
         /**
          * Called when {@link PlaybackState2} for this player is changed.
          */
-        void onPlaybackChanged(PlaybackState2 state);
+        default void onPlaybackStateChanged(PlaybackState2 state) { }
+
+        /**
+         * Called to indicate an error.
+         *
+         * @param mediaId optional mediaId to indicate error
+         * @param what what
+         * @param extra
+         */
+        default void onError(@Nullable String mediaId, int what, int extra) { }
     }
 
     // Transport controls that session will send command directly to this player.
@@ -75,17 +119,18 @@
     PlaylistParams getPlaylistParams();
 
     /**
-     * Add a {@link PlaybackListener} to be invoked when the playback state is changed.
+     * Register a {@link EventCallback}.
      *
-     * @param executor the Handler that will receive the listener
-     * @param listener the listener that will be run
+     * @param executor a callback executor
+     * @param callback a EventCallback
      */
-    void addPlaybackListener(Executor executor, PlaybackListener listener);
+    void registerEventCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull EventCallback callback);
 
     /**
-     * Remove previously added {@link PlaybackListener}.
+     * Unregister previously registered {@link EventCallback}.
      *
-     * @param listener the listener to be removed
+     * @param callback a EventCallback
      */
-    void removePlaybackListener(PlaybackListener listener);
+    void unregisterEventCallback(@NonNull EventCallback callback);
 }
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
index 0258dca..63e4e65 100644
--- a/media/java/android/media/MediaSession2.java
+++ b/media/java/android/media/MediaSession2.java
@@ -24,7 +24,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
 import android.media.session.MediaSession;
 import android.media.session.MediaSession.Callback;
 import android.media.session.PlaybackState;
@@ -41,7 +41,6 @@
 import android.os.Handler;
 import android.os.IInterface;
 import android.os.ResultReceiver;
-import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -87,8 +86,7 @@
     // TODO(jaewan): Should we define IntDef? Currently we don't have to allow subclass to add more.
     // TODO(jaewan): Shouldn't we pull out?
     // TODO(jaewan): Should we also protect getters not related with metadata?
-    //               Getters are getRatingType(), getPlaybackState(), getSessionActivity(),
-    //               getPlaylistParams())
+    //               Getters are getPlaybackState(), getSessionActivity(), getPlaylistParams()
     // Next ID: 23
     /**
      * Command code for the custom command which can be defined by string action in the
@@ -454,6 +452,11 @@
         /**
          * Called when a controller set rating of a media item through
          * {@link MediaController2#setRating(String, Rating2)}.
+         * <p>
+         * To allow setting user rating for a {@link MediaItem2}, the media item's metadata
+         * should have {@link Rating2} with the key {@link MediaMetadata#METADATA_KEY_USER_RATING},
+         * in order to provide possible rating style for controller. Controller will follow the
+         * rating style.
          *
          * @param controller controller information
          * @param mediaId media id from the controller
@@ -621,24 +624,6 @@
         }
 
         /**
-         * Set the style of rating used by this session. Apps trying to set the
-         * rating should use this style. Must be one of the following:
-         * <ul>
-         * <li>{@link Rating2#RATING_NONE}</li>
-         * <li>{@link Rating2#RATING_3_STARS}</li>
-         * <li>{@link Rating2#RATING_4_STARS}</li>
-         * <li>{@link Rating2#RATING_5_STARS}</li>
-         * <li>{@link Rating2#RATING_HEART}</li>
-         * <li>{@link Rating2#RATING_PERCENTAGE}</li>
-         * <li>{@link Rating2#RATING_THUMB_UP_DOWN}</li>
-         * </ul>
-         */
-        U setRatingType(@Rating2.Style int type) {
-            mProvider.setRatingType_impl(type);
-            return (U) this;
-        }
-
-        /**
          * Set an intent for launching UI for this Session. This can be used as a
          * quick link to an ongoing media screen. The intent should be for an
          * activity that may be started using {@link Context#startActivity(Intent)}.
@@ -710,11 +695,6 @@
         }
 
         @Override
-        public Builder setRatingType(@Rating2.Style int type) {
-            return super.setRatingType(type);
-        }
-
-        @Override
         public Builder setSessionActivity(@Nullable PendingIntent pi) {
             return super.setSessionActivity(pi);
         }
@@ -1077,8 +1057,9 @@
      * to. Events from the {@link MediaController2} will be sent directly to the underlying
      * player on the {@link Handler} where the session is created on.
      * <p>
-     * If the new player is successfully set, {@link PlaybackListener}
-     * will be called to tell the current playback state of the new player.
+     * If the new player is successfully set,
+     * {@link EventCallback#onPlaybackStateChanged(PlaybackState2)} will be called to tell the
+     * current playback state of the new player.
      * <p>
      * For the remote playback case which you want to handle volume by yourself, use
      * {@link #setPlayer(MediaPlayerInterface, VolumeProvider2)}.
@@ -1344,28 +1325,28 @@
     }
 
     /*
-     * Add a {@link PlaybackListener} to listen changes in the underlying
-     * {@link MediaPlayerInterface}. Listener will be called immediately to tell the current value.
+     * Register {@link EventCallback} to listen changes in the underlying
+     * {@link MediaPlayerInterface}, regardless of the change in the underlying player.
      * <p>
-     * Added listeners will be also called when the underlying player is changed.
+     * Registered callbacks will be also called when the underlying player is changed.
      *
-     * @param executor the call listener
-     * @param listener the listener that will be run
-     * @throws IllegalArgumentException when either the listener or handler is {@code null}.
+     * @param executor a callback Executor
+     * @param callback a EventCallback
+     * @throws IllegalArgumentException if executor or callback is {@code null}.
      */
-    public void addPlaybackListener(@NonNull @CallbackExecutor Executor executor,
-            @NonNull PlaybackListener listener) {
-        mProvider.addPlaybackListener_impl(executor, listener);
+    public void registerPlayerEventCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull EventCallback callback) {
+        mProvider.registerPlayerEventCallback_impl(executor, callback);
     }
 
     /**
-     * Remove previously added {@link PlaybackListener}.
+     * Unregister the previously registered {@link EventCallback}.
      *
-     * @param listener the listener to be removed
-     * @throws IllegalArgumentException if the listener is {@code null}.
+     * @param callback the callback to be removed
+     * @throws IllegalArgumentException if the callback is {@code null}.
      */
-    public void removePlaybackListener(@NonNull PlaybackListener listener) {
-        mProvider.removePlaybackListener_impl(listener);
+    public void unregisterPlayerEventCallback(@NonNull EventCallback callback) {
+        mProvider.unregisterPlayerEventCallback_impl(callback);
     }
 
     /**
diff --git a/media/java/android/media/PlaybackState2.java b/media/java/android/media/PlaybackState2.java
index 627974a..a95b8f2 100644
--- a/media/java/android/media/PlaybackState2.java
+++ b/media/java/android/media/PlaybackState2.java
@@ -34,7 +34,7 @@
  * @hide
  */
 public final class PlaybackState2 {
-    // Similar to the PlaybackState2 with following changes
+    // Similar to the PlaybackState with following changes
     //    - Not implement Parcelable and added from/toBundle()
     //    - Removed playback state that doesn't match with the MediaPlayer2
     //      Full list should be finalized when the MediaPlayer2 has getter for the playback state.
@@ -73,6 +73,7 @@
     //         |    EventCallback.onBufferingUpdate()   |                                        |
     //         +----------------------------------------+----------------------------------------+
     //    - Removed actions and custom actions.
+    //    - Removed error string
     //    - Repeat mode / shuffle mode is now in the PlaylistParams
     // TODO(jaewan): Replace states from MediaPlayer2
     /**
@@ -110,8 +111,7 @@
     public final static int STATE_BUFFERING = 4;
 
     /**
-     * State indicating this item is currently in an error state. The error
-     * message should also be set when entering this state.
+     * State indicating this item is currently in an error state.
      */
     public final static int STATE_ERROR = 5;
 
@@ -122,13 +122,10 @@
 
     private final PlaybackState2Provider mProvider;
 
-    // TODO(jaewan): Better error handling?
-    //               E.g. media item at #2 has issue, but continue playing #3
-    //                    login error. fire intent xxx to login
     public PlaybackState2(@NonNull Context context, int state, long position, long updateTime,
-            float speed, long bufferedPosition, long activeItemId, CharSequence error) {
+            float speed, long bufferedPosition, long activeItemId) {
         mProvider = ApiLoader.getProvider(context).createPlaybackState2(context, this, state,
-                position, updateTime, speed, bufferedPosition, activeItemId, error);
+                position, updateTime, speed, bufferedPosition, activeItemId);
     }
 
     @Override
@@ -141,10 +138,8 @@
      * <ul>
      * <li> {@link PlaybackState2#STATE_NONE}</li>
      * <li> {@link PlaybackState2#STATE_STOPPED}</li>
-     * <li> {@link PlaybackState2#STATE_PREPARED}</li>
      * <li> {@link PlaybackState2#STATE_PAUSED}</li>
      * <li> {@link PlaybackState2#STATE_PLAYING}</li>
-     * <li> {@link PlaybackState2#STATE_FINISH}</li>
      * <li> {@link PlaybackState2#STATE_BUFFERING}</li>
      * <li> {@link PlaybackState2#STATE_ERROR}</li>
      * </ul>
@@ -182,14 +177,6 @@
     }
 
     /**
-     * Get a user readable error message. This should be set when the state is
-     * {@link PlaybackState2#STATE_ERROR}.
-     */
-    public CharSequence getErrorMessage() {
-        return mProvider.getErrorMessage_impl();
-    }
-
-    /**
      * Get the elapsed real time at which position was last updated. If the
      * position has never been set this will return 0;
      *
diff --git a/media/java/android/media/SessionPlayer2.java b/media/java/android/media/SessionPlayer2.java
deleted file mode 100644
index 60acf16..0000000
--- a/media/java/android/media/SessionPlayer2.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright 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 android.media;
-
-import android.annotation.SystemApi;
-import android.content.Context;
-import android.media.MediaSession2.PlaylistParams;
-import android.media.update.ApiLoader;
-import android.media.update.SessionPlayer2Provider;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * Implementation of the {@link MediaPlayerInterface} which is backed by the {@link MediaPlayer2}
- * @hide
- */
-public class SessionPlayer2 implements MediaPlayerInterface {
-    private final SessionPlayer2Provider mProvider;
-
-    public SessionPlayer2(Context context) {
-        mProvider = ApiLoader.getProvider(context).createSessionPlayer2(context, this);
-    }
-
-    @Override
-    public void play() {
-        mProvider.play_impl();
-    }
-
-    @Override
-    public void prepare() {
-        mProvider.prepare_impl();
-    }
-
-    @Override
-    public void pause() {
-        mProvider.pause_impl();
-    }
-
-    @Override
-    public void stop() {
-        mProvider.stop_impl();
-    }
-
-    @Override
-    public void skipToPrevious() {
-        mProvider.skipToPrevious_impl();
-    }
-
-    @Override
-    public void skipToNext() {
-        mProvider.skipToNext_impl();
-    }
-
-    @Override
-    public void seekTo(long pos) {
-        mProvider.seekTo_impl(pos);
-    }
-
-    @Override
-    public void fastForward() {
-        mProvider.fastForward_impl();
-    }
-
-    @Override
-    public void rewind() {
-        mProvider.rewind_impl();
-    }
-
-    @Override
-    public PlaybackState2 getPlaybackState() {
-        return mProvider.getPlaybackState_impl();
-    }
-
-    @Override
-    public void setAudioAttributes(AudioAttributes attributes) {
-        mProvider.setAudioAttributes_impl(attributes);
-    }
-
-    @Override
-    public AudioAttributes getAudioAttributes() {
-        return mProvider.getAudioAttributes_impl();
-    }
-
-    @Override
-    public void setPlaylist(List<MediaItem2> playlist) {
-        mProvider.setPlaylist_impl(playlist);
-    }
-
-    @Override
-    public List<MediaItem2> getPlaylist() {
-        return mProvider.getPlaylist_impl();
-    }
-
-    @Override
-    public void setCurrentPlaylistItem(int index) {
-        mProvider.setCurrentPlaylistItem_impl(index);
-    }
-
-    @Override
-    public void setPlaylistParams(PlaylistParams params) {
-        mProvider.setPlaylistParams_impl(params);
-    }
-
-    @Override
-    public void addPlaylistItem(int index, MediaItem2 item) {
-        mProvider.addPlaylistItem_impl(index, item);
-    }
-
-    @Override
-    public void removePlaylistItem(MediaItem2 item) {
-        mProvider.removePlaylistItem_impl(item);
-    }
-
-    @Override
-    public PlaylistParams getPlaylistParams() {
-        return mProvider.getPlaylistParams_impl();
-    }
-
-    @Override
-    public void addPlaybackListener(Executor executor, PlaybackListener listener) {
-        mProvider.addPlaybackListener_impl(executor, listener);
-    }
-
-    @Override
-    public void removePlaybackListener(PlaybackListener listener) {
-        mProvider.removePlaybackListener_impl(listener);
-    }
-
-    public MediaPlayer2 getPlayer() {
-        return mProvider.getPlayer_impl();
-    }
-
-    @SystemApi
-    public SessionPlayer2Provider getProvider() {
-        return mProvider;
-    }
-}
diff --git a/media/java/android/media/update/MediaBrowser2Provider.java b/media/java/android/media/update/MediaBrowser2Provider.java
index eda4c7c..a18701e 100644
--- a/media/java/android/media/update/MediaBrowser2Provider.java
+++ b/media/java/android/media/update/MediaBrowser2Provider.java
@@ -25,7 +25,7 @@
     void getLibraryRoot_impl(Bundle rootHints);
 
     void subscribe_impl(String parentId, Bundle extras);
-    void unsubscribe_impl(String parentId, Bundle extras);
+    void unsubscribe_impl(String parentId);
 
     void getItem_impl(String mediaId);
     void getChildren_impl(String parentId, int page, int pageSize, Bundle extras);
diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java
index 738bd0c..8d9efd5 100644
--- a/media/java/android/media/update/MediaController2Provider.java
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -43,7 +43,6 @@
     boolean isConnected_impl();
 
     PendingIntent getSessionActivity_impl();
-    int getRatingType_impl();
 
     void setVolumeTo_impl(int value, int flags);
     void adjustVolume_impl(int direction, int flags);
diff --git a/media/java/android/media/update/MediaLibraryService2Provider.java b/media/java/android/media/update/MediaLibraryService2Provider.java
index d837833..5b5d36e 100644
--- a/media/java/android/media/update/MediaLibraryService2Provider.java
+++ b/media/java/android/media/update/MediaLibraryService2Provider.java
@@ -30,10 +30,11 @@
     // Nothing new for now
 
     interface MediaLibrarySessionProvider extends MediaSession2Provider {
-        void notifyChildrenChanged_impl(ControllerInfo controller, String parentId, Bundle extras);
-        void notifyChildrenChanged_impl(String parentId, Bundle extras);
-        void notifySearchResultChanged_impl(ControllerInfo controller, String query, Bundle extras,
-                int itemCount);
+        void notifyChildrenChanged_impl(ControllerInfo controller, String parentId,
+                int childCount, Bundle extras);
+        void notifyChildrenChanged_impl(String parentId, int childCount, Bundle extras);
+        void notifySearchResultChanged_impl(ControllerInfo controller, String query, int itemCount,
+                Bundle extras);
     }
 
     interface LibraryRootProvider {
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
index 41162e0..fc1f671 100644
--- a/media/java/android/media/update/MediaSession2Provider.java
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -20,7 +20,7 @@
 import android.media.MediaItem2;
 import android.media.MediaMetadata2;
 import android.media.MediaPlayerInterface;
-import android.media.MediaPlayerInterface.PlaybackListener;
+import android.media.MediaPlayerInterface.EventCallback;
 import android.media.MediaSession2;
 import android.media.MediaSession2.Command;
 import android.media.MediaSession2.CommandButton;
@@ -61,8 +61,8 @@
     void setPlaylistParams_impl(PlaylistParams params);
     PlaylistParams getPlaylistParams_impl();
 
-    void addPlaybackListener_impl(Executor executor, PlaybackListener listener);
-    void removePlaybackListener_impl(PlaybackListener listener);
+    void registerPlayerEventCallback_impl(Executor executor, EventCallback callback);
+    void unregisterPlayerEventCallback_impl(EventCallback callback);
 
     interface CommandProvider {
         int getCommandCode_impl();
@@ -117,7 +117,6 @@
 
     interface BuilderBaseProvider<T extends MediaSession2, C extends SessionCallback> {
         void setVolumeProvider_impl(VolumeProvider2 volumeProvider);
-        void setRatingType_impl(int type);
         void setSessionActivity_impl(PendingIntent pi);
         void setId_impl(String id);
         void setSessionCallback_impl(Executor executor, C callback);
diff --git a/media/java/android/media/update/PlaybackState2Provider.java b/media/java/android/media/update/PlaybackState2Provider.java
index 2875e98..93f769c 100644
--- a/media/java/android/media/update/PlaybackState2Provider.java
+++ b/media/java/android/media/update/PlaybackState2Provider.java
@@ -33,8 +33,6 @@
 
     float getPlaybackSpeed_impl();
 
-    CharSequence getErrorMessage_impl();
-
     long getLastPositionUpdateTime_impl();
 
     long getCurrentPlaylistItemIndex_impl();
diff --git a/media/java/android/media/update/SessionPlayer2Provider.java b/media/java/android/media/update/SessionPlayer2Provider.java
deleted file mode 100644
index e068c21..0000000
--- a/media/java/android/media/update/SessionPlayer2Provider.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 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 android.media.update;
-
-import android.media.AudioAttributes;
-import android.media.MediaItem2;
-import android.media.MediaPlayer2;
-import android.media.MediaPlayerInterface.PlaybackListener;
-import android.media.MediaSession2.PlaylistParams;
-import android.media.PlaybackState2;
-
-import java.util.List;
-import java.util.concurrent.Executor;
-
-/**
- * @hide
- */
-public interface SessionPlayer2Provider {
-    void play_impl();
-    void prepare_impl();
-    void pause_impl();
-    void stop_impl();
-    void skipToPrevious_impl();
-    void skipToNext_impl();
-    void seekTo_impl(long pos);
-    void fastForward_impl();
-    void rewind_impl();
-    PlaybackState2 getPlaybackState_impl();
-    void setAudioAttributes_impl(AudioAttributes attributes);
-    AudioAttributes getAudioAttributes_impl();
-    void addPlaylistItem_impl(int index, MediaItem2 item);
-    void removePlaylistItem_impl(MediaItem2 item);
-    void setPlaylist_impl(List<MediaItem2> playlist);
-    List<MediaItem2> getPlaylist_impl();
-    void setCurrentPlaylistItem_impl(int index);
-    void setPlaylistParams_impl(PlaylistParams params);
-    PlaylistParams getPlaylistParams_impl();
-    void addPlaybackListener_impl(Executor executor, PlaybackListener listener);
-    void removePlaybackListener_impl(PlaybackListener listener);
-    MediaPlayer2 getPlayer_impl();
-}
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 57f04cc..29a30343 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -40,7 +40,6 @@
 import android.media.MediaSessionService2.MediaNotification;
 import android.media.PlaybackState2;
 import android.media.Rating2;
-import android.media.SessionPlayer2;
 import android.media.SessionToken2;
 import android.media.VolumeProvider2;
 import android.media.update.MediaLibraryService2Provider.LibraryRootProvider;
@@ -113,8 +112,6 @@
             String packageName, String serviceName, int uid);
     SessionToken2 SessionToken2_fromBundle(Context context, Bundle bundle);
 
-    SessionPlayer2Provider createSessionPlayer2(Context context, SessionPlayer2 instance);
-
     MediaItem2Provider createMediaItem2(Context context, MediaItem2 mediaItem2,
             String mediaId, DataSourceDesc dsd, MediaMetadata2 metadata, int flags);
     MediaItem2 fromBundle_MediaItem2(Context context, Bundle bundle);
@@ -136,7 +133,6 @@
     Rating2 newPercentageRating_Rating2(Context context, float percent);
 
     PlaybackState2Provider createPlaybackState2(Context context, PlaybackState2 instance, int state,
-            long position, long updateTime, float speed, long bufferedPosition, long activeItemId,
-            CharSequence error);
+            long position, long updateTime, float speed, long bufferedPosition, long activeItemId);
     PlaybackState2 fromBundle_PlaybackState2(Context context, Bundle bundle);
 }
diff --git a/native/webview/plat_support/Android.mk b/native/webview/plat_support/Android.mk
new file mode 100644
index 0000000..6a33fe2
--- /dev/null
+++ b/native/webview/plat_support/Android.mk
@@ -0,0 +1,52 @@
+#
+# Copyright (C) 2012 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.
+#
+
+# This package provides the system interfaces allowing WebView to render.
+
+LOCAL_PATH := $(call my-dir)
+
+# Native support library (libwebviewchromium_plat_support.so) - does NOT link
+# any native chromium code.
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libwebviewchromium_plat_support
+
+LOCAL_SRC_FILES:= \
+        draw_gl_functor.cpp \
+        jni_entry_point.cpp \
+        graphics_utils.cpp \
+        graphic_buffer_impl.cpp \
+
+LOCAL_C_INCLUDES:= \
+        external/skia/include/core \
+        frameworks/base/core/jni/android/graphics \
+        frameworks/native/include/ui \
+
+LOCAL_SHARED_LIBRARIES += \
+        libandroid_runtime \
+        liblog \
+        libcutils \
+        libui \
+        libutils \
+        libhwui \
+        libandroidfw
+
+LOCAL_MODULE_TAGS := optional
+
+# To remove warnings from skia header files
+LOCAL_CFLAGS := -Wno-unused-parameter
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/native/webview/plat_support/LICENSE b/native/webview/plat_support/LICENSE
new file mode 100644
index 0000000..972bb2e
--- /dev/null
+++ b/native/webview/plat_support/LICENSE
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/native/webview/plat_support/draw_gl.h b/native/webview/plat_support/draw_gl.h
new file mode 100644
index 0000000..c8434b6
--- /dev/null
+++ b/native/webview/plat_support/draw_gl.h
@@ -0,0 +1,131 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+//******************************************************************************
+// This is a copy of the coresponding android_webview/public/browser header.
+// Any changes to the interface should be made there.
+//
+// The purpose of having the copy is twofold:
+//  - it removes the need to have Chromium sources present in the tree in order
+//    to build the plat_support library,
+//  - it captures API that the corresponding Android release supports.
+//******************************************************************************
+
+#ifndef ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_GL_H_
+#define ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_GL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// 1 is L/L MR1
+//
+// 2 starts at M, and added an imperfect workaround for complex clipping by
+// elevating the WebView into an FBO layer. If any transform, clip, or outline
+// clip occurs that would either likely use the stencil buffer for clipping, or
+// require shader based clipping in HWUI, the WebView is drawn into an FBO (if
+// it fits).
+// This is a temporary workaround for a lack of WebView support for stencil/
+// shader based round rect clipping, and should be removed when webview is
+// capable of supporting these clips internally when drawing.
+//
+// 3 starts during development of P, when android defaults from HWUI to skia as
+// the GL renderer. Skia already maintains and restores its GL state, so there
+// is no need for WebView to restore this state. Skia also no longer promises
+// GL state on entering draw, such as no vertex array buffer binding.
+static const int kAwDrawGLInfoVersion = 3;
+
+// Holds the information required to trigger an OpenGL drawing operation.
+struct AwDrawGLInfo {
+  int version;  // The AwDrawGLInfo this struct was built with.
+
+  // Input: tells the draw function what action to perform.
+  enum Mode {
+    kModeDraw = 0,
+    kModeProcess,
+    kModeProcessNoContext,
+    kModeSync,
+  } mode;
+
+  // Input: current clip rect in surface coordinates. Reflects the current state
+  // of the OpenGL scissor rect. Both the OpenGL scissor rect and viewport are
+  // set by the caller of the draw function and updated during View animations.
+  int clip_left;
+  int clip_top;
+  int clip_right;
+  int clip_bottom;
+
+  // Input: current width/height of destination surface.
+  int width;
+  int height;
+
+  // Input: is the View rendered into an independent layer.
+  // If false, the surface is likely to hold to the full screen contents, with
+  // the scissor box set by the caller to the actual View location and size.
+  // Also the transformation matrix will contain at least a translation to the
+  // position of the View to render, plus any other transformations required as
+  // part of any ongoing View animation. View translucency (alpha) is ignored,
+  // although the framework will set is_layer to true for non-opaque cases.
+  // Can be requested via the View.setLayerType(View.LAYER_TYPE_NONE, ...)
+  // Android API method.
+  //
+  // If true, the surface is dedicated to the View and should have its size.
+  // The viewport and scissor box are set by the caller to the whole surface.
+  // Animation transformations are handled by the caller and not reflected in
+  // the provided transformation matrix. Translucency works normally.
+  // Can be requested via the View.setLayerType(View.LAYER_TYPE_HARDWARE, ...)
+  // Android API method.
+  bool is_layer;
+
+  // Input: current transformation matrix in surface pixels.
+  // Uses the column-based OpenGL matrix format.
+  float transform[16];
+};
+
+// Function to invoke a direct GL draw into the client's pre-configured
+// GL context. Obtained via AwContents.getDrawGLFunction() (static).
+// |view_context| is an opaque identifier that was returned by the corresponding
+// call to AwContents.getAwDrawGLViewContext().
+// |draw_info| carries the in and out parameters for this draw.
+// |spare| ignored; pass NULL.
+typedef void (AwDrawGLFunction)(long view_context,
+                                AwDrawGLInfo* draw_info,
+                                void* spare);
+enum AwMapMode {
+  MAP_READ_ONLY,
+  MAP_WRITE_ONLY,
+  MAP_READ_WRITE,
+};
+
+// Called to create a GraphicBuffer
+typedef long AwCreateGraphicBufferFunction(int w, int h);
+// Called to release a GraphicBuffer
+typedef void AwReleaseGraphicBufferFunction(long buffer_id);
+// Called to map a GraphicBuffer in |mode|.
+typedef int AwMapFunction(long buffer_id, AwMapMode mode, void** vaddr);
+// Called to unmap a GraphicBuffer
+typedef int AwUnmapFunction(long buffer_id);
+// Called to get a native buffer pointer
+typedef void* AwGetNativeBufferFunction(long buffer_id);
+// Called to get the stride of the buffer
+typedef unsigned int AwGetStrideFunction(long buffer_id);
+
+static const int kAwDrawGLFunctionTableVersion = 1;
+
+// Set of functions used in rendering in hardware mode
+struct AwDrawGLFunctionTable {
+  int version;
+  AwCreateGraphicBufferFunction* create_graphic_buffer;
+  AwReleaseGraphicBufferFunction* release_graphic_buffer;
+  AwMapFunction* map;
+  AwUnmapFunction* unmap;
+  AwGetNativeBufferFunction* get_native_buffer;
+  AwGetStrideFunction* get_stride;
+};
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_GL_H_
diff --git a/native/webview/plat_support/draw_gl_functor.cpp b/native/webview/plat_support/draw_gl_functor.cpp
new file mode 100644
index 0000000..d54f558
--- /dev/null
+++ b/native/webview/plat_support/draw_gl_functor.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+// Provides a webviewchromium glue layer adapter from the internal Android
+// GL Functor data types into the types the chromium stack expects, and back.
+
+#define LOG_TAG "webviewchromium_plat_support"
+
+#include "draw_gl.h"
+
+#include <Properties.h>
+#include <errno.h>
+#include <jni.h>
+#include <private/hwui/DrawGlInfo.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <utils/Functor.h>
+#include <utils/Log.h>
+
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+#define COMPILE_ASSERT(expr, err) \
+__unused static const char (err)[(expr) ? 1 : -1] = "";
+
+namespace android {
+namespace {
+
+AwDrawGLFunction* g_aw_drawgl_function = NULL;
+
+class DrawGLFunctor : public Functor {
+ public:
+  explicit DrawGLFunctor(jlong view_context) : view_context_(view_context) {}
+  virtual ~DrawGLFunctor() {}
+
+  // Functor
+  virtual status_t operator ()(int what, void* data) {
+    using uirenderer::DrawGlInfo;
+    if (!g_aw_drawgl_function) {
+      ALOGE("Cannot draw: no DrawGL Function installed");
+      return DrawGlInfo::kStatusDone;
+    }
+
+    AwDrawGLInfo aw_info;
+    // TODO(boliu): Remove property check once OpenGL fallback is removed.
+    auto render_pipeline_type =
+        android::uirenderer::Properties::getRenderPipelineType();
+    aw_info.version = (render_pipeline_type ==
+                       android::uirenderer::RenderPipelineType::OpenGL)
+                          ? 2
+                          : kAwDrawGLInfoVersion;
+    switch (what) {
+      case DrawGlInfo::kModeDraw: {
+        aw_info.mode = AwDrawGLInfo::kModeDraw;
+        DrawGlInfo* gl_info = reinterpret_cast<DrawGlInfo*>(data);
+
+        // Map across the input values.
+        aw_info.clip_left = gl_info->clipLeft;
+        aw_info.clip_top = gl_info->clipTop;
+        aw_info.clip_right = gl_info->clipRight;
+        aw_info.clip_bottom = gl_info->clipBottom;
+        aw_info.width = gl_info->width;
+        aw_info.height = gl_info->height;
+        aw_info.is_layer = gl_info->isLayer;
+        COMPILE_ASSERT(NELEM(aw_info.transform) == NELEM(gl_info->transform),
+                       mismatched_transform_matrix_sizes);
+        for (int i = 0; i < NELEM(aw_info.transform); ++i) {
+          aw_info.transform[i] = gl_info->transform[i];
+        }
+        break;
+      }
+      case DrawGlInfo::kModeProcess:
+        aw_info.mode = AwDrawGLInfo::kModeProcess;
+        break;
+      case DrawGlInfo::kModeProcessNoContext:
+        aw_info.mode = AwDrawGLInfo::kModeProcessNoContext;
+        break;
+      case DrawGlInfo::kModeSync:
+        aw_info.mode = AwDrawGLInfo::kModeSync;
+        break;
+      default:
+        ALOGE("Unexpected DrawGLInfo type %d", what);
+        return DrawGlInfo::kStatusDone;
+    }
+
+    // Invoke the DrawGL method.
+    g_aw_drawgl_function(view_context_, &aw_info, NULL);
+
+    return DrawGlInfo::kStatusDone;
+  }
+
+ private:
+  intptr_t view_context_;
+};
+
+// Raise the file handle soft limit to the hard limit since gralloc buffers
+// uses file handles.
+void RaiseFileNumberLimit() {
+  static bool have_raised_limit = false;
+  if (have_raised_limit)
+    return;
+
+  have_raised_limit = true;
+  struct rlimit limit_struct;
+  limit_struct.rlim_cur = 0;
+  limit_struct.rlim_max = 0;
+  if (getrlimit(RLIMIT_NOFILE, &limit_struct) == 0) {
+    limit_struct.rlim_cur = limit_struct.rlim_max;
+    if (setrlimit(RLIMIT_NOFILE, &limit_struct) != 0) {
+      ALOGE("setrlimit failed: %s", strerror(errno));
+    }
+  } else {
+    ALOGE("getrlimit failed: %s", strerror(errno));
+  }
+}
+
+jlong CreateGLFunctor(JNIEnv*, jclass, jlong view_context) {
+  RaiseFileNumberLimit();
+  return reinterpret_cast<jlong>(new DrawGLFunctor(view_context));
+}
+
+void DestroyGLFunctor(JNIEnv*, jclass, jlong functor) {
+  delete reinterpret_cast<DrawGLFunctor*>(functor);
+}
+
+void SetChromiumAwDrawGLFunction(JNIEnv*, jclass, jlong draw_function) {
+  g_aw_drawgl_function = reinterpret_cast<AwDrawGLFunction*>(draw_function);
+}
+
+const char kClassName[] = "com/android/webview/chromium/DrawGLFunctor";
+const JNINativeMethod kJniMethods[] = {
+    { "nativeCreateGLFunctor", "(J)J",
+        reinterpret_cast<void*>(CreateGLFunctor) },
+    { "nativeDestroyGLFunctor", "(J)V",
+        reinterpret_cast<void*>(DestroyGLFunctor) },
+    { "nativeSetChromiumAwDrawGLFunction", "(J)V",
+        reinterpret_cast<void*>(SetChromiumAwDrawGLFunction) },
+};
+
+}  // namespace
+
+void RegisterDrawGLFunctor(JNIEnv* env) {
+  jclass clazz = env->FindClass(kClassName);
+  LOG_ALWAYS_FATAL_IF(!clazz, "Unable to find class '%s'", kClassName);
+
+  int res = env->RegisterNatives(clazz, kJniMethods, NELEM(kJniMethods));
+  LOG_ALWAYS_FATAL_IF(res < 0, "register native methods failed: res=%d", res);
+}
+
+}  // namespace android
diff --git a/native/webview/plat_support/draw_sw.h b/native/webview/plat_support/draw_sw.h
new file mode 100644
index 0000000..7423e13
--- /dev/null
+++ b/native/webview/plat_support/draw_sw.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+//******************************************************************************
+// This is a copy of the coresponding android_webview/public/browser header.
+// Any changes to the interface should be made there.
+//
+// The purpose of having the copy is twofold:
+//  - it removes the need to have Chromium sources present in the tree in order
+//    to build the plat_support library,
+//  - it captures API that the corresponding Android release supports.
+//******************************************************************************
+
+#ifndef ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_SW_H_
+#define ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_SW_H_
+
+#include <jni.h>
+#include <stddef.h>
+
+#ifndef __cplusplus
+#error "Can't mix C and C++ when using jni.h"
+#endif
+
+class SkCanvasState;
+class SkPicture;
+
+static const int kAwPixelInfoVersion = 3;
+
+// Holds the information required to implement the SW draw to system canvas.
+struct AwPixelInfo {
+  int version;          // The kAwPixelInfoVersion this struct was built with.
+  SkCanvasState* state; // The externalize state in skia format.
+  // NOTE: If you add more members, bump kAwPixelInfoVersion.
+};
+
+// Function that can be called to fish out the underlying native pixel data
+// from a Java canvas object, for optimized rendering path.
+// Returns the pixel info on success, which must be freed via a call to
+// AwReleasePixelsFunction, or NULL.
+typedef AwPixelInfo* (AwAccessPixelsFunction)(JNIEnv* env, jobject canvas);
+
+// Must be called to balance every *successful* call to AwAccessPixelsFunction
+// (i.e. that returned true).
+typedef void (AwReleasePixelsFunction)(AwPixelInfo* pixels);
+
+// Called to create an Android Picture object encapsulating a native SkPicture.
+typedef jobject (AwCreatePictureFunction)(JNIEnv* env, SkPicture* picture);
+
+// Method that returns the current Skia function.
+typedef void (SkiaVersionFunction)(int* major, int* minor, int* patch);
+
+// Called to verify if the Skia versions are compatible.
+typedef bool (AwIsSkiaVersionCompatibleFunction)(SkiaVersionFunction function);
+
+static const int kAwDrawSWFunctionTableVersion = 1;
+
+// "vtable" for the functions declared in this file. An instance must be set via
+// AwContents.setAwDrawSWFunctionTable
+struct AwDrawSWFunctionTable {
+  int version;
+  AwAccessPixelsFunction* access_pixels;
+  AwReleasePixelsFunction* release_pixels;
+};
+
+#endif  // ANDROID_WEBVIEW_PUBLIC_BROWSER_DRAW_SW_H_
diff --git a/native/webview/plat_support/graphic_buffer_impl.cpp b/native/webview/plat_support/graphic_buffer_impl.cpp
new file mode 100644
index 0000000..4426778
--- /dev/null
+++ b/native/webview/plat_support/graphic_buffer_impl.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+// Provides the implementation of the GraphicBuffer interface in
+// renderer compostior
+
+#include "graphic_buffer_impl.h"
+
+#include <utils/Errors.h>
+
+namespace android {
+
+GraphicBufferImpl::GraphicBufferImpl(uint32_t w, uint32_t h)
+  : mBuffer(new android::GraphicBuffer(w, h, PIXEL_FORMAT_RGBA_8888,
+       android::GraphicBuffer::USAGE_HW_TEXTURE |
+       android::GraphicBuffer::USAGE_SW_READ_OFTEN |
+       android::GraphicBuffer::USAGE_SW_WRITE_OFTEN)) {
+}
+
+GraphicBufferImpl::~GraphicBufferImpl() {
+}
+
+// static
+long GraphicBufferImpl::Create(int w, int h) {
+  GraphicBufferImpl* buffer = new GraphicBufferImpl(
+      static_cast<uint32_t>(w), static_cast<uint32_t>(h));
+  if (buffer->InitCheck() != NO_ERROR) {
+    delete buffer;
+    return 0;
+  }
+  return reinterpret_cast<intptr_t>(buffer);
+}
+
+// static
+void GraphicBufferImpl::Release(long buffer_id) {
+  GraphicBufferImpl* buffer = reinterpret_cast<GraphicBufferImpl*>(buffer_id);
+  delete buffer;
+}
+
+// static
+int GraphicBufferImpl::MapStatic(long buffer_id, AwMapMode mode, void** vaddr) {
+  GraphicBufferImpl* buffer = reinterpret_cast<GraphicBufferImpl*>(buffer_id);
+  return buffer->Map(mode, vaddr);
+}
+
+// static
+int GraphicBufferImpl::UnmapStatic(long buffer_id) {
+  GraphicBufferImpl* buffer = reinterpret_cast<GraphicBufferImpl*>(buffer_id);
+  return buffer->Unmap();
+}
+
+// static
+void* GraphicBufferImpl::GetNativeBufferStatic(long buffer_id) {
+  GraphicBufferImpl* buffer = reinterpret_cast<GraphicBufferImpl*>(buffer_id);
+  return buffer->GetNativeBuffer();
+}
+
+// static
+uint32_t GraphicBufferImpl::GetStrideStatic(long buffer_id) {
+  GraphicBufferImpl* buffer = reinterpret_cast<GraphicBufferImpl*>(buffer_id);
+  return buffer->GetStride();
+}
+
+status_t GraphicBufferImpl::Map(AwMapMode mode, void** vaddr) {
+  int usage = 0;
+  switch (mode) {
+    case MAP_READ_ONLY:
+      usage = android::GraphicBuffer::USAGE_SW_READ_OFTEN;
+      break;
+    case MAP_WRITE_ONLY:
+      usage = android::GraphicBuffer::USAGE_SW_WRITE_OFTEN;
+      break;
+    case MAP_READ_WRITE:
+      usage = android::GraphicBuffer::USAGE_SW_READ_OFTEN |
+          android::GraphicBuffer::USAGE_SW_WRITE_OFTEN;
+      break;
+    default:
+      return INVALID_OPERATION;
+  }
+  return mBuffer->lock(usage, vaddr);
+}
+
+status_t GraphicBufferImpl::Unmap() {
+  return mBuffer->unlock();
+}
+
+status_t GraphicBufferImpl::InitCheck() const {
+  return mBuffer->initCheck();
+}
+
+void* GraphicBufferImpl::GetNativeBuffer() const {
+  return mBuffer->getNativeBuffer();
+}
+
+uint32_t GraphicBufferImpl::GetStride() const {
+  static const int kBytesPerPixel = 4;
+  return mBuffer->getStride() * kBytesPerPixel;
+}
+
+} // namespace android
diff --git a/native/webview/plat_support/graphic_buffer_impl.h b/native/webview/plat_support/graphic_buffer_impl.h
new file mode 100644
index 0000000..442710a
--- /dev/null
+++ b/native/webview/plat_support/graphic_buffer_impl.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+// Provides the implementation of the GraphicBuffer interface in
+// renderer compostior
+
+#ifndef ANDROID_GRAPHIC_BUFFER_IMPL_H
+#define ANDROID_GRAPHIC_BUFFER_IMPL_H
+
+#include <ui/GraphicBuffer.h>
+
+#include "draw_gl.h"
+
+namespace android {
+
+class GraphicBufferImpl {
+ public:
+  ~GraphicBufferImpl();
+
+  static long Create(int w, int h);
+  static void Release(long buffer_id);
+  static int MapStatic(long buffer_id, AwMapMode mode, void** vaddr);
+  static int UnmapStatic(long buffer_id);
+  static void* GetNativeBufferStatic(long buffer_id);
+  static uint32_t GetStrideStatic(long buffer_id);
+
+ private:
+  status_t Map(AwMapMode mode, void** vaddr);
+  status_t Unmap();
+  status_t InitCheck() const;
+  void* GetNativeBuffer() const;
+  uint32_t GetStride() const;
+  GraphicBufferImpl(uint32_t w, uint32_t h);
+
+  sp<android::GraphicBuffer> mBuffer;
+};
+
+}  // namespace android
+
+#endif  // ANDROID_GRAPHIC_BUFFER_IMPL_H
diff --git a/native/webview/plat_support/graphics_utils.cpp b/native/webview/plat_support/graphics_utils.cpp
new file mode 100644
index 0000000..89beb75
--- /dev/null
+++ b/native/webview/plat_support/graphics_utils.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+// Provides a webviewchromium glue layer adapter from the internal Android
+// graphics types into the types the chromium stack expects, and back.
+
+#define LOG_TAG "webviewchromium_plat_support"
+
+#include "draw_gl.h"
+#include "draw_sw.h"
+
+#include <cstdlib>
+#include <jni.h>
+#include <utils/Log.h>
+#include "graphic_buffer_impl.h"
+#include "GraphicsJNI.h"
+#include "SkCanvasStateUtils.h"
+#include "SkGraphics.h"
+#include "SkPicture.h"
+
+#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+
+namespace android {
+namespace {
+
+class PixelInfo : public AwPixelInfo {
+ public:
+  explicit PixelInfo(android::Canvas* canvas);
+  ~PixelInfo();
+};
+
+
+PixelInfo::PixelInfo(android::Canvas* canvas) {
+  memset(this, 0, sizeof(AwPixelInfo));
+  version = kAwPixelInfoVersion;
+  state = canvas->captureCanvasState();
+}
+
+PixelInfo::~PixelInfo() {
+  if (state)
+    SkCanvasStateUtils::ReleaseCanvasState(state);
+}
+
+AwPixelInfo* GetPixels(JNIEnv* env, jobject java_canvas) {
+  android::Canvas* nativeCanvas = GraphicsJNI::getNativeCanvas(env, java_canvas);
+  if (!nativeCanvas)
+    return NULL;
+
+  PixelInfo* pixels = new PixelInfo(nativeCanvas);
+  if (!pixels->state) {
+      delete pixels;
+      pixels = NULL;
+  }
+  return pixels;
+}
+
+void ReleasePixels(AwPixelInfo* pixels) {
+  delete static_cast<PixelInfo*>(pixels);
+}
+
+jlong GetDrawSWFunctionTable(JNIEnv* env, jclass) {
+  static AwDrawSWFunctionTable function_table;
+  function_table.version = kAwDrawSWFunctionTableVersion;
+  function_table.access_pixels = &GetPixels;
+  function_table.release_pixels = &ReleasePixels;
+  return reinterpret_cast<intptr_t>(&function_table);
+}
+
+jlong GetDrawGLFunctionTable(JNIEnv* env, jclass) {
+  static AwDrawGLFunctionTable function_table;
+  function_table.version = kAwDrawGLFunctionTableVersion;
+  function_table.create_graphic_buffer = &GraphicBufferImpl::Create;
+  function_table.release_graphic_buffer = &GraphicBufferImpl::Release;
+  function_table.map = &GraphicBufferImpl::MapStatic;
+  function_table.unmap = &GraphicBufferImpl::UnmapStatic;
+  function_table.get_native_buffer = &GraphicBufferImpl::GetNativeBufferStatic;
+  function_table.get_stride = &GraphicBufferImpl::GetStrideStatic;
+  return reinterpret_cast<intptr_t>(&function_table);
+}
+
+const char kClassName[] = "com/android/webview/chromium/GraphicsUtils";
+const JNINativeMethod kJniMethods[] = {
+    { "nativeGetDrawSWFunctionTable", "()J",
+        reinterpret_cast<void*>(GetDrawSWFunctionTable) },
+    { "nativeGetDrawGLFunctionTable", "()J",
+        reinterpret_cast<void*>(GetDrawGLFunctionTable) },
+};
+
+}  // namespace
+
+void RegisterGraphicsUtils(JNIEnv* env) {
+  jclass clazz = env->FindClass(kClassName);
+  LOG_ALWAYS_FATAL_IF(!clazz, "Unable to find class '%s'", kClassName);
+
+  int res = env->RegisterNatives(clazz, kJniMethods, NELEM(kJniMethods));
+  LOG_ALWAYS_FATAL_IF(res < 0, "register native methods failed: res=%d", res);
+}
+
+}  // namespace android
diff --git a/native/webview/plat_support/jni_entry_point.cpp b/native/webview/plat_support/jni_entry_point.cpp
new file mode 100644
index 0000000..4771be1
--- /dev/null
+++ b/native/webview/plat_support/jni_entry_point.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#define LOG_TAG "webviewchromium_plat_support"
+
+#include <jni.h>
+#include <utils/Log.h>
+
+namespace android {
+
+void RegisterDrawGLFunctor(JNIEnv* env);
+void RegisterGraphicsUtils(JNIEnv* env);
+
+}  // namespace android
+
+JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
+  JNIEnv* env = NULL;
+  jint ret = vm->AttachCurrentThread(&env, NULL);
+  LOG_ALWAYS_FATAL_IF(ret != JNI_OK, "AttachCurrentThread failed");
+  android::RegisterDrawGLFunctor(env);
+  android::RegisterGraphicsUtils(env);
+
+  return JNI_VERSION_1_4;
+}
diff --git a/packages/MtpDocumentsProvider/res/values-bs/strings.xml b/packages/MtpDocumentsProvider/res/values-bs/strings.xml
index 33323f8..18c2363 100644
--- a/packages/MtpDocumentsProvider/res/values-bs/strings.xml
+++ b/packages/MtpDocumentsProvider/res/values-bs/strings.xml
@@ -19,7 +19,7 @@
     <string name="app_label" msgid="6271216747302322594">"MTP Host"</string>
     <string name="downloads_app_label" msgid="7120690641874849726">"Preuzimanja"</string>
     <string name="root_name" msgid="5819495383921089536">"<xliff:g id="DEVICE_MODEL">%1$s</xliff:g> <xliff:g id="STORAGE_NAME">%2$s</xliff:g>"</string>
-    <string name="accessing_notification_title" msgid="3030133609230917944">"Pristupanje datotekama iz uređaja <xliff:g id="DEVICE_MODEL">%1$s</xliff:g>"</string>
+    <string name="accessing_notification_title" msgid="3030133609230917944">"Pristupanje fajlovima iz uređaja <xliff:g id="DEVICE_MODEL">%1$s</xliff:g>"</string>
     <string name="error_busy_device" msgid="3997316850357386589">"Drugi uređaj je zauzet. Nećete moći prenositi fajlove dok ne bude dostupan."</string>
     <string name="error_locked_device" msgid="7557872102188356147">"Fajlovi nisu pronađeni. Moguće je da je drugi uređaj zaključan. Ako jeste, otključajte ga i pokušajte ponovo."</string>
 </resources>
diff --git a/packages/PrintSpooler/res/values-as/strings.xml b/packages/PrintSpooler/res/values-as/strings.xml
new file mode 100644
index 0000000..2beaac8
--- /dev/null
+++ b/packages/PrintSpooler/res/values-as/strings.xml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4469836075319831821">"স্পুলাৰ প্ৰিণ্ট কৰক"</string>
+    <string name="more_options_button" msgid="2243228396432556771">"অধিক বিকল্প"</string>
+    <string name="label_destination" msgid="9132510997381599275">"লক্ষ্যস্থান"</string>
+    <string name="label_copies" msgid="3634531042822968308">"প্ৰতিলিপিসমূহ"</string>
+    <string name="label_copies_summary" msgid="3861966063536529540">"প্ৰতিলিপসমূহ:"</string>
+    <string name="label_paper_size" msgid="908654383827777759">"কাগজৰ আকাৰ"</string>
+    <string name="label_paper_size_summary" msgid="5668204981332138168">"কাগজৰ আকাৰ:"</string>
+    <string name="label_color" msgid="1108690305218188969">"ৰং"</string>
+    <!-- no translation found for label_duplex (5370037254347072243) -->
+    <skip />
+    <string name="label_orientation" msgid="2853142581990496477">"দিশ"</string>
+    <string name="label_pages" msgid="7768589729282182230">"পৃষ্ঠাসমূহ"</string>
+    <!-- no translation found for destination_default_text (5422708056807065710) -->
+    <skip />
+    <string name="template_all_pages" msgid="3322235982020148762">"সকলো <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
+    <string name="template_page_range" msgid="428638530038286328">"<xliff:g id="PAGE_COUNT">%1$s</xliff:g>ৰ পৰিসৰ"</string>
+    <string name="pages_range_example" msgid="8558694453556945172">"যেনে: ১—৫, ৮, ১১—১৩"</string>
+    <string name="print_preview" msgid="8010217796057763343">"প্ৰিণ্টৰ পূৰ্বদৰ্শন"</string>
+    <string name="install_for_print_preview" msgid="6366303997385509332">"পূৰ্বদৰ্শনৰ বাবে PDF ভিউৱাৰ ইনষ্টল কৰক"</string>
+    <string name="printing_app_crashed" msgid="854477616686566398">"প্ৰিণ্টিং এপ্ ক্ৰেশ্ব হৈছে"</string>
+    <string name="generating_print_job" msgid="3119608742651698916">"প্ৰিণ্টিং প্ৰস্তুত কৰি আছে"</string>
+    <string name="save_as_pdf" msgid="5718454119847596853">"PDF ৰূপে ছেভ কৰক"</string>
+    <string name="all_printers" msgid="5018829726861876202">"সকলো প্ৰিণ্টাৰ…"</string>
+    <string name="print_dialog" msgid="32628687461331979">"প্ৰিণ্ট সংবাদ"</string>
+    <!-- no translation found for current_page_template (5145005201131935302) -->
+    <skip />
+    <string name="page_description_template" msgid="6831239682256197161">"পৃষ্ঠা <xliff:g id="PAGE_COUNT">%2$d</xliff:g>ৰ <xliff:g id="CURRENT_PAGE">%1$d</xliff:g>"</string>
+    <string name="summary_template" msgid="8899734908625669193">"সাৰাংশ, প্ৰতিলিপিসমূহ <xliff:g id="COPIES">%1$s</xliff:g>, কাগজৰ আকাৰ <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
+    <string name="expand_handle" msgid="7282974448109280522">"হেণ্ডেল বিস্তাৰ কৰক"</string>
+    <string name="collapse_handle" msgid="6886637989442507451">"হেণ্ডেল সংকুচিত কৰক"</string>
+    <string name="print_button" msgid="645164566271246268">"প্ৰিণ্ট কৰক"</string>
+    <string name="savetopdf_button" msgid="2976186791686924743">"PDFৰ জৰিয়তে ছেভ কৰক"</string>
+    <string name="print_options_expanded" msgid="6944679157471691859">"প্ৰিণ্ট বিকল্পসমূহ বিস্তাৰ কৰা হ\'ল"</string>
+    <string name="print_options_collapsed" msgid="7455930445670414332">"প্ৰিণ্ট বিকল্পসমূহ সংকুচিত কৰা হ\'ল"</string>
+    <string name="search" msgid="5421724265322228497">"সন্ধান কৰক"</string>
+    <string name="all_printers_label" msgid="3178848870161526399">"সকলো প্ৰিণ্টাৰ"</string>
+    <string name="add_print_service_label" msgid="5356702546188981940">"সেৱা যোগ কৰক"</string>
+    <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"সন্ধান বাকচটো দেখুওৱা হ\'ল"</string>
+    <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"সন্ধান বাকচটো ঢাক খাই আছে"</string>
+    <string name="print_add_printer" msgid="1088656468360653455">"প্ৰিণ্টাৰ যোগ কৰক"</string>
+    <string name="print_select_printer" msgid="7388760939873368698">"প্ৰিণ্টাৰ বাছনি কৰক"</string>
+    <string name="print_forget_printer" msgid="5035287497291910766">"প্ৰিণ্টাৰ পাহৰি যাওক"</string>
+    <!-- no translation found for print_search_result_count_utterance (6997663738361080868) -->
+    <!-- no translation found for printer_extended_description_template (1366699227703381874) -->
+    <skip />
+    <!-- no translation found for printer_info_desc (7181988788991581654) -->
+    <skip />
+    <!-- no translation found for notification_channel_progress (872788690775721436) -->
+    <skip />
+    <!-- no translation found for notification_channel_failure (9042250774797916414) -->
+    <skip />
+    <!-- no translation found for could_not_create_file (3425025039427448443) -->
+    <skip />
+    <!-- no translation found for print_services_disabled_toast (9089060734685174685) -->
+    <skip />
+    <string name="print_searching_for_printers" msgid="6550424555079932867">"প্ৰিণ্টাৰৰ সন্ধান কৰি আছে"</string>
+    <!-- no translation found for print_no_print_services (8561247706423327966) -->
+    <skip />
+    <string name="print_no_printers" msgid="4869403323900054866">"প্ৰিণ্টাৰ পোৱা নগ\'ল"</string>
+    <!-- no translation found for cannot_add_printer (7840348733668023106) -->
+    <skip />
+    <!-- no translation found for select_to_add_printers (3800709038689830974) -->
+    <skip />
+    <!-- no translation found for enable_print_service (3482815747043533842) -->
+    <skip />
+    <!-- no translation found for enabled_services_title (7036986099096582296) -->
+    <skip />
+    <!-- no translation found for recommended_services_title (3799434882937956924) -->
+    <skip />
+    <!-- no translation found for disabled_services_title (7313253167968363211) -->
+    <skip />
+    <!-- no translation found for all_services_title (5578662754874906455) -->
+    <skip />
+    <!-- no translation found for print_services_recommendation_subtitle (5678487708807185138) -->
+    <string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> প্ৰিণ্ট কৰি থকা হৈছে"</string>
+    <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> বাতিল কৰি থকা হৈছে"</string>
+    <string name="failed_notification_title_template" msgid="2256217208186530973">"প্ৰিণ্টাৰৰ আসোঁৱাহ <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
+    <string name="blocked_notification_title_template" msgid="1175435827331588646">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> প্ৰিণ্টাৰে অৱৰোধ কৰিছে"</string>
+    <string name="cancel" msgid="4373674107267141885">"বাতিল কৰক"</string>
+    <string name="restart" msgid="2472034227037808749">"ৰিষ্টাৰ্ট কৰক"</string>
+    <string name="no_connection_to_printer" msgid="2159246915977282728">"প্ৰিণ্টাৰ সংযোগ হৈ থকা নাই"</string>
+    <string name="reason_unknown" msgid="5507940196503246139">"অজ্ঞাত"</string>
+    <!-- no translation found for print_service_security_warning_title (2160752291246775320) -->
+    <skip />
+    <!-- no translation found for print_service_security_warning_summary (1427434625361692006) -->
+    <skip />
+  <string-array name="color_mode_labels">
+    <item msgid="7602948745415174937">"ক\'লা আৰু বগা"</item>
+    <item msgid="2762241247228983754">"ৰং"</item>
+  </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"একো নাই"</item>
+    <item msgid="7296563835355641719">"দীঘল প্ৰান্ত"</item>
+    <item msgid="79513688117503758">"চুটি প্ৰান্ত"</item>
+  </string-array>
+  <string-array name="orientation_labels">
+    <item msgid="4061931020926489228">"প\'ৰ্ট্ৰেইট"</item>
+    <item msgid="3199660090246166812">"লেণ্ডস্কেইপ"</item>
+  </string-array>
+    <string name="print_write_error_message" msgid="5787642615179572543">"ফাইলত লিখিব পৰা নহ\'ল"</string>
+    <string name="print_error_default_message" msgid="8602678405502922346">"দুঃখিত, প্ৰিণ্টিঙৰ কাম নহ\'ল। পুনৰ চেষ্টা কৰক।"</string>
+    <string name="print_error_retry" msgid="1426421728784259538">"পুনৰ চেষ্টা কৰক"</string>
+    <string name="print_error_printer_unavailable" msgid="8985614415253203381">"এই প্ৰিণ্টাৰটো বৰ্তমান উপলব্ধ নহয়।"</string>
+    <!-- no translation found for print_cannot_load_page (6179560924492912009) -->
+    <skip />
+    <string name="print_preparing_preview" msgid="3939930735671364712">"পূৰ্বদৰ্শন প্ৰস্তুত কৰি আছে…"</string>
+</resources>
diff --git a/packages/PrintSpooler/res/values-or/strings.xml b/packages/PrintSpooler/res/values-or/strings.xml
new file mode 100644
index 0000000..e45c091
--- /dev/null
+++ b/packages/PrintSpooler/res/values-or/strings.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4469836075319831821">"ପ୍ରିଣ୍ଟ ସ୍ପୁଲର୍‍"</string>
+    <string name="more_options_button" msgid="2243228396432556771">"ଅଧିକ ବିକଳ୍ପ"</string>
+    <string name="label_destination" msgid="9132510997381599275">"ଲକ୍ଷ୍ୟସ୍ଥଳ"</string>
+    <string name="label_copies" msgid="3634531042822968308">"କପୀଗୁଡ଼ିକ"</string>
+    <string name="label_copies_summary" msgid="3861966063536529540">"କପୀଗୁଡ଼ିକ:"</string>
+    <string name="label_paper_size" msgid="908654383827777759">"କାଗଜର ଆକାର"</string>
+    <string name="label_paper_size_summary" msgid="5668204981332138168">"କାଗଜର ଆକାର:"</string>
+    <string name="label_color" msgid="1108690305218188969">"ରଙ୍ଗ"</string>
+    <!-- no translation found for label_duplex (5370037254347072243) -->
+    <skip />
+    <string name="label_orientation" msgid="2853142581990496477">"ଓରିଏଣ୍ଟେଶନ୍‍"</string>
+    <!-- no translation found for label_pages (7768589729282182230) -->
+    <skip />
+    <!-- no translation found for destination_default_text (5422708056807065710) -->
+    <skip />
+    <!-- no translation found for template_all_pages (3322235982020148762) -->
+    <skip />
+    <!-- no translation found for template_page_range (428638530038286328) -->
+    <skip />
+    <string name="pages_range_example" msgid="8558694453556945172">"ଯେପରିକି 1—5,8,11—13"</string>
+    <string name="print_preview" msgid="8010217796057763343">"ପ୍ରିଣ୍ଟ ଝଲକ"</string>
+    <string name="install_for_print_preview" msgid="6366303997385509332">"ଝଲକ ଦେଖିବା ପାଇଁ PDF ଭ୍ୟୁଅର୍‍ ଇନଷ୍ଟଲ୍‍ କରନ୍ତୁ"</string>
+    <string name="printing_app_crashed" msgid="854477616686566398">"ପ୍ରିଣ୍ଟିଙ୍ଗ ଆପ୍‍ କ୍ରାଶ୍‍ ହୋଇଗଲା"</string>
+    <string name="generating_print_job" msgid="3119608742651698916">"ପ୍ରିଣ୍ଟ ଜବ୍‍ ତିଆରି କରାଯାଉଛି"</string>
+    <string name="save_as_pdf" msgid="5718454119847596853">"PDF ଭାବରେ ସେଭ୍‍ କରନ୍ତୁ"</string>
+    <string name="all_printers" msgid="5018829726861876202">"ସମସ୍ତ ପ୍ରିଣ୍ଟର୍‌…"</string>
+    <string name="print_dialog" msgid="32628687461331979">"ପ୍ରିଣ୍ଟ ଡାୟଲଗ୍‍"</string>
+    <!-- no translation found for current_page_template (5145005201131935302) -->
+    <skip />
+    <!-- no translation found for page_description_template (6831239682256197161) -->
+    <skip />
+    <!-- no translation found for summary_template (8899734908625669193) -->
+    <skip />
+    <!-- no translation found for expand_handle (7282974448109280522) -->
+    <skip />
+    <!-- no translation found for collapse_handle (6886637989442507451) -->
+    <skip />
+    <string name="print_button" msgid="645164566271246268">"ପ୍ରିଣ୍ଟ କରନ୍ତୁ"</string>
+    <!-- no translation found for savetopdf_button (2976186791686924743) -->
+    <skip />
+    <!-- no translation found for print_options_expanded (6944679157471691859) -->
+    <skip />
+    <!-- no translation found for print_options_collapsed (7455930445670414332) -->
+    <skip />
+    <string name="search" msgid="5421724265322228497">"ସର୍ଚ୍ଚ କରନ୍ତୁ"</string>
+    <string name="all_printers_label" msgid="3178848870161526399">"ସମସ୍ତ ପ୍ରିଣ୍ଟର୍‌"</string>
+    <string name="add_print_service_label" msgid="5356702546188981940">"ସେବା ଯୋଡ଼ନ୍ତୁ"</string>
+    <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"ସର୍ଚ୍ଚ ବକ୍ସ ଦେଖାଯାଇଛି"</string>
+    <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"ସର୍ଚ୍ଚ ବକ୍ସ ଲୁଚି ରହିଛି"</string>
+    <string name="print_add_printer" msgid="1088656468360653455">"ପ୍ରିଣ୍ଟର୍‌ ଯୋଡ଼ନ୍ତୁ"</string>
+    <string name="print_select_printer" msgid="7388760939873368698">"ପ୍ରିଣ୍ଟର୍‍ ଚୟନ କରନ୍ତୁ"</string>
+    <string name="print_forget_printer" msgid="5035287497291910766">"ପ୍ରିଣ୍ଟର୍‍ ଭୁଲିଯାଆନ୍ତୁ"</string>
+    <!-- no translation found for print_search_result_count_utterance (6997663738361080868) -->
+    <!-- no translation found for printer_extended_description_template (1366699227703381874) -->
+    <skip />
+    <!-- no translation found for printer_info_desc (7181988788991581654) -->
+    <skip />
+    <!-- no translation found for notification_channel_progress (872788690775721436) -->
+    <skip />
+    <!-- no translation found for notification_channel_failure (9042250774797916414) -->
+    <skip />
+    <!-- no translation found for could_not_create_file (3425025039427448443) -->
+    <skip />
+    <!-- no translation found for print_services_disabled_toast (9089060734685174685) -->
+    <skip />
+    <string name="print_searching_for_printers" msgid="6550424555079932867">"ପ୍ରିଣ୍ଟର୍‌ ଖୋଜାଯାଉଛି"</string>
+    <!-- no translation found for print_no_print_services (8561247706423327966) -->
+    <skip />
+    <string name="print_no_printers" msgid="4869403323900054866">"କୌଣସି ପ୍ରିଣ୍ଟର୍‍ ମିଳିଲା ନାହିଁ"</string>
+    <!-- no translation found for cannot_add_printer (7840348733668023106) -->
+    <skip />
+    <!-- no translation found for select_to_add_printers (3800709038689830974) -->
+    <skip />
+    <!-- no translation found for enable_print_service (3482815747043533842) -->
+    <skip />
+    <!-- no translation found for enabled_services_title (7036986099096582296) -->
+    <skip />
+    <!-- no translation found for recommended_services_title (3799434882937956924) -->
+    <skip />
+    <!-- no translation found for disabled_services_title (7313253167968363211) -->
+    <skip />
+    <!-- no translation found for all_services_title (5578662754874906455) -->
+    <skip />
+    <!-- no translation found for print_services_recommendation_subtitle (5678487708807185138) -->
+    <string name="printing_notification_title_template" msgid="295903957762447362">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ପ୍ରିଣ୍ଟ କରାଯାଉଛି"</string>
+    <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> କ୍ୟାନ୍ସଲ୍‍ କରାଯାଉଛି"</string>
+    <string name="failed_notification_title_template" msgid="2256217208186530973">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ପ୍ରିଣ୍ଟର୍‍ ତ୍ରୁଟି"</string>
+    <string name="blocked_notification_title_template" msgid="1175435827331588646">"ପ୍ରିଣ୍ଟର୍‍ ଦ୍ୱାରା ରୋକାଯାଇଥିବା <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
+    <string name="cancel" msgid="4373674107267141885">"କ୍ୟାନ୍ସଲ୍‍"</string>
+    <string name="restart" msgid="2472034227037808749">"ରିଷ୍ଟାର୍ଟ କରନ୍ତୁ"</string>
+    <string name="no_connection_to_printer" msgid="2159246915977282728">"ପ୍ରିଣ୍ଟର୍‍କୁ କୌଣସି ସଂଯୋଗ ନାହିଁ"</string>
+    <string name="reason_unknown" msgid="5507940196503246139">"ଅଜଣା"</string>
+    <!-- no translation found for print_service_security_warning_title (2160752291246775320) -->
+    <skip />
+    <!-- no translation found for print_service_security_warning_summary (1427434625361692006) -->
+    <skip />
+  <string-array name="color_mode_labels">
+    <item msgid="7602948745415174937">"କଳା ଓ ଧଳା"</item>
+    <item msgid="2762241247228983754">"ରଙ୍ଗ"</item>
+  </string-array>
+  <string-array name="duplex_mode_labels">
+    <item msgid="3882302912790928315">"କିଛିନୁହେଁ"</item>
+    <item msgid="7296563835355641719">"ଲମ୍ବା ପ୍ରାନ୍ତ"</item>
+    <item msgid="79513688117503758">"ଛୋଟ ପ୍ରାନ୍ତ"</item>
+  </string-array>
+  <string-array name="orientation_labels">
+    <item msgid="4061931020926489228">"ପୋର୍ଟ୍ରେଟ୍"</item>
+    <item msgid="3199660090246166812">"ଲ୍ୟାଣ୍ଡସ୍କେପ୍"</item>
+  </string-array>
+    <string name="print_write_error_message" msgid="5787642615179572543">"ଫାଇଲ‍ରେ ଲେଖିପାରିଲା ନାହିଁ"</string>
+    <string name="print_error_default_message" msgid="8602678405502922346">"କାମ କଲାନାହିଁ, ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ"</string>
+    <string name="print_error_retry" msgid="1426421728784259538">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
+    <string name="print_error_printer_unavailable" msgid="8985614415253203381">"ପ୍ରିଣ୍ଟର୍‍ ବର୍ତ୍ତମାନ ଉପଲବ୍ଧ ନାହିଁ।"</string>
+    <!-- no translation found for print_cannot_load_page (6179560924492912009) -->
+    <skip />
+    <string name="print_preparing_preview" msgid="3939930735671364712">"ଝଲକ ତିଆରି କରାଯାଉଛି…"</string>
+</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index ae544dd..df1a7a8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -37,7 +37,6 @@
 import android.net.wifi.WifiNetworkScoreCache.CacheListener;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
 import android.os.SystemClock;
@@ -104,7 +103,6 @@
     // TODO: Allow control of this?
     // Combo scans can take 5-6s to complete - set to 10s.
     private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000;
-    private static final int NUM_SCANS_TO_CONFIRM_AP_LOSS = 3;
 
     private final Context mContext;
     private final WifiManager mWifiManager;
@@ -112,30 +110,38 @@
     private final ConnectivityManager mConnectivityManager;
     private final NetworkRequest mNetworkRequest;
     private final AtomicBoolean mConnected = new AtomicBoolean(false);
-    private final WifiListener mListener;
-    @VisibleForTesting WorkHandler mWorkHandler;
+    private final WifiListenerExecutor mListener;
+    @VisibleForTesting Handler mWorkHandler;
     private HandlerThread mWorkThread;
 
     private WifiTrackerNetworkCallback mNetworkCallback;
 
-    @GuardedBy("mLock")
-    private boolean mRegistered;
+    /**
+     * Synchronization lock for managing concurrency between main and worker threads.
+     *
+     * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints}.
+     */
+    private final Object mLock = new Object();
 
     /** The list of AccessPoints, aggregated visible ScanResults with metadata. */
     @GuardedBy("mLock")
     private final List<AccessPoint> mInternalAccessPoints = new ArrayList<>();
 
+    @GuardedBy("mLock")
+    private final Set<NetworkKey> mRequestedScores = new ArraySet<>();
+
     /**
-    * Synchronization lock for managing concurrency between main and worker threads.
-    *
-    * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints}.
-    */
-    private final Object mLock = new Object();
+     * Tracks whether fresh scan results have been received since scanning start.
+     *
+     * <p>If this variable is false, we will not evict the scan result cache or invoke callbacks
+     * so that we do not update the UI with stale data / clear out existing UI elements prematurely.
+     */
+    private boolean mStaleScanResults = true;
 
-    private final HashMap<String, Integer> mSeenBssids = new HashMap<>();
-
-    // TODO(sghuman): Change this to be keyed on AccessPoint.getKey
+    // Does not need to be locked as it only updated on the worker thread, with the exception of
+    // during onStart, which occurs before the receiver is registered on the work handler.
     private final HashMap<String, ScanResult> mScanResultCache = new HashMap<>();
+    private boolean mRegistered;
 
     private NetworkInfo mLastNetworkInfo;
     private WifiInfo mLastInfo;
@@ -145,21 +151,11 @@
     private boolean mNetworkScoringUiEnabled;
     private long mMaxSpeedLabelScoreCacheAge;
 
-    @GuardedBy("mLock")
-    private final Set<NetworkKey> mRequestedScores = new ArraySet<>();
+
 
     @VisibleForTesting
     Scanner mScanner;
 
-    /**
-     * Tracks whether fresh scan results have been received since scanning start.
-     *
-     * <p>If this variable is false, we will not evict the scan result cache or invoke callbacks
-     * so that we do not update the UI with stale data / clear out existing UI elements prematurely.
-     */
-    @GuardedBy("mLock")
-    private boolean mStaleScanResults = true;
-
     private static IntentFilter newIntentFilter() {
         IntentFilter filter = new IntentFilter();
         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
@@ -180,7 +176,7 @@
     @Deprecated
     public WifiTracker(Context context, WifiListener wifiListener,
             boolean includeSaved, boolean includeScans) {
-        this(context, wifiListener,
+        this(context, new WifiListenerExecutor(wifiListener),
                 context.getSystemService(WifiManager.class),
                 context.getSystemService(ConnectivityManager.class),
                 context.getSystemService(NetworkScoreManager.class),
@@ -191,7 +187,7 @@
     // calling apps once IC window is complete
     public WifiTracker(Context context, WifiListener wifiListener,
             @NonNull Lifecycle lifecycle, boolean includeSaved, boolean includeScans) {
-        this(context, wifiListener,
+        this(context, new WifiListenerExecutor(wifiListener),
                 context.getSystemService(WifiManager.class),
                 context.getSystemService(ConnectivityManager.class),
                 context.getSystemService(NetworkScoreManager.class),
@@ -200,13 +196,13 @@
     }
 
     @VisibleForTesting
-    WifiTracker(Context context, WifiListener wifiListener,
+    WifiTracker(Context context, WifiListenerExecutor wifiListenerExecutor,
             WifiManager wifiManager, ConnectivityManager connectivityManager,
             NetworkScoreManager networkScoreManager,
             IntentFilter filter) {
         mContext = context;
         mWifiManager = wifiManager;
-        mListener = new WifiListenerWrapper(wifiListener);
+        mListener = wifiListenerExecutor;
         mConnectivityManager = connectivityManager;
 
         // check if verbose logging developer option has been turned on or off
@@ -238,13 +234,11 @@
     // during construction
     void setWorkThread(HandlerThread workThread) {
         mWorkThread = workThread;
-        mWorkHandler = new WorkHandler(workThread.getLooper());
+        mWorkHandler = new Handler(workThread.getLooper());
         mScoreCache = new WifiNetworkScoreCache(mContext, new CacheListener(mWorkHandler) {
             @Override
             public void networkCacheUpdated(List<ScoredNetwork> networks) {
-                synchronized (mLock) {
-                    if (!mRegistered) return;
-                }
+                if (!mRegistered) return;
 
                 if (Log.isLoggable(TAG, Log.VERBOSE)) {
                     Log.v(TAG, "Score cache was updated with networks: " + networks);
@@ -259,25 +253,6 @@
         mWorkThread.quit();
     }
 
-    /** Synchronously update the list of access points with the latest information. */
-    @MainThread
-    public void forceUpdate() {
-        synchronized (mLock) {
-            mWorkHandler.removeMessages(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
-            mLastInfo = mWifiManager.getConnectionInfo();
-            mLastNetworkInfo = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
-
-            final List<ScanResult> newScanResults = mWifiManager.getScanResults();
-            if (isVerboseLoggingEnabled()) {
-                Log.i(TAG, "Fetched scan results: " + newScanResults);
-            }
-
-            List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
-            mInternalAccessPoints.clear();
-            updateAccessPointsLocked(newScanResults, configs);
-        }
-    }
-
     /**
      * Temporarily stop scanning for wifi networks.
      *
@@ -288,9 +263,7 @@
             mScanner.pause();
             mScanner = null;
         }
-        synchronized (mLock) {
-            mStaleScanResults = true;
-        }
+        mStaleScanResults = true;
     }
 
     /**
@@ -303,7 +276,6 @@
             mScanner = new Scanner();
         }
 
-        mWorkHandler.sendEmptyMessage(WorkHandler.MSG_RESUME);
         if (mWifiManager.isWifiEnabled()) {
             mScanner.resume();
         }
@@ -318,31 +290,46 @@
     @Override
     @MainThread
     public void onStart() {
-        synchronized (mLock) {
-            registerScoreCache();
+        // fetch current ScanResults instead of waiting for broadcast of fresh results
+        forceUpdate();
 
-            mNetworkScoringUiEnabled =
-                    Settings.Global.getInt(
-                            mContext.getContentResolver(),
-                            Settings.Global.NETWORK_SCORING_UI_ENABLED, 0) == 1;
+        registerScoreCache();
 
-            mMaxSpeedLabelScoreCacheAge =
-                    Settings.Global.getLong(
-                            mContext.getContentResolver(),
-                            Settings.Global.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS,
-                            DEFAULT_MAX_CACHED_SCORE_AGE_MILLIS);
+        mNetworkScoringUiEnabled =
+                Settings.Global.getInt(
+                        mContext.getContentResolver(),
+                        Settings.Global.NETWORK_SCORING_UI_ENABLED, 0) == 1;
 
-            resumeScanning();
-            if (!mRegistered) {
-                mContext.registerReceiver(mReceiver, mFilter);
-                // NetworkCallback objects cannot be reused. http://b/20701525 .
-                mNetworkCallback = new WifiTrackerNetworkCallback();
-                mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
-                mRegistered = true;
-            }
+        mMaxSpeedLabelScoreCacheAge =
+                Settings.Global.getLong(
+                        mContext.getContentResolver(),
+                        Settings.Global.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS,
+                        DEFAULT_MAX_CACHED_SCORE_AGE_MILLIS);
+
+        resumeScanning();
+        if (!mRegistered) {
+            mContext.registerReceiver(mReceiver, mFilter, null /* permission */, mWorkHandler);
+            // NetworkCallback objects cannot be reused. http://b/20701525 .
+            mNetworkCallback = new WifiTrackerNetworkCallback();
+            mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
+            mRegistered = true;
         }
     }
 
+
+    /**
+     * Synchronously update the list of access points with the latest information.
+     *
+     * <p>Intended to only be invoked within {@link #onStart()}.
+     */
+    @MainThread
+    private void forceUpdate() {
+        mLastInfo = mWifiManager.getConnectionInfo();
+        mLastNetworkInfo = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
+
+        fetchScansAndConfigsAndUpdateAccessPoints();
+    }
+
     private void registerScoreCache() {
         mNetworkScoreManager.registerNetworkScoreCache(
                 NetworkKey.TYPE_WIFI,
@@ -375,17 +362,15 @@
     @Override
     @MainThread
     public void onStop() {
-        synchronized (mLock) {
-            if (mRegistered) {
-                mContext.unregisterReceiver(mReceiver);
-                mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
-                mRegistered = false;
-            }
-            unregisterScoreCache();
-            pauseScanning(); // and set mStaleScanResults
-
-            mWorkHandler.removePendingMessages();
+        if (mRegistered) {
+            mContext.unregisterReceiver(mReceiver);
+            mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+            mRegistered = false;
         }
+        unregisterScoreCache();
+        pauseScanning(); // and set mStaleScanResults
+
+        mWorkHandler.removeCallbacksAndMessages(null /* remove all */);
     }
 
     private void unregisterScoreCache() {
@@ -409,9 +394,6 @@
      */
     @AnyThread
     public List<AccessPoint> getAccessPoints() {
-        // TODO(sghuman): Investigate how to eliminate or reduce the need for locking now that we
-        // have transitioned to a single worker thread model.
-
         synchronized (mLock) {
             return new ArrayList<>(mInternalAccessPoints);
         }
@@ -446,15 +428,10 @@
         }
     }
 
-    private void handleResume() {
-        // TODO(sghuman): Investigate removing this and replacing it with a cache eviction call
-        // instead.
-        mScanResultCache.clear();
-        mSeenBssids.clear();
-    }
-
-    private Collection<ScanResult> updateScanResultCache(final List<ScanResult> newResults) {
-        // TODO(sghuman): Delete this and replace it with the Map of Ap Keys to ScanResults
+    private ArrayMap<String, List<ScanResult>> updateScanResultCache(
+            final List<ScanResult> newResults) {
+        // TODO(sghuman): Delete this and replace it with the Map of Ap Keys to ScanResults for
+        // memory efficiency
         for (ScanResult newResult : newResults) {
             if (newResult.SSID == null || newResult.SSID.isEmpty()) {
                 continue;
@@ -467,10 +444,27 @@
             evictOldScans();
         }
 
-        // TODO(sghuman): Update a Map<ApKey, List<ScanResults>> variable to be reused later after
-        // double threads have been removed.
+        ArrayMap<String, List<ScanResult>> scanResultsByApKey = new ArrayMap<>();
+        for (ScanResult result : mScanResultCache.values()) {
+            // Ignore hidden and ad-hoc networks.
+            if (result.SSID == null || result.SSID.length() == 0 ||
+                    result.capabilities.contains("[IBSS]")) {
+                continue;
+            }
 
-        return mScanResultCache.values();
+            String apKey = AccessPoint.getKey(result);
+            List<ScanResult> resultList;
+            if (scanResultsByApKey.containsKey(apKey)) {
+                resultList = scanResultsByApKey.get(apKey);
+            } else {
+                resultList = new ArrayList<>();
+                scanResultsByApKey.put(apKey, resultList);
+            }
+
+            resultList.add(result);
+        }
+
+        return scanResultsByApKey;
     }
 
     /**
@@ -504,93 +498,59 @@
     }
 
     /**
-     * Safely modify {@link #mInternalAccessPoints} by acquiring {@link #mLock} first.
-     *
-     * <p>Will not perform the update if {@link #mStaleScanResults} is true
+     * Retrieves latest scan results and wifi configs, then calls
+     * {@link #updateAccessPoints(List, List)}.
      */
-    private void updateAccessPoints() {
-        List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+    private void fetchScansAndConfigsAndUpdateAccessPoints() {
         final List<ScanResult> newScanResults = mWifiManager.getScanResults();
         if (isVerboseLoggingEnabled()) {
             Log.i(TAG, "Fetched scan results: " + newScanResults);
         }
 
-        synchronized (mLock) {
-            if(!mStaleScanResults) {
-                updateAccessPointsLocked(newScanResults, configs);
-            }
-        }
+        List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+        updateAccessPoints(newScanResults, configs);
     }
 
-    /**
-     * Update the internal list of access points.
-     *
-     * <p>Do not call directly (except for forceUpdate), use {@link #updateAccessPoints()} which
-     * acquires the lock first.
-     */
-    @GuardedBy("mLock")
-    private void updateAccessPointsLocked(final List<ScanResult> newScanResults,
+    /** Update the internal list of access points. */
+    private void updateAccessPoints(final List<ScanResult> newScanResults,
             List<WifiConfiguration> configs) {
-        // TODO(sghuman): Reduce the synchronization time by only holding the lock when
-        // modifying lists exposed to operations on the MainThread (getAccessPoints, stopTracking,
-        // startTracking, etc).
 
-        WifiConfiguration connectionConfig = null;
-        if (mLastInfo != null) {
-            connectionConfig = getWifiConfigurationForNetworkId(
-                    mLastInfo.getNetworkId(), configs);
-        }
-
-        // Swap the current access points into a cached list.
-        List<AccessPoint> cachedAccessPoints = new ArrayList<>(mInternalAccessPoints);
-        ArrayList<AccessPoint> accessPoints = new ArrayList<>();
-
-        // Clear out the configs so we don't think something is saved when it isn't.
-        for (AccessPoint accessPoint : cachedAccessPoints) {
-            accessPoint.clearConfig();
-        }
-
-        final Collection<ScanResult> results = updateScanResultCache(newScanResults);
-
+        // Map configs and scan results necessary to make AccessPoints
         final Map<String, WifiConfiguration> configsByKey = new ArrayMap(configs.size());
         if (configs != null) {
             for (WifiConfiguration config : configs) {
                 configsByKey.put(AccessPoint.getKey(config), config);
             }
         }
+        ArrayMap<String, List<ScanResult>> scanResultsByApKey =
+                updateScanResultCache(newScanResults);
 
-        final List<NetworkKey> scoresToRequest = new ArrayList<>();
-        if (results != null) {
-            // TODO(sghuman): Move this loop to updateScanResultCache and make instance variable
-            // after double handlers are removed.
-            ArrayMap<String, List<ScanResult>> scanResultsByApKey = new ArrayMap<>();
-            for (ScanResult result : results) {
-                // Ignore hidden and ad-hoc networks.
-                if (result.SSID == null || result.SSID.length() == 0 ||
-                        result.capabilities.contains("[IBSS]")) {
-                    continue;
-                }
+        WifiConfiguration connectionConfig = null;
+        if (mLastInfo != null) {
+            // TODO(sghuman): Refactor to match config network id when updating configs below, and
+            // then update network info and wifi info only on match
+            connectionConfig = getWifiConfigurationForNetworkId(
+                    mLastInfo.getNetworkId(), configs);
+        }
 
-                NetworkKey key = NetworkKey.createFromScanResult(result);
-                if (key != null && !mRequestedScores.contains(key)) {
-                    scoresToRequest.add(key);
-                }
+        // Rather than dropping and reacquiring the lock multiple times in this method, we lock
+        // once for efficiency of lock acquisition time and readability
+        synchronized (mLock) {
+            // Swap the current access points into a cached list for maintaining AP listeners
+            List<AccessPoint> cachedAccessPoints;
+            cachedAccessPoints = new ArrayList<>(mInternalAccessPoints);
 
-                String apKey = AccessPoint.getKey(result);
-                List<ScanResult> resultList;
-                if (scanResultsByApKey.containsKey(apKey)) {
-                    resultList = scanResultsByApKey.get(apKey);
-                } else {
-                    resultList = new ArrayList<>();
-                    scanResultsByApKey.put(apKey, resultList);
-                }
+            ArrayList<AccessPoint> accessPoints = new ArrayList<>();
 
-                resultList.add(result);
-            }
+            final List<NetworkKey> scoresToRequest = new ArrayList<>();
 
             for (Map.Entry<String, List<ScanResult>> entry : scanResultsByApKey.entrySet()) {
-                // List can not be empty as it is dynamically constructed on each iteration
-                ScanResult firstResult = entry.getValue().get(0);
+                for (ScanResult result : entry.getValue()) {
+                    NetworkKey key = NetworkKey.createFromScanResult(result);
+                    if (key != null && !mRequestedScores.contains(key)) {
+                        scoresToRequest.add(key);
+                    }
+                }
 
                 AccessPoint accessPoint =
                         getCachedOrCreate(entry.getValue(), cachedAccessPoints);
@@ -599,46 +559,43 @@
                 }
 
                 // Update the matching config if there is one, to populate saved network info
-                WifiConfiguration config = configsByKey.get(entry.getKey());
-                if (config != null) {
-                    accessPoint.update(config);
-                }
+                accessPoint.update(configsByKey.get(entry.getKey()));
 
                 accessPoints.add(accessPoint);
             }
-        }
 
-        requestScoresForNetworkKeys(scoresToRequest);
-        for (AccessPoint ap : accessPoints) {
-            ap.update(mScoreCache, mNetworkScoringUiEnabled, mMaxSpeedLabelScoreCacheAge);
-        }
-
-        // Pre-sort accessPoints to speed preference insertion
-        Collections.sort(accessPoints);
-
-        // Log accesspoints that were deleted
-        if (DBG()) {
-            Log.d(TAG, "------ Dumping SSIDs that were not seen on this scan ------");
-            for (AccessPoint prevAccessPoint : mInternalAccessPoints) {
-                if (prevAccessPoint.getSsid() == null)
-                    continue;
-                String prevSsid = prevAccessPoint.getSsidStr();
-                boolean found = false;
-                for (AccessPoint newAccessPoint : accessPoints) {
-                    if (newAccessPoint.getSsidStr() != null && newAccessPoint.getSsidStr()
-                            .equals(prevSsid)) {
-                        found = true;
-                        break;
-                    }
-                }
-                if (!found)
-                    Log.d(TAG, "Did not find " + prevSsid + " in this scan");
+            requestScoresForNetworkKeys(scoresToRequest);
+            for (AccessPoint ap : accessPoints) {
+                ap.update(mScoreCache, mNetworkScoringUiEnabled, mMaxSpeedLabelScoreCacheAge);
             }
-            Log.d(TAG, "---- Done dumping SSIDs that were not seen on this scan ----");
-        }
 
-        mInternalAccessPoints.clear();
-        mInternalAccessPoints.addAll(accessPoints);
+            // Pre-sort accessPoints to speed preference insertion
+            Collections.sort(accessPoints);
+
+            // Log accesspoints that are being removed
+            if (DBG()) {
+                Log.d(TAG, "------ Dumping SSIDs that were not seen on this scan ------");
+                for (AccessPoint prevAccessPoint : mInternalAccessPoints) {
+                    if (prevAccessPoint.getSsid() == null)
+                        continue;
+                    String prevSsid = prevAccessPoint.getSsidStr();
+                    boolean found = false;
+                    for (AccessPoint newAccessPoint : accessPoints) {
+                        if (newAccessPoint.getSsidStr() != null && newAccessPoint.getSsidStr()
+                                .equals(prevSsid)) {
+                            found = true;
+                            break;
+                        }
+                    }
+                    if (!found)
+                        Log.d(TAG, "Did not find " + prevSsid + " in this scan");
+                }
+                Log.d(TAG, "---- Done dumping SSIDs that were not seen on this scan ----");
+            }
+
+            mInternalAccessPoints.clear();
+            mInternalAccessPoints.addAll(accessPoints);
+        }
 
         conditionallyNotifyListeners();
     }
@@ -659,21 +616,6 @@
         return accessPoint;
     }
 
-    @VisibleForTesting
-    AccessPoint getCachedOrCreate(WifiConfiguration config, List<AccessPoint> cache) {
-        final int N = cache.size();
-        for (int i = 0; i < N; i++) {
-            if (cache.get(i).matches(config)) {
-                AccessPoint ret = cache.remove(i);
-                ret.loadConfig(config);
-
-                return ret;
-            }
-        }
-        final AccessPoint accessPoint = new AccessPoint(mContext, config);
-        return accessPoint;
-    }
-
     private void updateNetworkInfo(NetworkInfo networkInfo) {
 
         /* Sticky broadcasts can call this when wifi is disabled */
@@ -739,7 +681,7 @@
         synchronized (mLock) {
             if (!mInternalAccessPoints.isEmpty()) {
                 mInternalAccessPoints.clear();
-                mListener.onAccessPointsChanged();
+                conditionallyNotifyListeners();
             }
         }
     }
@@ -767,126 +709,82 @@
         }
     }
 
+    /**
+     *  Receiver for handling broadcasts.
+     *
+     *  This receiver is registered on the WorkHandler.
+     */
     @VisibleForTesting
     final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            // No work should be performed in this Receiver, instead all operations should be passed
-            // off to the WorkHandler to avoid concurrent modification exceptions.
-
             String action = intent.getAction();
 
             if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
-                mWorkHandler.obtainMessage(
-                        WorkHandler.MSG_UPDATE_WIFI_STATE,
+                updateWifiState(
                         intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                            WifiManager.WIFI_STATE_UNKNOWN),
-                        0).sendToTarget();
+                                WifiManager.WIFI_STATE_UNKNOWN));
             } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
-                mWorkHandler
-                        .obtainMessage(
-                            WorkHandler.MSG_UPDATE_ACCESS_POINTS,
-                            WorkHandler.CLEAR_STALE_SCAN_RESULTS,
-                            0)
-                        .sendToTarget();
+                mStaleScanResults = false;
+
+                fetchScansAndConfigsAndUpdateAccessPoints();
             } else if (WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action)
                     || WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
-                mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
+                fetchScansAndConfigsAndUpdateAccessPoints();
             } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
+                // TODO(sghuman): Refactor these methods so they cannot result in duplicate
+                // onAccessPointsChanged updates being called from this intent.
                 NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-                mWorkHandler.obtainMessage(WorkHandler.MSG_UPDATE_NETWORK_INFO, info)
-                        .sendToTarget();
-                mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
+                updateNetworkInfo(info);
+                fetchScansAndConfigsAndUpdateAccessPoints();
             } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
                 NetworkInfo info =
                         mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
-                mWorkHandler.obtainMessage(WorkHandler.MSG_UPDATE_NETWORK_INFO, info)
-                        .sendToTarget();
+                updateNetworkInfo(info);
             }
         }
     };
 
+    /**
+     * Handles updates to WifiState.
+     *
+     * <p>If Wifi is not enabled in the enabled state, {@link #mStaleScanResults} will be set to
+     * true.
+     */
+    private void updateWifiState(int state) {
+        if (state == WifiManager.WIFI_STATE_ENABLED) {
+            if (mScanner != null) {
+                // We only need to resume if mScanner isn't null because
+                // that means we want to be scanning.
+                mScanner.resume();
+            }
+        } else {
+            clearAccessPointsAndConditionallyUpdate();
+            mLastInfo = null;
+            mLastNetworkInfo = null;
+            if (mScanner != null) {
+                mScanner.pause();
+            }
+            mStaleScanResults = true;
+        }
+        mListener.onWifiStateChanged(state);
+    }
+
     private final class WifiTrackerNetworkCallback extends ConnectivityManager.NetworkCallback {
         public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
             if (network.equals(mWifiManager.getCurrentNetwork())) {
+                // TODO(sghuman): Investigate whether this comment still holds true and if it makes
+                // more sense fetch the latest network info here:
+
                 // We don't send a NetworkInfo object along with this message, because even if we
                 // fetch one from ConnectivityManager, it might be older than the most recent
                 // NetworkInfo message we got via a WIFI_STATE_CHANGED broadcast.
-                mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_NETWORK_INFO);
+                mWorkHandler.post(() -> updateNetworkInfo(null));
             }
         }
     }
 
     @VisibleForTesting
-    final class WorkHandler extends Handler {
-        @VisibleForTesting static final int MSG_UPDATE_ACCESS_POINTS = 0;
-        private static final int MSG_UPDATE_NETWORK_INFO = 1;
-        private static final int MSG_RESUME = 2;
-        private static final int MSG_UPDATE_WIFI_STATE = 3;
-
-        private static final int CLEAR_STALE_SCAN_RESULTS = 1;
-
-        public WorkHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            // TODO(sghuman): Clean up synchronization to only be used when modifying collections
-            // exposed to the MainThread (through onStart, onStop, forceUpdate).
-            synchronized (mLock) {
-                processMessage(msg);
-            }
-        }
-
-        private void processMessage(Message msg) {
-            if (!mRegistered) return;
-
-            switch (msg.what) {
-                case MSG_UPDATE_ACCESS_POINTS:
-                    if (msg.arg1 == CLEAR_STALE_SCAN_RESULTS) {
-                        mStaleScanResults = false;
-                    }
-                    updateAccessPoints();
-                    break;
-                case MSG_UPDATE_NETWORK_INFO:
-                    updateNetworkInfo((NetworkInfo) msg.obj);
-                    break;
-                case MSG_RESUME:
-                    handleResume();
-                    break;
-                case MSG_UPDATE_WIFI_STATE:
-                    if (msg.arg1 == WifiManager.WIFI_STATE_ENABLED) {
-                        if (mScanner != null) {
-                            // We only need to resume if mScanner isn't null because
-                            // that means we want to be scanning.
-                            mScanner.resume();
-                        }
-                    } else {
-                        clearAccessPointsAndConditionallyUpdate();
-                        mLastInfo = null;
-                        mLastNetworkInfo = null;
-                        if (mScanner != null) {
-                            mScanner.pause();
-                        }
-                        synchronized (mLock) {
-                            mStaleScanResults = true;
-                        }
-                    }
-                    mListener.onWifiStateChanged(msg.arg1);
-                    break;
-            }
-        }
-
-        private void removePendingMessages() {
-            removeMessages(MSG_UPDATE_ACCESS_POINTS);
-            removeMessages(MSG_UPDATE_NETWORK_INFO);
-            removeMessages(MSG_RESUME);
-            removeMessages(MSG_UPDATE_WIFI_STATE);
-        }
-    }
-
-    @VisibleForTesting
     class Scanner extends Handler {
         static final int MSG_SCAN = 0;
 
@@ -944,19 +842,16 @@
     }
 
     /**
-     * Wraps the given {@link WifiListener} instance and executes it's methods on the Main Thread.
+     * Wraps the given {@link WifiListener} instance and executes its methods on the Main Thread.
      *
-     * <p>This mechanism allows us to no longer need a separate MainHandler and WorkHandler, which
-     * were previously both performing work, while avoiding errors which occur from executing
-     * callbacks which manipulate UI elements from a different thread than the MainThread.
+     * <p>Also logs all callbacks invocations when verbose logging is enabled.
      */
-    private static class WifiListenerWrapper implements WifiListener {
+    @VisibleForTesting
+    public static class WifiListenerExecutor implements WifiListener {
 
-        private final Handler mHandler;
         private final WifiListener mDelegatee;
 
-        public WifiListenerWrapper(WifiListener listener) {
-            mHandler = new Handler(Looper.getMainLooper());
+        public WifiListenerExecutor(WifiListener listener) {
             mDelegatee = listener;
         }
 
@@ -966,7 +861,7 @@
                 Log.i(TAG,
                         String.format("Invoking onWifiStateChanged callback with state %d", state));
             }
-            mHandler.post(() -> mDelegatee.onWifiStateChanged(state));
+            ThreadUtils.postOnMainThread(() -> mDelegatee.onWifiStateChanged(state));
         }
 
         @Override
@@ -974,7 +869,7 @@
             if (isVerboseLoggingEnabled()) {
                 Log.i(TAG, "Invoking onConnectedChanged callback");
             }
-            mHandler.post(() -> mDelegatee.onConnectedChanged());
+            ThreadUtils.postOnMainThread(() -> mDelegatee.onConnectedChanged());
         }
 
         @Override
@@ -982,10 +877,15 @@
             if (isVerboseLoggingEnabled()) {
                 Log.i(TAG, "Invoking onAccessPointsChanged callback");
             }
-            mHandler.post(() -> mDelegatee.onAccessPointsChanged());
+            ThreadUtils.postOnMainThread(() -> mDelegatee.onAccessPointsChanged());
         }
     }
 
+    /**
+     * WifiListener interface that defines callbacks indicating state changes in WifiTracker.
+     *
+     * <p>All callbacks are invoked on the MainThread.
+     */
     public interface WifiListener {
         /**
          * Called when the state of Wifi has changed, the state will be one of
@@ -1016,7 +916,7 @@
     }
 
     /**
-     * Invokes {@link WifiListenerWrapper#onAccessPointsChanged()} if {@link #mStaleScanResults}
+     * Invokes {@link WifiListenerExecutor#onAccessPointsChanged()} iif {@link #mStaleScanResults}
      * is false.
      */
     private void conditionallyNotifyListeners() {
@@ -1024,6 +924,6 @@
             return;
         }
 
-        ThreadUtils.postOnMainThread(() -> mListener.onAccessPointsChanged());
+        mListener.onAccessPointsChanged();
     }
 }
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 0c49bb6..8fd4700 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -21,18 +21,15 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.atMost;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -58,7 +55,6 @@
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -76,12 +72,10 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.BitSet;
-import java.util.Collections;
 import java.util.List;
-import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 // TODO(sghuman): Change these to robolectric tests b/35766684.
 
@@ -141,7 +135,7 @@
     @Mock private RssiCurve mockBadgeCurve1;
     @Mock private RssiCurve mockBadgeCurve2;
     @Mock private WifiManager mockWifiManager;
-    @Mock private WifiTracker.WifiListener mockWifiListener;
+    @Mock private WifiTracker.WifiListenerExecutor mockWifiListenerExecutor;
 
     private final List<NetworkKey> mRequestedKeys = new ArrayList<>();
 
@@ -153,6 +147,7 @@
 
     private int mOriginalScoringUiSettingValue;
 
+    @SuppressWarnings("VisibleForTests")
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -203,17 +198,14 @@
                     }
                 }).when(mockNetworkScoreManager).requestScores(Matchers.<NetworkKey[]>any());
 
-        doAnswer(
-                new Answer<Void>() {
-                  @Override
-                  public Void answer (InvocationOnMock invocation) throws Throwable {
+        // We use a latch to detect callbacks as Tracker initialization state often invokes
+        // callbacks
+        doAnswer(invocation -> {
                     if (mAccessPointsChangedLatch != null) {
                       mAccessPointsChangedLatch.countDown();
                     }
-
                     return null;
-                  }
-                }).when(mockWifiListener).onAccessPointsChanged();
+                }).when(mockWifiListenerExecutor).onAccessPointsChanged();
 
         // Turn on Scoring UI features
         mOriginalScoringUiSettingValue = Settings.Global.getInt(
@@ -271,8 +263,7 @@
             tracker.mReceiver.onReceive(mContext, intent);
         }
 
-        sendScanResultsAndProcess(tracker);
-        waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
+        sendScanResults(tracker);
 
         return tracker;
     }
@@ -280,7 +271,7 @@
     private WifiTracker createMockedWifiTracker() {
         final WifiTracker wifiTracker = new WifiTracker(
                 mContext,
-                mockWifiListener,
+                mockWifiListenerExecutor,
                 mockWifiManager,
                 mockConnectivityManager,
                 mockNetworkScoreManager,
@@ -291,26 +282,19 @@
 
     private void startTracking(WifiTracker tracker)  throws InterruptedException {
         CountDownLatch latch = new CountDownLatch(1);
-        mScannerHandler.post(new Runnable() {
-            @Override
-            public void run() {
+        mScannerHandler.post(() -> {
                 tracker.onStart();
                 latch.countDown();
-            }
         });
         assertTrue("Latch timed out", latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
     }
 
-    private void sendScanResultsAndProcess(WifiTracker tracker) throws InterruptedException {
-        mAccessPointsChangedLatch = new CountDownLatch(1);
+    private void sendScanResults(WifiTracker tracker) throws InterruptedException {
         Intent i = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
         tracker.mReceiver.onReceive(mContext, i);
-
-        assertTrue("Latch timed out",
-                mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
     }
 
-    private void updateScores() {
+    private void sendUpdatedScores() throws InterruptedException {
         Bundle attr1 = new Bundle();
         attr1.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, mockBadgeCurve1);
         ScoredNetwork sc1 =
@@ -356,12 +340,8 @@
 
     private void waitForHandlersToProcessCurrentlyEnqueuedMessages(WifiTracker tracker)
             throws InterruptedException {
-        // TODO(sghuman): This should no longer be necessary in a single work handler model
-
         CountDownLatch workerLatch = new CountDownLatch(1);
-        tracker.mWorkHandler.post(() -> {
-            workerLatch.countDown();
-        });
+        tracker.mWorkHandler.post(() -> workerLatch.countDown());
         assertTrue("Latch timed out while waiting for WorkerHandler",
                 workerLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
     }
@@ -381,7 +361,6 @@
         Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
         tracker.mReceiver.onReceive(mContext, intent);
-        waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
     }
 
     @Test
@@ -457,7 +436,6 @@
 
         mRequestScoresLatch = new CountDownLatch(1);
         startTracking(tracker);
-        tracker.forceUpdate();
         assertTrue("Latch timed out",
                 mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
 
@@ -470,7 +448,7 @@
             throws InterruptedException {
         // Start the tracker and inject the initial scan results and then stop tracking
         WifiTracker tracker =  createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
-        updateScoresAndWaitForAccessPointsChangedCallback(tracker);
+        updateScoresAndWaitForCacheListenerToProcess(tracker);
         tracker.onStop();
 
         assertThat(mScoreCacheCaptor.getValue().getScoredNetwork(NETWORK_KEY_1)).isNotNull();
@@ -481,19 +459,18 @@
             throws InterruptedException {
         WifiTracker tracker = createMockedWifiTracker();
         startTracking(tracker);
-        sendScanResultsAndProcess(tracker);
+        sendScanResults(tracker);
 
-        updateScoresAndWaitForAccessPointsChangedCallback(tracker);
+        updateScoresAndWaitForCacheListenerToProcess(tracker);
     }
 
-    private void updateScoresAndWaitForAccessPointsChangedCallback(WifiTracker tracker)
+    private void updateScoresAndWaitForCacheListenerToProcess(WifiTracker tracker)
             throws InterruptedException {
-        // Updating scores can happen together or one after the other, so the latch countdown is set
-        // to 2.
-        mAccessPointsChangedLatch = new CountDownLatch(1);
-        updateScores();
-        assertTrue("onAccessPointChanged was not called after updating scores",
-            mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
+        // Scores are updated via the cache listener hence we need to wait for the work handler
+        // to finish before proceeding.
+        sendUpdatedScores();
+
+        // Ensure the work handler has processed the scores inside the cache listener of WifiTracker
         waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
     }
 
@@ -505,7 +482,7 @@
         assertEquals(aps.get(0).getSsidStr(), SSID_1);
         assertEquals(aps.get(1).getSsidStr(), SSID_2);
 
-        updateScoresAndWaitForAccessPointsChangedCallback(tracker);
+        updateScoresAndWaitForCacheListenerToProcess(tracker);
 
         aps = tracker.getAccessPoints();
         assertTrue(aps.size() == 2);
@@ -527,7 +504,7 @@
         assertEquals(aps.get(0).getSsidStr(), SSID_1);
         assertEquals(aps.get(1).getSsidStr(), SSID_2);
 
-        updateScoresAndWaitForAccessPointsChangedCallback(tracker);
+        updateScoresAndWaitForCacheListenerToProcess(tracker);
 
         aps = tracker.getAccessPoints();
         assertTrue(aps.size() == 2);
@@ -535,12 +512,11 @@
         assertEquals(aps.get(1).getSsidStr(), SSID_2);
     }
 
-    @FlakyTest
     @Test
     public void scoreCacheUpdateScoresShouldInsertSpeedIntoAccessPoint()
             throws InterruptedException {
         WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
-        updateScoresAndWaitForAccessPointsChangedCallback(tracker);
+        updateScoresAndWaitForCacheListenerToProcess(tracker);
 
         List<AccessPoint> aps = tracker.getAccessPoints();
 
@@ -557,7 +533,7 @@
     public void scoreCacheUpdateMeteredShouldUpdateAccessPointMetering()
             throws InterruptedException {
         WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
-        updateScoresAndWaitForAccessPointsChangedCallback(tracker);
+        updateScoresAndWaitForCacheListenerToProcess(tracker);
 
         List<AccessPoint> aps = tracker.getAccessPoints();
 
@@ -579,7 +555,7 @@
                 0 /* disabled */);
 
         WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
-        updateScoresAndWaitForAccessPointsChangedCallback(tracker);
+        updateScoresAndWaitForCacheListenerToProcess(tracker);
 
         List<AccessPoint> aps = tracker.getAccessPoints();
 
@@ -617,7 +593,7 @@
                 .thenReturn(Arrays.asList(buildScanResult1(), buildScanResult2(), newResult));
 
         mRequestScoresLatch = new CountDownLatch(1);
-        sendScanResultsAndProcess(tracker);
+        sendScanResults(tracker);
         assertTrue(mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
 
         assertEquals(1, mRequestedKeys.size());
@@ -636,7 +612,7 @@
         // Verify listener is unregistered so updating a score does not throw an error by posting
         // a message to the dead work handler
         mWorkerThread.quit();
-        updateScores();
+        sendUpdatedScores();
     }
 
     /**
@@ -670,7 +646,8 @@
         when(mockWifiManager.getConfiguredNetworks())
                 .thenReturn(new ArrayList<WifiConfiguration>());
         when(mockWifiManager.getScanResults()).thenReturn(results);
-        tracker.forceUpdate();
+
+        startTracking(tracker);
     }
 
     @Test
@@ -682,26 +659,19 @@
         WifiInfo info = new WifiInfo(CONNECTED_AP_1_INFO);
         info.setRssi(newRssi);
 
-        CountDownLatch latch = new CountDownLatch(1);
-
         // Once the new info has been fetched, we need to wait for the access points to be copied
         mAccessPointsChangedLatch = new CountDownLatch(1);
-        doAnswer(invocation -> {
-                    latch.countDown();
-                    return info;
-                }).when(mockWifiManager).getConnectionInfo();
+        doAnswer(invocation -> info).when(mockWifiManager).getConnectionInfo();
 
         tracker.mReceiver.onReceive(mContext, new Intent(WifiManager.RSSI_CHANGED_ACTION));
-        assertTrue("New connection info never retrieved",
-                latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
+
         assertTrue("onAccessPointsChanged never called",
                 mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
-
         assertThat(tracker.getAccessPoints().get(0).getRssi()).isEqualTo(newRssi);
     }
 
     @Test
-    public void forceUpdateShouldSynchronouslyFetchLatestInformation() throws Exception {
+    public void onStartShouldSynchronouslyFetchLatestInformation() throws Exception {
         Network mockNetwork = mock(Network.class);
         when(mockWifiManager.getCurrentNetwork()).thenReturn(mockNetwork);
 
@@ -713,19 +683,20 @@
         when(mockConnectivityManager.getNetworkInfo(any(Network.class))).thenReturn(networkInfo);
 
         WifiTracker tracker = createMockedWifiTracker();
-        tracker.forceUpdate();
+        startTracking(tracker);
 
         verify(mockWifiManager).getConnectionInfo();
         verify(mockWifiManager, times(1)).getConfiguredNetworks();
         verify(mockConnectivityManager).getNetworkInfo(any(Network.class));
 
-        verify(mockWifiListener, never()).onAccessPointsChanged(); // mStaleAccessPoints is true
+        // mStaleAccessPoints is true
+        verify(mockWifiListenerExecutor, never()).onAccessPointsChanged();
         assertThat(tracker.getAccessPoints().size()).isEqualTo(2);
         assertThat(tracker.getAccessPoints().get(0).isActive()).isTrue();
     }
 
     @Test
-    public void stopTrackingShouldRemoveWifiListenerCallbacks() throws Exception {
+    public void stopTrackingShouldRemoveAllPendingWork() throws Exception {
         WifiTracker tracker = createMockedWifiTracker();
         startTracking(tracker);
 
@@ -743,25 +714,21 @@
         });
 
         // Enqueue messages
-        tracker.mWorkHandler.sendEmptyMessage(WifiTracker.WorkHandler.MSG_UPDATE_ACCESS_POINTS);
+        final AtomicBoolean executed = new AtomicBoolean(false);
+        tracker.mWorkHandler.post(() -> executed.set(true));
 
         try {
             ready.await(); // Make sure we have entered the first message handler
         } catch (InterruptedException e) {}
         tracker.onStop();
 
-        verify(mockWifiListener, atMost(1)).onAccessPointsChanged();
-        verify(mockWifiListener, atMost(1)).onConnectedChanged();
-        verify(mockWifiListener, atMost(1)).onWifiStateChanged(anyInt());
-
         lock.countDown();
         assertTrue("Latch timed out", latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
 
-        assertThat(tracker.mWorkHandler.hasMessages(
-                WifiTracker.WorkHandler.MSG_UPDATE_ACCESS_POINTS)).isFalse();
-        waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
+        // In case the method was already executing
+        assertThat(tracker.mWorkHandler.hasMessagesOrCallbacks()).isFalse();
 
-        verifyNoMoreInteractions(mockWifiListener);
+        assertThat(executed.get()).isFalse();
     }
 
     @Test
@@ -769,10 +736,8 @@
             throws Exception {
         WifiTracker tracker = createMockedWifiTracker();
         startTracking(tracker);
-        waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
 
         tracker.onStop();
-        waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
 
         startTracking(tracker);
 
@@ -782,11 +747,10 @@
         tracker.mReceiver.onReceive(
                 mContext, new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION));
 
-        waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
 
-        verify(mockWifiListener, never()).onAccessPointsChanged();
+        verify(mockWifiListenerExecutor, never()).onAccessPointsChanged();
 
-        sendScanResultsAndProcess(tracker); // verifies onAccessPointsChanged is invoked
+        sendScanResults(tracker); // verifies onAccessPointsChanged is invoked
     }
 
     @Test
@@ -794,7 +758,6 @@
             throws Exception {
         WifiTracker tracker = createMockedWifiTracker();
         startTracking(tracker);
-        waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
 
         tracker.mReceiver.onReceive(mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION));
         tracker.mReceiver.onReceive(
@@ -802,10 +765,9 @@
         tracker.mReceiver.onReceive(
                 mContext, new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION));
 
-        waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
-        verify(mockWifiListener, never()).onAccessPointsChanged();
+        verify(mockWifiListenerExecutor, never()).onAccessPointsChanged();
 
-        sendScanResultsAndProcess(tracker); // verifies onAccessPointsChanged is invoked
+        sendScanResults(tracker); // verifies onAccessPointsChanged is invoked
     }
 
     @Test
@@ -813,11 +775,10 @@
         WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected();
 
         when(mockWifiManager.isWifiEnabled()).thenReturn(false);
+
         mAccessPointsChangedLatch = new CountDownLatch(1);
         tracker.mReceiver.onReceive(mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION));
-
-        assertTrue(mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
-        waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
+        assertThat(mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
 
         assertThat(tracker.getAccessPoints()).isEmpty();
     }
@@ -825,7 +786,7 @@
     @Test
     public void onConnectedChangedCallback_shouldNotBeInvokedWhenNoStateChange() throws Exception {
         WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected();
-        verify(mockWifiListener, times(1)).onConnectedChanged();
+        verify(mockWifiListenerExecutor, times(1)).onConnectedChanged();
 
         NetworkInfo networkInfo = new NetworkInfo(
                 ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype");
@@ -835,14 +796,13 @@
         intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
         tracker.mReceiver.onReceive(mContext, intent);
 
-        waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
-        verify(mockWifiListener, times(1)).onConnectedChanged();
+        verify(mockWifiListenerExecutor, times(1)).onConnectedChanged();
     }
 
     @Test
     public void onConnectedChangedCallback_shouldBeInvokedWhenStateChanges() throws Exception {
         WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected();
-        verify(mockWifiListener, times(1)).onConnectedChanged();
+        verify(mockWifiListenerExecutor, times(1)).onConnectedChanged();
 
         NetworkInfo networkInfo = new NetworkInfo(
                 ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype");
@@ -853,9 +813,8 @@
         intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
         tracker.mReceiver.onReceive(mContext, intent);
 
-        waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
         assertThat(tracker.isConnected()).isFalse();
-        verify(mockWifiListener, times(2)).onConnectedChanged();
+        verify(mockWifiListenerExecutor, times(2)).onConnectedChanged();
     }
 
     @Test
diff --git a/packages/SettingsProvider/res/values-as/defaults.xml b/packages/SettingsProvider/res/values-as/defaults.xml
new file mode 100644
index 0000000..ea05c92
--- /dev/null
+++ b/packages/SettingsProvider/res/values-as/defaults.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+    <string name="def_device_name_simple" msgid="9037785625140748221">"%1$s"</string>
+    <string name="def_nfc_payment_component" msgid="5861297439873026958"></string>
+    <string name="def_backup_manager_constants" msgid="75273734665044867"></string>
+    <string name="def_backup_local_transport_parameters" msgid="303005414813191641"></string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-as/strings.xml b/packages/SettingsProvider/res/values-as/strings.xml
new file mode 100644
index 0000000..233253e
--- /dev/null
+++ b/packages/SettingsProvider/res/values-as/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4567566098528588863">"ছেটিংছসমূহৰ সঞ্চয়াগাৰ"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-or/defaults.xml b/packages/SettingsProvider/res/values-or/defaults.xml
new file mode 100644
index 0000000..ea05c92
--- /dev/null
+++ b/packages/SettingsProvider/res/values-or/defaults.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="def_device_name" msgid="6309317409634339402">"%1$s %2$s"</string>
+    <string name="def_device_name_simple" msgid="9037785625140748221">"%1$s"</string>
+    <string name="def_nfc_payment_component" msgid="5861297439873026958"></string>
+    <string name="def_backup_manager_constants" msgid="75273734665044867"></string>
+    <string name="def_backup_local_transport_parameters" msgid="303005414813191641"></string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-or/strings.xml b/packages/SettingsProvider/res/values-or/strings.xml
new file mode 100644
index 0000000..04f68c8
--- /dev/null
+++ b/packages/SettingsProvider/res/values-or/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/**
+ * Copyright (c) 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="4567566098528588863">"ସେଟିଙ୍ଗ ଷ୍ଟୋରେଜ୍‌"</string>
+</resources>
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
index 4b3afdc..6c31b2a 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
@@ -181,6 +181,5 @@
         void onScreenOff();
         void onShowSafetyWarning(int flags);
         void onAccessibilityModeChanged(Boolean showA11yStream);
-        void onConnectedDeviceChanged(String deviceName);
     }
 }
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
index 255e377..93df340 100644
--- a/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
+++ b/packages/SystemUI/res/drawable/ic_sysbar_rotate_button.xml
@@ -14,104 +14,110 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
-                 xmlns:aapt="http://schemas.android.com/aapt">
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
     <aapt:attr name="android:drawable">
-        <vector android:height="24dp"
+        <vector android:name="root"
                 android:width="24dp"
-                android:viewportHeight="102"
-                android:viewportWidth="102"
-                android:tint="?attr/singleToneColor">
-            <group android:name="_R_G">
-                <group android:name="_R_G_L_0_G" android:translateX="53.086" android:translateY="48.907000000000004" android:pivotX="-2.083" android:pivotY="2.083" android:rotation="90">
-                    <group android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0" android:rotation="100.1" android:scaleX="0.7979999999999999" android:scaleY="0.7979999999999999">
-                        <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M17.15 -37.84 C30.19,-31.91 39.52,-19.62 41.9,-4.86 C42.15,-3.39 43.45,-2.31 44.95,-2.31 C46.88,-2.31 48.34,-4.06 48.05,-5.94 C44.37,-27.64 27.64,-45.91 0.84,-48.09 C-1.08,-48.25 -2.17,-45.91 -0.83,-44.53 C-0.83,-44.53 9.87,-33.83 9.87,-33.83 C10.67,-33.04 11.92,-33.04 12.76,-33.79 C12.76,-33.79 17.15,-37.84 17.15,-37.84c "/>
-                    </group>
-                    <group android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0" android:rotation="87.2" android:scaleX="0.77" android:scaleY="0.77">
-                        <path android:name="_R_G_L_0_G_D_1_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-21.32 42.01 C-34.36,36.07 -43.68,23.78 -46.07,9.02 C-46.33,7.55 -47.62,6.47 -49.12,6.47 C-51.04,6.47 -52.51,8.23 -52.21,10.11 C-48.53,31.81 -31.81,50.08 -5.01,52.25 C-3.09,52.42 -2,50.08 -3.34,48.7 C-3.34,48.7 -14.04,38 -14.04,38 C-14.84,37.21 -16.11,37.19 -16.93,37.96 C-16.93,37.96 -21.32,42.01 -21.32,42.01c "/>
-                    </group>
-                    <path android:name="_R_G_L_0_G_D_2_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M40.77 9.4 C40.77,9.4 -9.4,-40.77 -9.4,-40.77 C-11.91,-43.28 -15.67,-43.28 -18.18,-40.77 C-18.18,-40.77 -44.94,-14.01 -44.94,-14.01 C-47.45,-11.5 -47.45,-7.74 -44.94,-5.23 C-44.94,-5.23 5.23,44.94 5.23,44.94 C7.74,47.45 11.51,47.45 14.01,44.94 C14.01,44.94 40.77,18.18 40.77,18.18 C43.28,15.67 43.28,11.91 40.77,9.4c  M3.85 34.82 C3.85,34.82 -34.4,-3.44 -34.4,-3.44 C-34.4,-3.44 -7.64,-30.19 -7.64,-30.19 C-7.64,-30.19 30.61,8.06 30.61,8.06 C30.61,8.06 3.85,34.82 3.85,34.82c "/>
-                </group>
+                android:height="24dp"
+                android:viewportWidth="24.0"
+                android:viewportHeight="24.0">
+            <group android:name="icon" android:pivotX="12" android:pivotY="12">
+                <!-- Tint color to be set directly -->
+                <path android:fillColor="#FFFFFFFF"
+                      android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14z"/>
             </group>
-            <group android:name="time_group"/>
         </vector>
     </aapt:attr>
 
-    <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+    <!-- Repeat all animations 3 times but don't fade out at the end -->
+    <target android:name="root">
         <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator android:propertyName="rotation" android:duration="333" android:startOffset="0" android:valueFrom="100.1" android:valueTo="0" android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
+            <set android:ordering="sequentially">
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
+                <!-- Linear fade out -->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="1700"
+                                android:valueFrom="1"
+                                android:valueTo="0"
+                                android:interpolator="@android:anim/linear_interpolator"/>
+                <!-- Linear fade in-->
+                <objectAnimator android:propertyName="alpha"
+                                android:duration="100"
+                                android:startOffset="100"
+                                android:valueFrom="0"
+                                android:valueTo="1"
+                                android:interpolator="@android:anim/linear_interpolator" />
             </set>
         </aapt:attr>
     </target>
-
-    <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+    <target android:name="icon">
         <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator android:propertyName="scaleX" android:duration="333" android:startOffset="0" android:valueFrom="0.798" android:valueTo="1" android:valueType="floatType">
+            <set android:ordering="sequentially">
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="100"
+                                android:duration="600"
+                                android:valueFrom="?attr/rotateButtonStartAngle"
+                                android:valueTo="?attr/rotateButtonEndAngle">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0"/>
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
-                <objectAnimator android:propertyName="scaleY" android:duration="333" android:startOffset="0" android:valueFrom="0.798" android:valueTo="1" android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
 
-    <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator android:propertyName="rotation" android:duration="333" android:startOffset="0" android:valueFrom="87.2" android:valueTo="0" android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="?attr/rotateButtonStartAngle"
+                                android:valueTo="?attr/rotateButtonStartAngle"/>
 
-    <target android:name="_R_G_L_0_G_D_1_P_0_G_0_T_0">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator android:propertyName="scaleX" android:duration="333" android:startOffset="0" android:valueFrom="0.77" android:valueTo="1" android:valueType="floatType">
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="?attr/rotateButtonStartAngle"
+                                android:valueTo="?attr/rotateButtonEndAngle">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0"/>
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
-                <objectAnimator android:propertyName="scaleY" android:duration="333" android:startOffset="0" android:valueFrom="0.77" android:valueTo="1" android:valueType="floatType">
-                    <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.667,1 1.0,1.0"/>
-                    </aapt:attr>
-                </objectAnimator>
-            </set>
-        </aapt:attr>
-    </target>
 
-    <target android:name="_R_G_L_0_G">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator android:propertyName="rotation" android:duration="333" android:startOffset="0" android:valueFrom="90" android:valueTo="0" android:valueType="floatType">
+                <!-- Reset rotation position for fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:startOffset="1300"
+                                android:duration="100"
+                                android:valueFrom="?attr/rotateButtonStartAngle"
+                                android:valueTo="?attr/rotateButtonStartAngle"/>
+
+                <!-- Icon rotation with start timing offset after fade in -->
+                <objectAnimator android:propertyName="rotation"
+                                android:duration="600"
+                                android:valueFrom="?attr/rotateButtonStartAngle"
+                                android:valueTo="?attr/rotateButtonEndAngle">
                     <aapt:attr name="android:interpolator">
-                        <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,1 1.0,1.0"/>
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.408,1.181 0.674,1.08 1.0,1.0"/>
                     </aapt:attr>
                 </objectAnimator>
             </set>
         </aapt:attr>
     </target>
-
-    <target android:name="time_group">
-        <aapt:attr name="android:animation">
-            <set android:ordering="together">
-                <objectAnimator android:propertyName="translateX" android:duration="517" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
-            </set>
-        </aapt:attr>
-    </target>
 </animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml b/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml
new file mode 100644
index 0000000..8d03ce7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/rounded_bg_bottom_background.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    <solid android:color="@*android:color/material_grey_200" />
+    <corners
+        android:bottomLeftRadius="@dimen/corner_size"
+        android:topLeftRadius="0dp"
+        android:bottomRightRadius="@dimen/corner_size"
+        android:topRightRadius="0dp"
+        />
+</shape>
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar.xml b/packages/SystemUI/res/layout/car_left_navigation_bar.xml
index 866b5a5..18301a8 100644
--- a/packages/SystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/SystemUI/res/layout/car_left_navigation_bar.xml
@@ -51,7 +51,7 @@
             android:id="@+id/hvac"
             android:layout_height="wrap_content"
             android:layout_width="match_parent"
-            systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end"
+            systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
             systemui:broadcast="true"
             android:src="@drawable/car_ic_hvac"
             android:background="?android:attr/selectableItemBackground"
diff --git a/packages/SystemUI/res/layout/car_navigation_bar.xml b/packages/SystemUI/res/layout/car_navigation_bar.xml
index 4666c60..9ff16a2 100644
--- a/packages/SystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/SystemUI/res/layout/car_navigation_bar.xml
@@ -49,7 +49,7 @@
             android:id="@+id/hvac"
             android:layout_height="match_parent"
             android:layout_width="wrap_content"
-            systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end"
+            systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
             systemui:broadcast="true"
             android:src="@drawable/car_ic_hvac"
             android:background="?android:attr/selectableItemBackground"
diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar.xml b/packages/SystemUI/res/layout/car_right_navigation_bar.xml
index 99ab802..99bd23c 100644
--- a/packages/SystemUI/res/layout/car_right_navigation_bar.xml
+++ b/packages/SystemUI/res/layout/car_right_navigation_bar.xml
@@ -51,7 +51,7 @@
             android:id="@+id/hvac"
             android:layout_height="wrap_content"
             android:layout_width="match_parent"
-            systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end"
+            systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
             systemui:broadcast="true"
             android:src="@drawable/car_ic_hvac"
             android:background="?android:attr/selectableItemBackground"
diff --git a/packages/SystemUI/res/layout/output_chooser.xml b/packages/SystemUI/res/layout/output_chooser.xml
deleted file mode 100644
index b9f7b15..0000000
--- a/packages/SystemUI/res/layout/output_chooser.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<!-- extends LinearLayout -->
-<com.android.systemui.HardwareUiLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:layout_marginBottom="0dp"
-    android:clipToPadding="false"
-    android:theme="@style/qs_theme"
-    android:clipChildren="false">
-    <com.android.systemui.volume.OutputChooserLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:sysui="http://schemas.android.com/apk/res-auto"
-        android:id="@+id/output_chooser"
-        android:layout_width="@dimen/output_chooser_panel_width"
-        android:layout_height="@dimen/output_chooser_panel_width"
-        android:layout_gravity="center_vertical|end"
-        android:orientation="vertical"
-        android:translationZ="8dp"
-        android:padding="20dp" >
-
-        <TextView
-            android:id="@+id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textDirection="locale"
-            android:textAppearance="@style/TextAppearance.QS.DetailHeader"
-            android:layout_marginBottom="20dp" />
-
-        <com.android.systemui.qs.AutoSizingList
-            android:id="@android:id/list"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:orientation="vertical"
-            sysui:itemHeight="@dimen/qs_detail_item_height"
-            style="@style/AutoSizingList"/>
-
-        <LinearLayout
-            android:id="@android:id/empty"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_gravity="center"
-            android:gravity="center"
-            android:orientation="vertical">
-
-            <TextView
-                android:id="@+id/empty_text"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:textDirection="locale"
-                android:layout_marginTop="20dp"
-                android:textAppearance="@style/TextAppearance.QS.DetailEmpty"/>
-        </LinearLayout>
-    </com.android.systemui.volume.OutputChooserLayout>
-</com.android.systemui.HardwareUiLayout>
diff --git a/packages/SystemUI/res/layout/output_chooser_item.xml b/packages/SystemUI/res/layout/output_chooser_item.xml
deleted file mode 100644
index c3ddbbe..0000000
--- a/packages/SystemUI/res/layout/output_chooser_item.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:minHeight="@dimen/qs_detail_item_height"
-              android:background="@drawable/btn_borderless_rect"
-              android:clickable="true"
-              android:focusable="true"
-              android:gravity="center_vertical"
-              android:orientation="horizontal" >
-
-    <ImageView
-        android:id="@android:id/icon"
-        android:layout_width="@dimen/qs_detail_item_icon_width"
-        android:layout_height="@dimen/qs_detail_item_icon_size"
-        android:layout_marginStart="@dimen/qs_detail_item_icon_marginStart"
-        android:layout_marginEnd="@dimen/qs_detail_item_icon_marginEnd"
-        android:background="?android:selectableItemBackgroundBorderless"
-        android:tint="?android:attr/textColorPrimary"/>
-
-    <LinearLayout
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="12dp"
-        android:layout_weight="1"
-        android:orientation="vertical" >
-
-        <TextView
-            android:id="@android:id/title"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:textDirection="locale"
-            android:ellipsize="end"
-            android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" />
-
-        <TextView
-            android:id="@android:id/summary"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:textDirection="locale"
-            android:layout_marginTop="2dp"
-            android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary" />
-    </LinearLayout>
-
-    <ImageView
-        android:id="@android:id/icon2"
-        style="@style/QSBorderlessButton"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:layout_marginStart="30dp"
-        android:clickable="true"
-        android:focusable="true"
-        android:scaleType="center"
-        android:contentDescription="@*android:string/media_route_controller_disconnect"
-        android:tint="?android:attr/textColorPrimary" />
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings_header.xml b/packages/SystemUI/res/layout/quick_settings_header.xml
new file mode 100644
index 0000000..43197c4
--- /dev/null
+++ b/packages/SystemUI/res/layout/quick_settings_header.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<com.android.systemui.qs.QSTooltipView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/qs_header_tooltip_height"
+    android:alpha="0"
+    android:gravity="center_horizontal|bottom"
+    android:visibility="invisible">
+
+    <TextView
+        android:id="@+id/header_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/quick_settings_header_onboarding_text"
+        android:textAppearance="@style/TextAppearance.QS.TileLabel"
+        android:textColor="?android:attr/colorAccent" />
+
+</com.android.systemui.qs.QSTooltipView>
diff --git a/packages/SystemUI/res/layout/rotate_suggestion.xml b/packages/SystemUI/res/layout/rotate_suggestion.xml
deleted file mode 100644
index 5074682..0000000
--- a/packages/SystemUI/res/layout/rotate_suggestion.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<com.android.systemui.statusbar.policy.KeyButtonView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/rotate_suggestion"
-    android:layout_width="@dimen/navigation_extra_key_width"
-    android:layout_height="match_parent"
-    android:layout_marginEnd="2dp"
-    android:visibility="invisible"
-    android:scaleType="centerInside"
-    android:contentDescription="@string/accessibility_rotate_button"
-/>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 803659f..b6d241b 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -23,79 +23,70 @@
     <!-- right-aligned to be physically near volume button -->
     <LinearLayout
         android:id="@+id/volume_dialog"
+        android:minWidth="@dimen/volume_dialog_panel_width"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical|right"
-        android:minWidth="@dimen/volume_dialog_panel_width"
         android:background="@android:color/transparent"
         android:layout_margin="@dimen/volume_dialog_base_margin"
         android:orientation="vertical"
         android:clipChildren="false" >
 
-        <LinearLayout
-            android:id="@+id/volume_dialog_rows"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:clipChildren="false"
-            android:clipToPadding="false"
-            android:background="@drawable/rounded_bg_full"
-            android:translationZ="@dimen/volume_panel_elevation"
-            android:orientation="horizontal" >
-                <!-- volume rows added and removed here! :-) -->
-        </LinearLayout>
-
         <FrameLayout
-            android:id="@+id/footer"
-            android:layout_width="@dimen/volume_dialog_panel_width"
-            android:layout_height="@dimen/volume_dialog_panel_width"
-            android:layout_marginTop="6dp"
-            android:layout_marginBottom="6dp"
-            android:layout_below="@id/volume_dialog_rows"
+            android:id="@+id/ringer"
+            android:layout_width="@dimen/volume_dialog_ringer_size"
+            android:layout_height="@dimen/volume_dialog_ringer_size"
+            android:layout_marginBottom="@dimen/volume_dialog_spacer"
+            android:elevation="@dimen/volume_panel_elevation"
+            android:layout_gravity="right"
             android:background="@drawable/rounded_bg_full">
-
-            <LinearLayout
-                android:id="@+id/footer_linear_layout"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:clipChildren="false"
-                android:clipToPadding="false"
-                android:gravity="center"
-                android:layout_gravity="end"
-                android:translationZ="@dimen/volume_panel_elevation"
-                android:clickable="true"
-                android:orientation="vertical" >
-
-                <TextView
-                    android:id="@+id/ringer_title"
-                    android:text="@string/ring_toggle_title"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:ellipsize="end"
-                    android:maxLines="1"
-                    android:layout_centerVertical="true"
-                    android:textColor="?android:attr/colorControlNormal"
-                    android:textAppearance="@style/TextAppearance.Volume.Header" />
-
-                <com.android.keyguard.AlphaOptimizedImageButton
-                    android:id="@+id/ringer_icon"
-                    style="@style/VolumeButtons"
-                    android:background="?android:selectableItemBackgroundBorderless"
-                    android:layout_width="@dimen/volume_dialog_panel_width"
-                    android:layout_height="@dimen/volume_button_size"
-                    android:tint="@color/accent_tint_color_selector"
-                    android:soundEffectsEnabled="false" />
-
-                <TextView
-                    android:id="@+id/ringer_status"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:ellipsize="end"
-                    android:maxLines="1"
-                    android:textColor="?android:attr/colorControlNormal"
-                    android:textAppearance="@style/TextAppearance.Volume.Header.Secondary" />
-            </LinearLayout>
+            <com.android.keyguard.AlphaOptimizedImageButton
+                android:id="@+id/ringer_icon"
+                style="@style/VolumeButtons"
+                android:background="?android:selectableItemBackgroundBorderless"
+                android:layout_width="@dimen/volume_dialog_tap_target_size"
+                android:layout_height="@dimen/volume_dialog_tap_target_size"
+                android:tint="@color/accent_tint_color_selector"
+                android:layout_gravity="center"
+                android:soundEffectsEnabled="false" />
 
             <include layout="@layout/volume_dnd_icon"/>
         </FrameLayout>
+
+        <LinearLayout
+            android:id="@+id/main"
+            android:layout_width="wrap_content"
+            android:minWidth="@dimen/volume_dialog_panel_width"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            android:background="@drawable/rounded_bg_full"
+            android:elevation="@dimen/volume_panel_elevation" >
+            <LinearLayout
+                android:id="@+id/volume_dialog_rows"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:minWidth="@dimen/volume_dialog_panel_width"
+                android:gravity="center"
+                android:orientation="horizontal" >
+                    <!-- volume rows added and removed here! :-) -->
+            </LinearLayout>
+            <FrameLayout
+                android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:background="@drawable/rounded_bg_bottom_background">
+                <com.android.keyguard.AlphaOptimizedImageButton
+                    android:id="@+id/settings"
+                    android:src="@drawable/ic_settings"
+                    android:layout_width="@dimen/volume_dialog_tap_target_size"
+                    android:layout_height="@dimen/volume_dialog_tap_target_size"
+                    android:layout_gravity="center"
+                    android:background="?android:selectableItemBackgroundBorderless"
+                    android:tint="#8A000000"
+                    android:soundEffectsEnabled="false" />
+            </FrameLayout>
+        </LinearLayout>
+
     </LinearLayout>
 </com.android.systemui.volume.VolumeUiLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index fb9355a..def6f6b 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -18,78 +18,37 @@
     android:tag="row"
     android:layout_height="wrap_content"
     android:layout_width="@dimen/volume_dialog_panel_width"
-    android:clipChildren="true"
-    android:clipToPadding="true"
+    android:clipChildren="false"
+    android:clipToPadding="false"
     android:theme="@style/qs_theme">
 
     <LinearLayout
-        android:layout_height="match_parent"
+        android:layout_height="wrap_content"
         android:layout_width="match_parent"
-        android:layout_marginTop="10dp"
-        android:layout_marginBottom="10dp"
+        android:layout_marginTop="@dimen/volume_dialog_slider_margin_top"
         android:gravity="center"
+        android:layout_gravity="center"
         android:orientation="vertical" >
-
-        <LinearLayout
-            android:orientation="vertical"
+        <TextView
+            android:id="@+id/volume_row_header"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:gravity="center"
-            android:padding="5dp">
-            <TextView
-                android:id="@+id/volume_row_header"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:ellipsize="end"
-                android:maxLength="10"
-                android:maxLines="1"
-                android:textColor="?android:attr/colorControlNormal"
-                android:textAppearance="@style/TextAppearance.Volume.Header" />
-            <LinearLayout
-                android:id="@+id/output_chooser"
-                android:orientation="vertical"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:minWidth="48dp"
-                android:minHeight="48dp"
-                android:paddingTop="10dp"
-                android:background="?android:selectableItemBackgroundBorderless"
-                android:gravity="center">
-                <TextView
-                    android:id="@+id/volume_row_connected_device"
-                    android:visibility="gone"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:maxLength="10"
-                    android:ellipsize="end"
-                    android:maxLines="1"
-                    android:textAppearance="@style/TextAppearance.Volume.Header.Secondary" />
-                <com.android.keyguard.AlphaOptimizedImageButton
-                    android:id="@+id/output_chooser_button"
-                    android:layout_width="24dp"
-                    android:layout_height="24dp"
-                    android:background="?android:selectableItemBackgroundBorderless"
-                    android:contentDescription="@string/accessibility_output_chooser"
-                    style="@style/VolumeButtons"
-                    android:clickable="false"
-                    android:layout_centerVertical="true"
-                    android:src="@drawable/ic_swap"
-                    android:soundEffectsEnabled="false"/>
-            </LinearLayout>
-        </LinearLayout>
+            android:ellipsize="end"
+            android:maxLength="10"
+            android:maxLines="1"
+            android:visibility="gone"
+            android:textColor="?android:attr/colorControlNormal"
+            android:textAppearance="@style/TextAppearance.Volume.Header" />
         <FrameLayout
             android:id="@+id/volume_row_slider_frame"
-            android:padding="0dp"
-            android:layout_width="@dimen/volume_dialog_panel_width"
+            android:layout_width="match_parent"
             android:layoutDirection="rtl"
-            android:layout_height="@dimen/volume_dialog_panel_width">
+            android:layout_height="@dimen/volume_dialog_slider_height">
             <SeekBar
                 android:id="@+id/volume_row_slider"
                 android:clickable="true"
-                android:padding="0dp"
-                android:layout_margin="0dp"
-                android:layout_width="@dimen/volume_dialog_panel_width"
-                android:layout_height="@dimen/volume_dialog_panel_width"
+                android:layout_width="@dimen/volume_dialog_slider_height"
+                android:layout_height="match_parent"
                 android:layoutDirection="rtl"
                 android:layout_gravity="center"
                 android:rotation="90" />
@@ -98,10 +57,10 @@
         <com.android.keyguard.AlphaOptimizedImageButton
             android:id="@+id/volume_row_icon"
             style="@style/VolumeButtons"
-            android:padding="10dp"
-            android:layout_width="@dimen/volume_button_size"
-            android:layout_height="@dimen/volume_button_size"
+            android:layout_width="@dimen/volume_dialog_tap_target_size"
+            android:layout_height="@dimen/volume_dialog_tap_target_size"
             android:background="?android:selectableItemBackgroundBorderless"
+            android:tint="@color/accent_tint_color_selector"
             android:soundEffectsEnabled="false" />
     </LinearLayout>
 
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index a5e37d5..13ca114 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -40,7 +40,7 @@
     <dimen name="battery_detail_graph_space_top">27dp</dimen>
     <dimen name="battery_detail_graph_space_bottom">27dp</dimen>
 
-    <dimen name="qs_tile_margin_top">16dp</dimen>
+    <dimen name="qs_tile_margin_top">32dp</dimen>
     <dimen name="qs_brightness_padding_top">6dp</dimen>
     <dimen name="qs_detail_margin_top">28dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index f0a5fe4..b11266a 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -136,5 +136,9 @@
         <attr name="singleLineButtonPaddingHorizontal" format="dimension" />
         <attr name="doubleLineButtonPaddingHorizontal" format="dimension" />
     </declare-styleable>
+
+    <!-- Used to style rotate suggestion button AVD animations -->
+    <attr name="rotateButtonStartAngle" format="float" />
+    <attr name="rotateButtonEndAngle" format="float" />
 </resources>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e679fcd..59c009f 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -263,12 +263,22 @@
     <dimen name="qs_panel_width">@dimen/notification_panel_width</dimen>
 
     <!-- the amount the volume panel should be offset at the end from the view next to it (or
-    the scren edge, in portrait-->
-    <dimen name="volume_dialog_base_margin">12dp</dimen>
+    the screen edge, in portrait-->
+    <dimen name="volume_dialog_base_margin">8dp</dimen>
 
-    <dimen name="volume_dialog_panel_width">100dp</dimen>
+    <dimen name="volume_dialog_panel_width">64dp</dimen>
 
-    <dimen name="output_chooser_panel_width">320dp</dimen>
+    <dimen name="volume_dialog_slider_height">101dp</dimen>
+
+    <dimen name="volume_dialog_row_height">252dp</dimen>
+
+    <dimen name="volume_dialog_ringer_size">64dp</dimen>
+
+    <dimen name="volume_dialog_tap_target_size">48dp</dimen>
+
+    <dimen name="volume_dialog_spacer">4dp</dimen>
+
+    <dimen name="volume_dialog_slider_margin_top">13dp</dimen>
 
     <!-- Gravity for the notification panel -->
     <integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top -->
@@ -290,7 +300,7 @@
 
     <dimen name="qs_tile_height">106dp</dimen>
     <dimen name="qs_tile_margin">19dp</dimen>
-    <dimen name="qs_tile_margin_top">16dp</dimen>
+    <dimen name="qs_tile_margin_top">32dp</dimen>
     <dimen name="qs_quick_tile_size">48dp</dimen>
     <dimen name="qs_quick_tile_padding">12dp</dimen>
     <dimen name="qs_header_gear_translation">16dp</dimen>
@@ -309,6 +319,7 @@
     <dimen name="qs_tile_padding_bottom">16dp</dimen>
     <dimen name="qs_tile_spacing">4dp</dimen>
     <dimen name="qs_panel_padding_bottom">0dp</dimen>
+    <dimen name="qs_panel_padding_top">32dp</dimen>
     <dimen name="qs_detail_header_height">56dp</dimen>
     <dimen name="qs_detail_header_padding">0dp</dimen>
     <dimen name="qs_detail_image_width">56dp</dimen>
@@ -333,6 +344,9 @@
     <dimen name="qs_detail_item_icon_width">32dp</dimen>
     <dimen name="qs_detail_item_icon_marginStart">0dp</dimen>
     <dimen name="qs_detail_item_icon_marginEnd">20dp</dimen>
+    <dimen name="qs_header_padding_start">16dp</dimen>
+    <dimen name="qs_header_padding_end">24dp</dimen>
+    <dimen name="qs_header_tooltip_height">32dp</dimen>
     <dimen name="qs_footer_padding_start">16dp</dimen>
     <dimen name="qs_footer_padding_end">24dp</dimen>
     <dimen name="qs_footer_icon_size">16dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 2d30f4c..0e92c60 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -47,7 +47,6 @@
     <item type="id" name="qs_icon_tag"/>
     <item type="id" name="qs_slash_tag"/>
     <item type="id" name="scrim"/>
-    <item type="id" name="scrim_target"/>
     <item type="id" name="scrim_alpha_start"/>
     <item type="id" name="scrim_alpha_end"/>
     <item type="id" name="notification_power"/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 0b5b7bd..920dd98 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -668,6 +668,8 @@
     <!-- Textual description of Ethernet connections -->
     <string name="ethernet_label">Ethernet</string>
 
+    <!-- QuickSettings: Onboarding text that introduces users to long press on an option in order to view the option's menu in Settings [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_header_onboarding_text">Press &amp; hold on the icons for more options</string>
     <!-- QuickSettings: Do not disturb [CHAR LIMIT=NONE] -->
     <string name="quick_settings_dnd_label">Do not disturb</string>
     <!-- QuickSettings: Do not disturb - Priority only [CHAR LIMIT=NONE] -->
@@ -1344,6 +1346,10 @@
 
     <string name="volume_dialog_title">%s volume controls</string>
 
+    <string name="volume_dialog_ringer_guidance_vibrate">Calls and notifications will vibrate</string>
+    <string name="volume_dialog_ringer_guidance_silent">Calls and notifications will be muted</string>
+    <string name="volume_dialog_ringer_guidance_ring">Calls and notifications will ring</string>
+
     <string name="output_title">Media output</string>
     <string name="output_calls_title">Phone call output</string>
     <string name="output_none_found">No devices found</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index d2ed4d1..a01f71a 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -485,4 +485,25 @@
         <item name="android:colorBackground">?android:attr/colorSecondary</item>
     </style>
 
+    <!-- Used to style rotate suggestion button AVD animations -->
+    <style name="RotateButtonCCWStart0">
+        <item name="rotateButtonStartAngle">0</item>
+        <item name="rotateButtonEndAngle">-90</item>
+    </style>
+
+    <style name="RotateButtonCCWStart90">
+        <item name="rotateButtonStartAngle">90</item>
+        <item name="rotateButtonEndAngle">0</item>
+    </style>
+
+    <style name="RotateButtonCWStart0">
+        <item name="rotateButtonStartAngle">0</item>
+        <item name="rotateButtonEndAngle">90</item>
+    </style>
+
+    <style name="RotateButtonCWStart90">
+        <item name="rotateButtonStartAngle">90</item>
+        <item name="rotateButtonEndAngle">180</item>
+    </style>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index adb4e33..ee573fb 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -30,26 +30,28 @@
 
     @Retention(RetentionPolicy.SOURCE)
     @StringDef({
-        Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME,
-        Key.DEBUG_MODE_ENABLED,
-        Key.HOTSPOT_TILE_LAST_USED,
-        Key.COLOR_INVERSION_TILE_LAST_USED,
-        Key.DND_TILE_VISIBLE,
-        Key.DND_TILE_COMBINED_ICON,
-        Key.DND_CONFIRMED_PRIORITY_INTRODUCTION,
-        Key.DND_CONFIRMED_SILENCE_INTRODUCTION,
-        Key.DND_FAVORITE_BUCKET_INDEX,
-        Key.DND_NONE_SELECTED,
-        Key.DND_FAVORITE_ZEN,
-        Key.QS_HOTSPOT_ADDED,
-        Key.QS_DATA_SAVER_ADDED,
-        Key.QS_DATA_SAVER_DIALOG_SHOWN,
-        Key.QS_INVERT_COLORS_ADDED,
-        Key.QS_WORK_ADDED,
-        Key.QS_NIGHTDISPLAY_ADDED,
-        Key.SEEN_MULTI_USER,
-        Key.NUM_APPS_LAUNCHED,
-        Key.HAS_SEEN_RECENTS_ONBOARDING,
+            Key.OVERVIEW_LAST_STACK_TASK_ACTIVE_TIME,
+            Key.DEBUG_MODE_ENABLED,
+            Key.HOTSPOT_TILE_LAST_USED,
+            Key.COLOR_INVERSION_TILE_LAST_USED,
+            Key.DND_TILE_VISIBLE,
+            Key.DND_TILE_COMBINED_ICON,
+            Key.DND_CONFIRMED_PRIORITY_INTRODUCTION,
+            Key.DND_CONFIRMED_SILENCE_INTRODUCTION,
+            Key.DND_FAVORITE_BUCKET_INDEX,
+            Key.DND_NONE_SELECTED,
+            Key.DND_FAVORITE_ZEN,
+            Key.QS_HOTSPOT_ADDED,
+            Key.QS_DATA_SAVER_ADDED,
+            Key.QS_DATA_SAVER_DIALOG_SHOWN,
+            Key.QS_INVERT_COLORS_ADDED,
+            Key.QS_WORK_ADDED,
+            Key.QS_NIGHTDISPLAY_ADDED,
+            Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
+            Key.SEEN_MULTI_USER,
+            Key.NUM_APPS_LAUNCHED,
+            Key.HAS_SEEN_RECENTS_ONBOARDING,
+            Key.SEEN_RINGER_GUIDANCE_COUNT
     })
     public @interface Key {
         @Deprecated
@@ -76,9 +78,15 @@
         String QS_WORK_ADDED = "QsWorkAdded";
         @Deprecated
         String QS_NIGHTDISPLAY_ADDED = "QsNightDisplayAdded";
+        /**
+         * Used for tracking how many times the user has seen the long press tooltip in the Quick
+         * Settings panel.
+         */
+        String QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT = "QsLongPressTooltipShownCount";
         String SEEN_MULTI_USER = "HasSeenMultiUser";
         String NUM_APPS_LAUNCHED = "NumAppsLaunched";
         String HAS_SEEN_RECENTS_ONBOARDING = "HasSeenRecentsOnboarding";
+        String SEEN_RINGER_GUIDANCE_COUNT = "RingerGuidanceCount";
     }
 
     public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index c3f7eb1..175cddc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -44,6 +44,7 @@
 
     public static final float EXPANDED_TILE_DELAY = .86f;
 
+
     private final ArrayList<View> mAllViews = new ArrayList<>();
     /**
      * List of {@link View}s representing Quick Settings that are being animated from the quick QS
@@ -65,6 +66,10 @@
     private TouchAnimator mNonfirstPageDelayedAnimator;
     private TouchAnimator mBrightnessAnimator;
 
+    /**
+     * Whether we're in the middle of animating between the collapsed and expanded states.
+     */
+    private boolean mIsAnimating;
     private boolean mOnKeyguard;
 
     private boolean mAllowFancy;
@@ -89,6 +94,9 @@
             Log.w(TAG, "QS Not using page layout");
         }
         panel.setPageListener(this);
+
+        // At time of creation, the QS panel is never animating.
+        mIsAnimating = false;
     }
 
     public void onRtlChanged() {
@@ -243,6 +251,11 @@
             } else {
                 mBrightnessAnimator = null;
             }
+            View headerView = mQsPanel.getHeaderView();
+            if (headerView!= null) {
+                firstPageBuilder.addFloat(headerView, "translationY", heightDiff, 0);
+                mAllViews.add(headerView);
+            }
             mFirstPageAnimator = firstPageBuilder
                     .setListener(this)
                     .build();
@@ -329,11 +342,21 @@
 
     @Override
     public void onAnimationAtStart() {
+        if (mIsAnimating) {
+            mQsPanel.onCollapse();
+        }
+        mIsAnimating = false;
+
         mQuickQsPanel.setVisibility(View.VISIBLE);
     }
 
     @Override
     public void onAnimationAtEnd() {
+        if (mIsAnimating) {
+            mQsPanel.onExpanded();
+        }
+        mIsAnimating = false;
+
         mQuickQsPanel.setVisibility(View.INVISIBLE);
         final int N = mQuickQsViews.size();
         for (int i = 0; i < N; i++) {
@@ -343,6 +366,11 @@
 
     @Override
     public void onAnimationStarted() {
+        if (!mIsAnimating) {
+            mQsPanel.onAnimating();
+        }
+        mIsAnimating = true;
+
         mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE);
         if (mOnFirstPage) {
             final int N = mQuickQsViews.size();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index cdcc5e6..476cb40 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
 
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -57,6 +58,7 @@
 public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener {
 
     public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
+    public static final String QS_SHOW_LONG_PRESS_TOOLTIP = "qs_show_long_press";
 
     protected final Context mContext;
     protected final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
@@ -72,6 +74,7 @@
     private BrightnessController mBrightnessController;
     protected QSTileHost mHost;
 
+    protected QSTooltipView mTooltipView;
     protected QSSecurityFooter mFooter;
     private boolean mGridContentVisible = true;
 
@@ -93,6 +96,11 @@
 
         setOrientation(VERTICAL);
 
+        mTooltipView = (QSTooltipView) LayoutInflater.from(mContext)
+                .inflate(R.layout.quick_settings_header, this, false);
+
+        addView(mTooltipView);
+
         mBrightnessView = LayoutInflater.from(mContext).inflate(
             R.layout.quick_settings_brightness_dialog, this, false);
         addView(mBrightnessView);
@@ -142,7 +150,10 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        Dependency.get(TunerService.class).addTunable(this, QS_SHOW_BRIGHTNESS);
+        final TunerService tunerService = Dependency.get(TunerService.class);
+        tunerService.addTunable(this, QS_SHOW_BRIGHTNESS);
+        tunerService.addTunable(this, QS_SHOW_LONG_PRESS_TOOLTIP);
+
         if (mHost != null) {
             setTiles(mHost.getTiles());
         }
@@ -174,11 +185,16 @@
     @Override
     public void onTuningChanged(String key, String newValue) {
         if (QS_SHOW_BRIGHTNESS.equals(key)) {
-            mBrightnessView.setVisibility(newValue == null || Integer.parseInt(newValue) != 0
-                    ? VISIBLE : GONE);
+            updateViewVisibilityForTuningValue(mBrightnessView, newValue);
+        } else if (QS_SHOW_LONG_PRESS_TOOLTIP.equals(key)) {
+            updateViewVisibilityForTuningValue(mTooltipView, newValue);
         }
     }
 
+    private void updateViewVisibilityForTuningValue(View view, @Nullable String newValue) {
+        view.setVisibility(newValue == null || Integer.parseInt(newValue) != 0 ? VISIBLE : GONE);
+    }
+
     public void openDetails(String subPanel) {
         QSTile tile = getTile(subPanel);
         showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0});
@@ -213,6 +229,10 @@
         return mBrightnessView;
     }
 
+    View getHeaderView() {
+        return mTooltipView;
+    }
+
     public void setCallback(QSDetail.Callback callback) {
         mCallback = callback;
     }
@@ -269,6 +289,21 @@
         if (mCustomizePanel != null && mCustomizePanel.isShown()) {
             mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
         }
+
+        // Instantly hide the header here since we don't want it to still be animating.
+        mTooltipView.setVisibility(View.INVISIBLE);
+    }
+
+    /**
+     * Called when the panel is fully animated out/expanded. This is different from the state
+     * tracked by {@link #mExpanded}, which only checks if the panel is even partially pulled out.
+     */
+    public void onExpanded() {
+        mTooltipView.fadeIn();
+    }
+
+    public void onAnimating() {
+        mTooltipView.fadeOut();
     }
 
     public void setExpanded(boolean expanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java
new file mode 100644
index 0000000..d1f9741
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java
@@ -0,0 +1,122 @@
+/*
+ * 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.systemui.qs;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.systemui.Prefs;
+
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Tooltip/header view for the Quick Settings panel.
+ */
+public class QSTooltipView extends LinearLayout {
+
+    private static final int FADE_ANIMATION_DURATION_MS = 300;
+    private static final long AUTO_FADE_OUT_DELAY_MS = TimeUnit.SECONDS.toMillis(6);
+    private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0;
+    public static final int MAX_TOOLTIP_SHOWN_COUNT = 3;
+
+    private final Handler mHandler = new Handler();
+    private final Runnable mAutoFadeOutRunnable = () -> fadeOut();
+
+    private int mShownCount;
+
+    public QSTooltipView(Context context) {
+        this(context, null);
+    }
+
+    public QSTooltipView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mShownCount = getStoredShownCount();
+    }
+
+    /** Returns the latest stored tooltip shown count from SharedPreferences. */
+    private int getStoredShownCount() {
+        return Prefs.getInt(
+                mContext,
+                Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
+                TOOLTIP_NOT_YET_SHOWN_COUNT);
+    }
+
+    /**
+     * Fades in the header view if we can show the tooltip - short circuits any running animation.
+     */
+    public void fadeIn() {
+        if (mShownCount < MAX_TOOLTIP_SHOWN_COUNT) {
+            animate().cancel();
+            setVisibility(View.VISIBLE);
+            animate()
+                    .alpha(1f)
+                    .setDuration(FADE_ANIMATION_DURATION_MS)
+                    .setListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            mHandler.postDelayed(mAutoFadeOutRunnable, AUTO_FADE_OUT_DELAY_MS);
+                        }
+                    })
+                    .start();
+
+            // Increment and drop the shown count in prefs for the next time we're deciding to
+            // fade in the tooltip. We first sanity check that the tooltip count hasn't changed yet
+            // in prefs (say, from a long press).
+            if (getStoredShownCount() <= mShownCount) {
+                Prefs.putInt(mContext, Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, ++mShownCount);
+            }
+        }
+    }
+
+    /**
+     * Fades out the header view if it's partially visible - short circuits any running animation.
+     */
+    public void fadeOut() {
+        animate().cancel();
+        if (getVisibility() == View.VISIBLE && getAlpha() != 0f) {
+            mHandler.removeCallbacks(mAutoFadeOutRunnable);
+            animate()
+                    .alpha(0f)
+                    .setDuration(FADE_ANIMATION_DURATION_MS)
+                    .setListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            perhapsMakeViewInvisible();
+                        }
+                    })
+                    .start();
+        } else {
+            perhapsMakeViewInvisible();
+        }
+    }
+
+    /**
+     * Only update visibility if the view is currently being shown. Otherwise, it's already been
+     * hidden by some other manner.
+     */
+    private void perhapsMakeViewInvisible() {
+        if (getVisibility() == View.VISIBLE) {
+            setVisibility(View.INVISIBLE);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index bdda3b9..f0684e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -123,9 +123,8 @@
 
     @Override
     public void onTuningChanged(String key, String newValue) {
-        // No tunings for you.
-        if (key.equals(QS_SHOW_BRIGHTNESS)) {
-            // No Brightness for you.
+        if (QS_SHOW_BRIGHTNESS.equals(key) || QS_SHOW_LONG_PRESS_TOOLTIP.equals(key)) {
+            // No Brightness or Tooltip for you!
             super.onTuningChanged(key, "0");
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 65135ab..23faa55 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -99,7 +99,11 @@
             record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight));
             previousView = record.tileView.updateAccessibilityOrder(previousView);
         }
-        int height = (mCellHeight + mCellMargin) * rows + (mCellMarginTop - mCellMargin);
+
+        // Only include the top margin in our measurement if we have more than 1 row to show.
+        // Otherwise, don't add the extra margin buffer at top.
+        int height = (mCellHeight + mCellMargin) * rows +
+                (rows != 0 ? (mCellMarginTop - mCellMargin) : 0);
         if (height < 0) height = 0;
         setMeasuredDimension(width, height);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
index 37f2528..6263efa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
@@ -107,7 +107,7 @@
         void onAnimationAtStart();
 
         /**
-         * Called when the animator moves into a position of "0". Start and end delays are
+         * Called when the animator moves into a position of "1". Start and end delays are
          * taken into account, so this position may cover a range of fractional inputs.
          */
         void onAnimationAtEnd();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 72592829..016cbd6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -42,6 +42,7 @@
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
+import com.android.systemui.Prefs;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
@@ -49,6 +50,7 @@
 import com.android.systemui.plugins.qs.QSTile.State;
 import com.android.systemui.qs.PagedTileLayout.TilePage;
 import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QSTooltipView;
 
 import java.util.ArrayList;
 
@@ -191,6 +193,11 @@
     public void longClick() {
         mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)));
         mHandler.sendEmptyMessage(H.LONG_CLICK);
+
+        Prefs.putInt(
+                mContext,
+                Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
+                QSTooltipView.MAX_TOOLTIP_SHOWN_COUNT);
     }
 
     public LogMaker populate(LogMaker logMaker) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 2723df7..bc2dff9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -82,7 +82,6 @@
 import com.android.systemui.statusbar.stack.AnimationProperties;
 import com.android.systemui.statusbar.stack.ExpandableViewState;
 import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
-import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackScrollState;
 
 import java.util.ArrayList;
@@ -181,6 +180,7 @@
     private AboveShelfChangedListener mAboveShelfChangedListener;
     private HeadsUpManager mHeadsUpManager;
     private View mHelperButton;
+    private boolean mChildIsExpanding;
 
     private boolean mJustClicked;
     private boolean mIconAnimationRunning;
@@ -575,8 +575,13 @@
      * @param isChildInGroup Is this notification now in a group
      * @param parent the new parent notification
      */
-    public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {;
+    public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {
         boolean childInGroup = StatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
+        if (mExpandAnimationRunning && !isChildInGroup && mNotificationParent != null) {
+            mNotificationParent.setChildIsExpanding(false);
+            mNotificationParent.setExtraWidthForClipping(0.0f);
+            mNotificationParent.setMinimumHeightForClipping(0);
+        }
         mNotificationParent = childInGroup ? parent : null;
         mPrivateLayout.setIsChildInGroup(childInGroup);
         mNotificationInflater.setIsChildInGroup(childInGroup);
@@ -651,10 +656,11 @@
                 visualStabilityManager, callback);
     }
 
-    public void getChildrenStates(StackScrollState resultState) {
+    public void getChildrenStates(StackScrollState resultState,
+            AmbientState ambientState) {
         if (mIsSummaryWithChildren) {
             ExpandableViewState parentState = resultState.getViewStateForView(this);
-            mChildrenContainer.getState(resultState, parentState);
+            mChildrenContainer.getState(resultState, parentState, ambientState);
         }
     }
 
@@ -1601,28 +1607,52 @@
         if (params == null) {
             return;
         }
-        setTranslationY(params.getTop());
         float zProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
                 params.getProgress(0, 50));
         float translationZ = MathUtils.lerp(params.getStartTranslationZ(),
                 mNotificationLaunchHeight,
                 zProgress);
         setTranslationZ(translationZ);
+        float extraWidthForClipping = params.getWidth() - getWidth()
+                + MathUtils.lerp(0, mOutlineRadius * 2, params.getProgress());
+        setExtraWidthForClipping(extraWidthForClipping);
+        int top = params.getTop();
+        float interpolation = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(params.getProgress());
+        int startClipTopAmount = params.getStartClipTopAmount();
+        if (mNotificationParent != null) {
+            top -= mNotificationParent.getTranslationY();
+            mNotificationParent.setTranslationZ(translationZ);
+            int parentStartClipTopAmount = params.getParentStartClipTopAmount();
+            if (startClipTopAmount != 0) {
+                int clipTopAmount = (int) MathUtils.lerp(parentStartClipTopAmount,
+                        parentStartClipTopAmount - startClipTopAmount,
+                        interpolation);
+                mNotificationParent.setClipTopAmount(clipTopAmount);
+            }
+            mNotificationParent.setExtraWidthForClipping(extraWidthForClipping);
+            mNotificationParent.setMinimumHeightForClipping(params.getHeight()
+                    + mNotificationParent.getActualHeight());
+        } else if (startClipTopAmount != 0) {
+            int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, interpolation);
+            setClipTopAmount(clipTopAmount);
+        }
+        setTranslationY(top);
         setActualHeight(params.getHeight());
+
         mBackgroundNormal.setExpandAnimationParams(params);
     }
 
     public void setExpandAnimationRunning(boolean expandAnimationRunning) {
+        View contentView;
+        if (mIsSummaryWithChildren) {
+            contentView =  mChildrenContainer;
+        } else {
+            contentView = getShowingLayout();
+        }
+        if (mGuts != null && mGuts.isExposed()) {
+            contentView = mGuts;
+        }
         if (expandAnimationRunning) {
-            View contentView;
-            if (mIsSummaryWithChildren) {
-                contentView =  mChildrenContainer;
-            } else {
-                contentView = getShowingLayout();
-            }
-            if (mGuts != null && mGuts.isExposed()) {
-                contentView = mGuts;
-            }
             contentView.animate()
                     .alpha(0f)
                     .setDuration(ActivityLaunchAnimator.ANIMATION_DURATION_FADE_CONTENT)
@@ -1637,15 +1667,35 @@
             if (mGuts != null) {
                 mGuts.setAlpha(1.0f);
             }
+            if (contentView != null) {
+                contentView.setAlpha(1.0f);
+            }
+            setExtraWidthForClipping(0.0f);
+            if (mNotificationParent != null) {
+                mNotificationParent.setExtraWidthForClipping(0.0f);
+                mNotificationParent.setMinimumHeightForClipping(0);
+            }
+        }
+        if (mNotificationParent != null) {
+            mNotificationParent.setChildIsExpanding(mExpandAnimationRunning);
         }
         updateChildrenVisibility();
         updateClipping();
         mBackgroundNormal.setExpandAnimationRunning(expandAnimationRunning);
     }
 
+    private void setChildIsExpanding(boolean isExpanding) {
+        mChildIsExpanding = isExpanding;
+    }
+
+    @Override
+    public boolean hasExpandingChild() {
+        return mChildIsExpanding;
+    }
+
     @Override
     protected boolean shouldClipToActualHeight() {
-        return super.shouldClipToActualHeight() && !mExpandAnimationRunning;
+        return super.shouldClipToActualHeight() && !mExpandAnimationRunning && !mChildIsExpanding;
     }
 
     @Override
@@ -1947,18 +1997,10 @@
         }
     }
 
-    private void updateMaxHeights() {
+    public void updateMaxHeights() {
         int intrinsicBefore = getIntrinsicHeight();
-        View expandedChild = mPrivateLayout.getExpandedChild();
-        if (expandedChild == null) {
-            expandedChild = mPrivateLayout.getContractedChild();
-        }
-        mMaxExpandHeight = expandedChild.getHeight();
-        View headsUpChild = mPrivateLayout.getHeadsUpChild();
-        if (headsUpChild == null) {
-            headsUpChild = mPrivateLayout.getContractedChild();
-        }
-        mHeadsUpHeight = headsUpChild.getHeight();
+        mMaxExpandHeight = mPrivateLayout.getExpandHeight();
+        mHeadsUpHeight = mPrivateLayout.getHeadsUpHeight();
         if (intrinsicBefore != getIntrinsicHeight()) {
             notifyHeightChanged(true  /* needsAnimation */);
         }
@@ -2245,7 +2287,7 @@
                 mGuts.setClipBottomAmount(clipBottomAmount);
             }
         }
-        if (mChildrenContainer != null) {
+        if (mChildrenContainer != null && !mChildIsExpanding) {
             // We have to update this even if it hasn't changed, since the children locations can
             // have changed
             mChildrenContainer.setClipBottomAmount(clipBottomAmount);
@@ -2453,7 +2495,7 @@
     public boolean isAboveShelf() {
         return !isOnKeyguard()
                 && (mIsPinned || mHeadsupDisappearRunning || (mIsHeadsUp && mAboveShelf)
-                || mExpandAnimationRunning);
+                || mExpandAnimationRunning || mChildIsExpanding);
     }
 
     public void setShowAmbient(boolean showAmbient) {
@@ -2478,7 +2520,7 @@
                 return true;
             }
         } else if (child == mChildrenContainer) {
-            if (isClippingNeeded() || !hasNoRounding()) {
+            if (!mChildIsExpanding && (isClippingNeeded() || !hasNoRounding())) {
                 return true;
             }
         } else if (child instanceof NotificationGuts) {
@@ -2544,11 +2586,19 @@
                 if (row.isExpandAnimationRunning()) {
                     return;
                 }
+                handleFixedTranslationZ(row);
                 super.applyToView(view);
                 row.applyChildrenState(mOverallState);
             }
         }
 
+        private void handleFixedTranslationZ(ExpandableNotificationRow row) {
+            if (row.hasExpandingChild()) {
+                zTranslation = row.getTranslationZ();
+                clipTopAmount = row.getClipTopAmount();
+            }
+        }
+
         @Override
         protected void onYTranslationAnimationFinished(View view) {
             super.onYTranslationAnimationFinished(view);
@@ -2567,6 +2617,7 @@
                 if (row.isExpandAnimationRunning()) {
                     return;
                 }
+                handleFixedTranslationZ(row);
                 super.animateTo(child, properties);
                 row.startChildAnimation(mOverallState, properties);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index 66b3a75..8bc2201 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -61,7 +61,7 @@
     private final Path mClipPath = new Path();
     private boolean mCustomOutline;
     private float mOutlineAlpha = -1f;
-    private float mOutlineRadius;
+    protected float mOutlineRadius;
     private boolean mAlwaysRoundBothCorners;
     private Path mTmpPath = new Path();
     private Path mTmpPath2 = new Path();
@@ -78,6 +78,8 @@
     protected boolean mShouldTranslateContents;
     private boolean mClipRoundedToClipTopAmount;
     private float mDistanceToTopRoundness = -1;
+    private float mExtraWidthForClipping;
+    private int mMinimumHeightForClipping = 0;
 
     private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
         @Override
@@ -202,11 +204,11 @@
         canvas.save();
         Path intersectPath = null;
         if (mClipRoundedToClipTopAmount) {
-            int left = 0;
+            int left = (int) (- mExtraWidthForClipping / 2.0f);
             int top = (int) (mClipTopAmount - mDistanceToTopRoundness);
-            int right = getWidth();
-            int bottom = (int) Math.max(getActualHeight() - mClipBottomAmount,
-                    top + mOutlineRadius);
+            int right = getWidth() + (int) (mExtraWidthForClipping + left);
+            int bottom = (int) Math.max(mMinimumHeightForClipping,
+                    Math.max(getActualHeight() - mClipBottomAmount, top + mOutlineRadius));
             ExpandableOutlineView.getRoundedRectPath(left, top, right, bottom, mOutlineRadius,
                     0.0f,
                     mClipPath);
@@ -234,6 +236,14 @@
         return result;
     }
 
+    public void setExtraWidthForClipping(float extraWidthForClipping) {
+        mExtraWidthForClipping = extraWidthForClipping;
+    }
+
+    public void setMinimumHeightForClipping(int minimumHeightForClipping) {
+        mMinimumHeightForClipping = minimumHeightForClipping;
+    }
+
     @Override
     public void setDistanceToTopRoundness(float distanceToTopRoundness) {
         super.setDistanceToTopRoundness(distanceToTopRoundness);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 1496a41..204adc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -543,6 +543,10 @@
         return false;
     }
 
+    public boolean hasExpandingChild() {
+        return false;
+    }
+
     /**
      * A listener notifying when {@link #getActualHeight} changes.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index c4d0b79..91960df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -143,6 +143,8 @@
     private int mClipBottomAmount;
     private boolean mIsLowPriority;
     private boolean mIsContentExpandable;
+    private boolean mRemoteInputVisible;
+    private int mUnrestrictedContentHeight;
 
 
     public NotificationContentView(Context context, AttributeSet attrs) {
@@ -293,6 +295,24 @@
         setMeasuredDimension(width, ownHeight);
     }
 
+    /**
+     * Get the extra height that needs to be added to the notification height for a given
+     * {@link RemoteInputView}.
+     * This is needed when the user is inline replying in order to ensure that the reply bar has
+     * enough padding.
+     *
+     * @param remoteInput The remote input to check.
+     * @return The extra height needed.
+     */
+    private int getExtraRemoteInputHeight(RemoteInputView remoteInput) {
+        if (remoteInput != null && remoteInput.getVisibility() == VISIBLE
+                && remoteInput.isActive()) {
+            return getResources().getDimensionPixelSize(
+                    com.android.internal.R.dimen.notification_content_margin);
+        }
+        return 0;
+    }
+
     private boolean updateContractedHeaderWidth() {
         // We need to update the expanded and the collapsed header to have exactly the same with to
         // have the expand buttons laid out at the same location.
@@ -538,19 +558,23 @@
     }
 
     public void setContentHeight(int contentHeight) {
-        mContentHeight = Math.max(Math.min(contentHeight, getHeight()), getMinHeight());
+        mUnrestrictedContentHeight = Math.max(contentHeight, getMinHeight());
+        int maxContentHeight = mContainingNotification.getIntrinsicHeight()
+                - getExtraRemoteInputHeight(mExpandedRemoteInput)
+                - getExtraRemoteInputHeight(mHeadsUpRemoteInput);
+        mContentHeight = Math.min(mUnrestrictedContentHeight, maxContentHeight);
         selectLayout(mAnimate /* animate */, false /* force */);
 
         int minHeightHint = getMinContentHeightHint();
 
         NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
         if (wrapper != null) {
-            wrapper.setContentHeight(mContentHeight, minHeightHint);
+            wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
         }
 
         wrapper = getVisibleWrapper(mTransformationStartVisibleType);
         if (wrapper != null) {
-            wrapper.setContentHeight(mContentHeight, minHeightHint);
+            wrapper.setContentHeight(mUnrestrictedContentHeight, minHeightHint);
         }
 
         updateClipping();
@@ -692,9 +716,9 @@
         if (mContainingNotification.isShowingAmbient()) {
             return getShowingAmbientView().getHeight();
         } else if (mExpandedChild != null) {
-            return mExpandedChild.getHeight();
+            return mExpandedChild.getHeight() + getExtraRemoteInputHeight(mExpandedRemoteInput);
         } else if (mIsHeadsUp && mHeadsUpChild != null && !mContainingNotification.isOnKeyguard()) {
-            return mHeadsUpChild.getHeight();
+            return mHeadsUpChild.getHeight() + getExtraRemoteInputHeight(mHeadsUpRemoteInput);
         }
         return mContractedChild.getHeight();
     }
@@ -746,7 +770,7 @@
     private void updateClipping() {
         if (mClipToActualHeight) {
             int top = (int) (mClipTopAmount - getTranslationY());
-            int bottom = (int) (mContentHeight - mClipBottomAmount - getTranslationY());
+            int bottom = (int) (mUnrestrictedContentHeight - mClipBottomAmount - getTranslationY());
             bottom = Math.max(top, bottom);
             mClipBounds.set(0, top, getWidth(), bottom);
             setClipBounds(mClipBounds);
@@ -790,7 +814,8 @@
                 }
                 NotificationViewWrapper visibleWrapper = getVisibleWrapper(visibleType);
                 if (visibleWrapper != null) {
-                    visibleWrapper.setContentHeight(mContentHeight, getMinContentHeightHint());
+                    visibleWrapper.setContentHeight(mUnrestrictedContentHeight,
+                            getMinContentHeightHint());
                 }
                 updateBackgroundColor(animate);
             }
@@ -1276,6 +1301,7 @@
                         mContext.getColor(R.color.remote_input_hint)));
 
                 existing.setWrapper(wrapper);
+                existing.setOnVisibilityChangedListener(this::setRemoteInputVisible);
 
                 if (existingPendingIntent != null || existing.isActive()) {
                     // The current action could be gone, or the pending intent no longer valid.
@@ -1576,4 +1602,31 @@
         }
         return null;
     }
+
+    public int getExpandHeight() {
+        View expandedChild = mExpandedChild;
+        if (expandedChild == null) {
+            expandedChild = mContractedChild;
+        }
+        return expandedChild.getHeight() + getExtraRemoteInputHeight(mExpandedRemoteInput);
+    }
+
+    public int getHeadsUpHeight() {
+        View headsUpChild = mHeadsUpChild;
+        if (headsUpChild == null) {
+            headsUpChild = mContractedChild;
+        }
+        return headsUpChild.getHeight()+ getExtraRemoteInputHeight(mHeadsUpRemoteInput);
+    }
+
+    public void setRemoteInputVisible(boolean remoteInputVisible) {
+        mRemoteInputVisible = remoteInputVisible;
+        setClipChildren(!remoteInputVisible);
+    }
+
+    @Override
+    public void setClipChildren(boolean clipChildren) {
+        clipChildren = clipChildren && !mRemoteInputVisible;
+        super.setClipChildren(clipChildren);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index ef44ad1..1d9cdf7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -42,8 +42,6 @@
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarWindowView;
 
-import java.util.function.Consumer;
-
 /**
  * A class that allows activities to be launched in a seamless way where the notification
  * transforms nicely into the starting window.
@@ -134,8 +132,24 @@
                         ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
                         mParams.startPosition = mSourceNotification.getLocationOnScreen();
                         mParams.startTranslationZ = mSourceNotification.getTranslationZ();
+                        mParams.startClipTopAmount = mSourceNotification.getClipTopAmount();
+                        if (mSourceNotification.isChildInGroup()) {
+                            int parentClip = mSourceNotification
+                                    .getNotificationParent().getClipTopAmount();
+                            mParams.parentStartClipTopAmount = parentClip;
+                            // We need to calculate how much the child is clipped by the parent
+                            // because children always have 0 clipTopAmount
+                            if (parentClip != 0) {
+                                float childClip = parentClip
+                                        - mSourceNotification.getTranslationY();
+                                if (childClip > 0.0f) {
+                                    mParams.startClipTopAmount = (int) Math.ceil(childClip);
+                                }
+                            }
+                        }
                         int targetWidth = app.sourceContainerBounds.width();
-                        int notificationHeight = mSourceNotification.getActualHeight();
+                        int notificationHeight = mSourceNotification.getActualHeight()
+                                - mSourceNotification.getClipBottomAmount();
                         int notificationWidth = mSourceNotification.getWidth();
                         anim.setDuration(ANIMATION_DURATION);
                         anim.setInterpolator(Interpolators.LINEAR);
@@ -241,6 +255,8 @@
         int top;
         int right;
         int bottom;
+        int startClipTopAmount;
+        int parentStartClipTopAmount;
 
         public ExpandAnimationParameters() {
         }
@@ -258,15 +274,32 @@
         }
 
         public int getTopChange() {
-            return Math.min(top - startPosition[1], 0);
+            // We need this compensation to ensure that the QS moves in sync.
+            int clipTopAmountCompensation = 0;
+            if (startClipTopAmount != 0.0f) {
+                clipTopAmountCompensation = (int) MathUtils.lerp(0, startClipTopAmount,
+                        Interpolators.FAST_OUT_SLOW_IN.getInterpolation(linearProgress));
+            }
+            return Math.min(top - startPosition[1] - clipTopAmountCompensation, 0);
         }
 
+        public float getProgress() {
+            return linearProgress;
+        }
 
         public float getProgress(long delay, long duration) {
             return MathUtils.constrain((linearProgress * ANIMATION_DURATION - delay)
                     / duration, 0.0f, 1.0f);
         }
 
+        public int getStartClipTopAmount() {
+            return startClipTopAmount;
+        }
+
+        public int getParentStartClipTopAmount() {
+            return parentStartClipTopAmount;
+        }
+
         public float getStartTranslationZ() {
             return startTranslationZ;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 79c605e..dc400e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -46,6 +46,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.Drawable;
 import android.inputmethodservice.InputMethodService;
 import android.os.Binder;
 import android.os.Bundle;
@@ -77,8 +78,8 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.LatencyTracker;
 import com.android.systemui.Dependency;
-import com.android.systemui.OverviewProxyService;
 import com.android.systemui.Interpolators;
+import com.android.systemui.OverviewProxyService;
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.assist.AssistManager;
@@ -111,6 +112,9 @@
     private static final boolean DEBUG = false;
     private static final String EXTRA_DISABLE_STATE = "disabled_state";
 
+    private final static int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
+    private final static int ROTATE_BUTTON_LOOP_DURATION_MS = 2000;
+
     /** Allow some time inbetween the long press for back and recents. */
     private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
 
@@ -150,8 +154,7 @@
     private RotationLockController mRotationLockController;
     private TaskStackListenerImpl mTaskStackListener;
 
-    private final Runnable mRemoveRotationProposal = () -> safeSetRotationButtonState(false);
-    private Animator mRotateShowAnimator;
+    private final Runnable mRemoveRotationProposal = () -> setRotateSuggestionButtonState(false);
     private Animator mRotateHideAnimator;
 
     private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
@@ -364,29 +367,130 @@
         // rotate button if shown.
 
         if (!isValid) {
-            safeSetRotationButtonState(false);
+            setRotateSuggestionButtonState(false);
             return;
         }
 
-        if (rotation == mWindowManager.getDefaultDisplay().getRotation()) {
+        final int winRotation = mWindowManager.getDefaultDisplay().getRotation();
+        if (rotation == winRotation) {
             // Use this as a signal to remove any current suggestions
             getView().getHandler().removeCallbacks(mRemoveRotationProposal);
-            safeSetRotationButtonState(false);
+            setRotateSuggestionButtonState(false);
         } else {
             mLastRotationSuggestion = rotation; // Remember rotation for click
-            safeSetRotationButtonState(true);
+
+            // Update the icon style to change animation parameters
+            if (mNavigationBarView != null) {
+                final boolean rotationCCW = isRotationAnimationCCW(winRotation, rotation);
+                int style;
+                if (winRotation == Surface.ROTATION_0 || winRotation == Surface.ROTATION_180) {
+                    style = rotationCCW ? R.style.RotateButtonCCWStart90 :
+                            R.style.RotateButtonCWStart90;
+                } else { // 90 or 270
+                    style = rotationCCW ? R.style.RotateButtonCCWStart0 :
+                            R.style.RotateButtonCWStart0;
+                }
+                mNavigationBarView.updateRotateSuggestionButtonStyle(style, true);
+            }
+
+            setRotateSuggestionButtonState(true);
             rescheduleRotationTimeout(false);
             mMetricsLogger.visible(MetricsEvent.ROTATION_SUGGESTION_SHOWN);
         }
     }
 
-    private void safeSetRotationButtonState(boolean vis) {
-        if (mNavigationBarView != null) mNavigationBarView.setRotateSuggestionButtonState(vis);
+    private boolean isRotationAnimationCCW(int from, int to) {
+        // All 180deg WM rotation animations are CCW, match that
+        if (from == Surface.ROTATION_0 && to == Surface.ROTATION_90) return false;
+        if (from == Surface.ROTATION_0 && to == Surface.ROTATION_180) return true; //180d so CCW
+        if (from == Surface.ROTATION_0 && to == Surface.ROTATION_270) return true;
+        if (from == Surface.ROTATION_90 && to == Surface.ROTATION_0) return true;
+        if (from == Surface.ROTATION_90 && to == Surface.ROTATION_180) return false;
+        if (from == Surface.ROTATION_90 && to == Surface.ROTATION_270) return true; //180d so CCW
+        if (from == Surface.ROTATION_180 && to == Surface.ROTATION_0) return true; //180d so CCW
+        if (from == Surface.ROTATION_180 && to == Surface.ROTATION_90) return true;
+        if (from == Surface.ROTATION_180 && to == Surface.ROTATION_270) return false;
+        if (from == Surface.ROTATION_270 && to == Surface.ROTATION_0) return false;
+        if (from == Surface.ROTATION_270 && to == Surface.ROTATION_90) return true; //180d so CCW
+        if (from == Surface.ROTATION_270 && to == Surface.ROTATION_180) return true;
+        return false; // Default
     }
 
-    private void safeSetRotationButtonState(boolean vis, boolean force) {
-        if (mNavigationBarView != null) {
-            mNavigationBarView.setRotateSuggestionButtonState(vis, force);
+    public void setRotateSuggestionButtonState(final boolean visible) {
+        setRotateSuggestionButtonState(visible, false);
+    }
+
+    public void setRotateSuggestionButtonState(final boolean visible, final boolean force) {
+        if (mNavigationBarView == null) return;
+
+        // At any point the the button can become invisible because an a11y service became active.
+        // Similarly, a call to make the button visible may be rejected because an a11y service is
+        // active. Must account for this.
+
+        ButtonDispatcher rotBtn = mNavigationBarView.getRotateSuggestionButton();
+        final boolean currentlyVisible = mNavigationBarView.isRotateButtonVisible();
+
+        // Rerun a show animation to indicate change but don't rerun a hide animation
+        if (!visible && !currentlyVisible) return;
+
+        View view = rotBtn.getCurrentView();
+        if (view == null) return;
+
+        KeyButtonDrawable kbd = rotBtn.getImageDrawable();
+        if (kbd == null) return;
+
+        // The KBD and AVD is recreated every new valid suggestion because of style changes.
+        AnimatedVectorDrawable animIcon = null;
+        if (kbd.getDrawable(0) instanceof AnimatedVectorDrawable) {
+            animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0);
+        }
+
+        if (visible) { // Appear and change (cannot force)
+            // Stop any currently running hide animations
+            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
+                mRotateHideAnimator.pause();
+            }
+
+            // Reset the alpha if any has changed due to hide animation
+            view.setAlpha(1f);
+
+            // Run the rotate icon's animation if it has one
+            if (animIcon != null) {
+                animIcon.reset();
+                animIcon.start();
+            }
+
+            // Set visibility, may fail if a11y service is active.
+            // If invisible, call will stop animation.
+            mNavigationBarView.setRotateButtonVisibility(true);
+
+        } else { // Hide
+
+            if (force) {
+                // If a hide animator is running stop it and make invisible
+                if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
+                    mRotateHideAnimator.pause();
+                }
+                mNavigationBarView.setRotateButtonVisibility(false);
+                return;
+            }
+
+            // Don't start any new hide animations if one is running
+            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
+
+            ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, "alpha",
+                    0f);
+            fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
+            fadeOut.setInterpolator(Interpolators.LINEAR);
+            fadeOut.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mNavigationBarView.setRotateButtonVisibility(false);
+                }
+            });
+
+            mRotateHideAnimator = fadeOut;
+            fadeOut.start();
         }
     }
 
@@ -394,13 +498,9 @@
         // May be called due to a new rotation proposal or a change in hover state
         if (reasonHover) {
             // Don't reschedule if a hide animator is running
-            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
-                return;
-            }
+            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
             // Don't reschedule if not visible
-            if (mNavigationBarView.getRotateSuggestionButton().getVisibility() != View.VISIBLE) {
-                return;
-            }
+            if (!mNavigationBarView.isRotateButtonVisible()) return;
         }
 
         Handler h = getView().getHandler();
@@ -827,7 +927,7 @@
                     if (shouldOverrideUserLockPrefs(rotation)) {
                         mRotationLockController.setRotationLockedAtAngle(true, rotation);
                     }
-                    safeSetRotationButtonState(false, true);
+                    setRotateSuggestionButtonState(false, true);
                 }
 
                 if (mNavigationBarView != null
@@ -863,22 +963,22 @@
 
         @Override
         public void onTaskStackChanged() {
-            safeSetRotationButtonState(false);
+            setRotateSuggestionButtonState(false);
         }
 
         @Override
         public void onTaskRemoved(int taskId) {
-            safeSetRotationButtonState(false);
+            setRotateSuggestionButtonState(false);
         }
 
         @Override
         public void onTaskMovedToFront(int taskId) {
-            safeSetRotationButtonState(false);
+            setRotateSuggestionButtonState(false);
         }
 
         @Override
         public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
-            safeSetRotationButtonState(false);
+            setRotateSuggestionButtonState(false);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 285980b..a5621e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -29,6 +29,7 @@
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.annotation.DrawableRes;
+import android.annotation.StyleRes;
 import android.app.ActivityManager;
 import android.app.StatusBarManager;
 import android.content.Context;
@@ -37,6 +38,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Message;
 import android.os.RemoteException;
@@ -87,8 +89,6 @@
     final static boolean DEBUG = false;
     final static String TAG = "StatusBar/NavBarView";
 
-    final static int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
-
     // slippery nav bar when everything is disabled, e.g. during setup
     final static boolean SLIPPERY_WHEN_DISABLED = true;
 
@@ -152,7 +152,7 @@
     private RecentsOnboarding mRecentsOnboarding;
     private NotificationPanelView mPanelView;
 
-    private Animator mRotateHideAnimator;
+    private int mRotateBtnStyle = R.style.RotateButtonCCWStart90;
 
     private class NavTransitionListener implements TransitionListener {
         private boolean mBackTransitioning;
@@ -429,10 +429,7 @@
             mImeIcon = getDrawable(darkContext, lightContext,
                     R.drawable.ic_ime_switcher_default, R.drawable.ic_ime_switcher_default);
 
-            int lightColor = Utils.getColorAttr(lightContext, R.attr.singleToneColor);
-            int darkColor = Utils.getColorAttr(darkContext, R.attr.singleToneColor);
-            mRotateSuggestionIcon = getDrawable(ctx, R.drawable.ic_sysbar_rotate_button,
-                    lightColor, darkColor);
+            updateRotateSuggestionButtonStyle(mRotateBtnStyle, false);
 
             if (ALTERNATE_CAR_MODE_UI) {
                 updateCarModeIcons(ctx);
@@ -726,93 +723,61 @@
             // Accessibility button overrides Menu, IME switcher and rotate buttons.
             setMenuVisibility(false, true);
             getImeSwitchButton().setVisibility(View.INVISIBLE);
-            setRotateSuggestionButtonState(false, true);
+            setRotateButtonVisibility(false);
         }
 
         getAccessibilityButton().setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
         getAccessibilityButton().setLongClickable(longClickable);
     }
 
-    public void setRotateSuggestionButtonState(final boolean visible) {
-        setRotateSuggestionButtonState(visible, false);
+    public void updateRotateSuggestionButtonStyle(@StyleRes int style, boolean setIcon) {
+        mRotateBtnStyle = style;
+        final Context ctx = getContext();
+
+        // Extract the dark and light tints
+        final int dualToneDarkTheme = Utils.getThemeAttr(ctx, R.attr.darkIconTheme);
+        final int dualToneLightTheme = Utils.getThemeAttr(ctx, R.attr.lightIconTheme);
+        Context darkContext = new ContextThemeWrapper(ctx, dualToneDarkTheme);
+        Context lightContext = new ContextThemeWrapper(ctx, dualToneLightTheme);
+        final int lightColor = Utils.getColorAttr(lightContext, R.attr.singleToneColor);
+        final int darkColor = Utils.getColorAttr(darkContext, R.attr.singleToneColor);
+
+        // Use the supplied style to set the icon's rotation parameters
+        Context rotateContext = new ContextThemeWrapper(ctx, style);
+
+        // Recreate the icon and set it if needed
+        mRotateSuggestionIcon = getDrawable(rotateContext, R.drawable.ic_sysbar_rotate_button,
+                lightColor, darkColor);
+        if (setIcon) getRotateSuggestionButton().setImageDrawable(mRotateSuggestionIcon);
     }
 
-    public void setRotateSuggestionButtonState(final boolean visible, final boolean force) {
-        ButtonDispatcher rotBtn = getRotateSuggestionButton();
-        final boolean currentlyVisible = mShowRotateButton;
-
-        // Rerun a show animation to indicate change but don't rerun a hide animation
-        if (!visible && !currentlyVisible) return;
-
-        View currentView = rotBtn.getCurrentView();
-        if (currentView == null) return;
-
-        KeyButtonDrawable kbd = rotBtn.getImageDrawable();
-        if (kbd == null) return;
-
-        AnimatedVectorDrawable animIcon = null;
-        if (kbd.getDrawable(0) instanceof AnimatedVectorDrawable) {
-            animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0);
-        }
-
-        if (visible) { // Appear and change, cannot force
-            setRotateButtonVisibility(true);
-
-            // Stop any currently running hide animations
-            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
-                mRotateHideAnimator.pause();
-            }
-
-            // Reset the alpha if any has changed due to hide animation
-            currentView.setAlpha(1f);
-
-            // Run the rotate icon's animation if it has one
-            if (animIcon != null) {
-                animIcon.reset();
-                animIcon.start();
-            }
-
-        } else { // Hide
-            if (force) {
-                // If a hide animator is running stop it and instantly make invisible
-                if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
-                    mRotateHideAnimator.pause();
-                }
-                setRotateButtonVisibility(false);
-                return;
-            }
-
-            // Don't start any new hide animations if one is running
-            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return;
-
-            ObjectAnimator fadeOut = ObjectAnimator.ofFloat(currentView, "alpha",
-                    0f);
-            fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
-            fadeOut.setInterpolator(Interpolators.LINEAR);
-            fadeOut.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    setRotateButtonVisibility(false);
-                }
-            });
-
-            mRotateHideAnimator = fadeOut;
-            fadeOut.start();
-        }
-    }
-
-    private void setRotateButtonVisibility(final boolean visible) {
+    public void setRotateButtonVisibility(final boolean visible) {
         // Never show if a11y is visible
         final boolean adjVisible = visible && !mShowAccessibilityButton;
         final int vis = adjVisible ? View.VISIBLE : View.INVISIBLE;
 
+        // No need to do anything if the request matches the current state
+        if (vis == getRotateSuggestionButton().getVisibility()) return;
+
         getRotateSuggestionButton().setVisibility(vis);
         mShowRotateButton = visible;
 
+        // Stop any active animations if hidden
+        if (!visible) {
+            Drawable d = mRotateSuggestionIcon.getDrawable(0);
+            if (d instanceof AnimatedVectorDrawable) {
+                AnimatedVectorDrawable avd = (AnimatedVectorDrawable) d;
+                avd.clearAnimationCallbacks();
+                avd.reset();
+            }
+        }
+
         // Hide/restore other button visibility, if necessary
         setNavigationIconHints(mNavigationIconHints, true);
     }
 
+    public boolean isRotateButtonVisible() { return mShowRotateButton; }
+
     @Override
     public void onFinishInflate() {
         mNavigationInflaterView = (NavigationBarInflaterView) findViewById(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 40fe50f..e77b135 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -140,6 +140,11 @@
             return false;
         }
 
+        // showAmbient == show in shade but not shelf
+        if (!showAmbient && notificationData.shouldSuppressScreenOn(entry.key)) {
+            return false;
+        }
+
         return true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 2b50853..255e5e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -112,7 +112,6 @@
     protected static final float SCRIM_IN_FRONT_ALPHA_LOCKED = GRADIENT_SCRIM_ALPHA_BUSY;
 
     static final int TAG_KEY_ANIM = R.id.scrim;
-    private static final int TAG_KEY_ANIM_TARGET = R.id.scrim_target;
     private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
     private static final int TAG_END_ALPHA = R.id.scrim_alpha_end;
     private static final float NOT_INITIALIZED = -1;
@@ -138,7 +137,8 @@
     protected float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
     protected float mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING;
 
-    private float mFraction;
+    // Assuming the shade is expanded during initialization
+    private float mExpansionFraction = 1f;
 
     private boolean mDarkenWhileDragging;
     protected boolean mAnimateChange;
@@ -252,6 +252,7 @@
         mCurrentBehindTint = state.getBehindTint();
         mCurrentInFrontAlpha = state.getFrontAlpha();
         mCurrentBehindAlpha = state.getBehindAlpha();
+        applyExpansionToAlpha();
 
         // Cancel blanking transitions that were pending before we requested a new state
         if (mPendingFrameCallback != null) {
@@ -363,45 +364,50 @@
      * @param fraction From 0 to 1 where 0 means collapse and 1 expanded.
      */
     public void setPanelExpansion(float fraction) {
-        if (mFraction != fraction) {
-            mFraction = fraction;
+        if (mExpansionFraction != fraction) {
+            mExpansionFraction = fraction;
 
-            if (mState == ScrimState.UNLOCKED) {
-                // Darken scrim as you pull down the shade when unlocked
-                float behindFraction = getInterpolatedFraction();
-                behindFraction = (float) Math.pow(behindFraction, 0.8f);
-                mCurrentBehindAlpha = behindFraction * mScrimBehindAlphaKeyguard;
-                mCurrentInFrontAlpha = 0;
-            } else if (mState == ScrimState.KEYGUARD) {
-                if (mUpdatePending) {
-                    return;
-                }
+            if (!(mState == ScrimState.UNLOCKED || mState == ScrimState.KEYGUARD)) {
+                return;
+            }
 
-                // Either darken of make the scrim transparent when you
-                // pull down the shade
-                float interpolatedFract = getInterpolatedFraction();
-                if (mDarkenWhileDragging) {
-                    mCurrentBehindAlpha = MathUtils.lerp(mScrimBehindAlphaUnlocking,
-                            mScrimBehindAlphaKeyguard, interpolatedFract);
-                    mCurrentInFrontAlpha = (1f - interpolatedFract) * SCRIM_IN_FRONT_ALPHA_LOCKED;
-                } else {
-                    mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, mScrimBehindAlphaKeyguard,
-                            interpolatedFract);
-                    mCurrentInFrontAlpha = 0;
-                }
-            } else {
+            applyExpansionToAlpha();
+
+            if (mUpdatePending) {
                 return;
             }
 
             if (mPinnedHeadsUpCount != 0) {
                 updateHeadsUpScrim(false);
             }
-
             updateScrim(false /* animate */, mScrimInFront, mCurrentInFrontAlpha);
             updateScrim(false /* animate */, mScrimBehind, mCurrentBehindAlpha);
         }
     }
 
+    private void applyExpansionToAlpha() {
+        if (mState == ScrimState.UNLOCKED) {
+            // Darken scrim as you pull down the shade when unlocked
+            float behindFraction = getInterpolatedFraction();
+            behindFraction = (float) Math.pow(behindFraction, 0.8f);
+            mCurrentBehindAlpha = behindFraction * mScrimBehindAlphaKeyguard;
+            mCurrentInFrontAlpha = 0;
+        } else if (mState == ScrimState.KEYGUARD) {
+            // Either darken of make the scrim transparent when you
+            // pull down the shade
+            float interpolatedFract = getInterpolatedFraction();
+            if (mDarkenWhileDragging) {
+                mCurrentBehindAlpha = MathUtils.lerp(mScrimBehindAlphaUnlocking,
+                        mScrimBehindAlphaKeyguard, interpolatedFract);
+                mCurrentInFrontAlpha = (1f - interpolatedFract) * SCRIM_IN_FRONT_ALPHA_LOCKED;
+            } else {
+                mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, mScrimBehindAlphaKeyguard,
+                        interpolatedFract);
+                mCurrentInFrontAlpha = 0;
+            }
+        }
+    }
+
     /**
      * Keyguard and shade scrim opacity varies according to how many notifications are visible.
      * @param notificationCount Number of visible notifications.
@@ -497,7 +503,7 @@
     }
 
     private float getInterpolatedFraction() {
-        float frac = mFraction;
+        float frac = mExpansionFraction;
         // let's start this 20% of the way down the screen
         frac = frac * 1.2f - 0.2f;
         if (frac <= 0) {
@@ -551,7 +557,7 @@
         return scrim == mScrimInFront ? mCurrentInFrontTint : mCurrentBehindTint;
     }
 
-    private void startScrimAnimation(final View scrim, float current, float target) {
+    private void startScrimAnimation(final View scrim, float current) {
         ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
         final int initialScrimTint = scrim instanceof ScrimView ? ((ScrimView) scrim).getTint() :
                 Color.TRANSPARENT;
@@ -559,7 +565,9 @@
             final float animAmount = (float) animation.getAnimatedValue();
             final int finalScrimTint = scrim == mScrimInFront ?
                     mCurrentInFrontTint : mCurrentBehindTint;
-            float alpha = MathUtils.lerp(current, target, animAmount);
+            float finalScrimAlpha = scrim == mScrimInFront ?
+                    mCurrentInFrontAlpha : mCurrentBehindAlpha;
+            float alpha = MathUtils.lerp(current, finalScrimAlpha, animAmount);
             int tint = ColorUtils.blendARGB(initialScrimTint, finalScrimTint, animAmount);
             updateScrimColor(scrim, alpha, tint);
             dispatchScrimsVisible();
@@ -570,6 +578,12 @@
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
+                final int finalScrimTint = scrim == mScrimInFront ?
+                        mCurrentInFrontTint : mCurrentBehindTint;
+                float finalScrimAlpha = scrim == mScrimInFront ?
+                        mCurrentInFrontAlpha : mCurrentBehindAlpha;
+                updateScrimColor(scrim, finalScrimAlpha, finalScrimTint);
+
                 if (mKeyguardFadingOutInProgress) {
                     mKeyguardFadeoutAnimation = null;
                     mKeyguardFadingOutInProgress = false;
@@ -577,7 +591,6 @@
                 onFinished();
 
                 scrim.setTag(TAG_KEY_ANIM, null);
-                scrim.setTag(TAG_KEY_ANIM_TARGET, null);
                 dispatchScrimsVisible();
 
                 if (!mDeferFinishedListener && mOnAnimationFinished != null) {
@@ -592,7 +605,6 @@
             mKeyguardFadeoutAnimation = anim;
         }
         scrim.setTag(TAG_KEY_ANIM, anim);
-        scrim.setTag(TAG_KEY_ANIM_TARGET, target);
     }
 
     protected Interpolator getInterpolator() {
@@ -700,7 +712,7 @@
                 if (animate) {
                     mDeferFinishedListener = true;
                 }
-                previousAnimator.cancel();
+                cancelAnimator(previousAnimator);
                 mDeferFinishedListener = false;
             } else {
                 animEndValue = ViewState.getChildTag(scrim, TAG_END_ALPHA);
@@ -709,9 +721,11 @@
 
         if (mPendingFrameCallback != null) {
             // Display is off and we're waiting.
+            cancelAnimator(previousAnimator);
             return;
         } else if (mBlankScreen) {
             // Need to blank the display before continuing.
+            cancelAnimator(previousAnimator);
             blankDisplay();
             return;
         } else if (!mScreenBlankingCallbackCalled) {
@@ -737,7 +751,7 @@
             if (animate) {
                 final float fromAlpha = scrimView == null ? scrim.getAlpha()
                         : scrimView.getViewAlpha();
-                startScrimAnimation(scrim, fromAlpha, alpha);
+                startScrimAnimation(scrim, fromAlpha);
                 scrim.setTag(TAG_START_ALPHA, currentAlpha);
                 scrim.setTag(TAG_END_ALPHA, alpha);
             } else {
@@ -765,6 +779,13 @@
         }
     }
 
+    @VisibleForTesting
+    protected void cancelAnimator(ValueAnimator previousAnimator) {
+        if (previousAnimator != null) {
+            previousAnimator.cancel();
+        }
+    }
+
     private void blankDisplay() {
         updateScrimColor(mScrimInFront, 1, Color.BLACK);
 
@@ -827,7 +848,7 @@
         } else {
             alpha = 1.0f - mTopHeadsUpDragAmount;
         }
-        float expandFactor = (1.0f - mFraction);
+        float expandFactor = (1.0f - mExpansionFraction);
         expandFactor = Math.max(expandFactor, 0.0f);
         return alpha * expandFactor;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 24920cb..4ffe5fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2804,6 +2804,7 @@
                     public void setRemoteInputActive(NotificationData.Entry entry,
                             boolean remoteInputActive) {
                         mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
+                        entry.row.updateMaxHeights();
                     }
                     public void lockScrollTo(NotificationData.Entry entry) {
                         mStackScroller.lockScrollTo(entry.row);
@@ -4519,6 +4520,9 @@
             }
             if (isScreenTurningOnOrOn()) {
                 if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Launching camera");
+                if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
+                    mStatusBarKeyguardViewManager.hideBouncer(false /* destroyView */);
+                }
                 mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source);
                 updateScrimController();
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index c667309..47ea3a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -161,7 +161,7 @@
         updateStates();
     }
 
-    private void hideBouncer(boolean destroyView) {
+    public void hideBouncer(boolean destroyView) {
         mBouncer.hide(destroyView);
         cancelPendingWakeupAction();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
index 0d21c4e..6ee6cb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -102,8 +102,6 @@
 
     @Override
     public void scanForAccessPoints() {
-        if (DEBUG) Log.d(TAG, "force update APs!");
-        mWifiTracker.forceUpdate();
         fireAcccessPointsCallback(mWifiTracker.getAccessPoints());
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 179c0d5..d74a59e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -60,6 +60,8 @@
 import com.android.systemui.statusbar.notification.NotificationViewWrapper;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
+import java.util.function.Consumer;
+
 /**
  * Host for the remote input.
  */
@@ -90,6 +92,7 @@
 
     private boolean mResetting;
     private NotificationViewWrapper mWrapper;
+    private Consumer<Boolean> mOnVisibilityChangedListener;
 
     public RemoteInputView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -451,6 +454,18 @@
         mWrapper = wrapper;
     }
 
+    public void setOnVisibilityChangedListener(Consumer<Boolean> visibilityChangedListener) {
+        mOnVisibilityChangedListener = visibilityChangedListener;
+    }
+
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        if (changedView == this) {
+            mOnVisibilityChangedListener.accept(visibility == VISIBLE);
+        }
+    }
+
     /**
      * An EditText that changes appearance based on whether it's focusable and becomes
      * un-focusable whenever the user navigates away from it or it becomes invisible.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index d7a810e..0f637fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -220,8 +220,7 @@
     }
 
     public int getInnerHeight() {
-        return Math.max(Math.min(mLayoutHeight, mMaxLayoutHeight) - mTopPadding
-                - mExpandAnimationTopChange, mLayoutMinHeight);
+        return Math.max(Math.min(mLayoutHeight, mMaxLayoutHeight) - mTopPadding, mLayoutMinHeight);
     }
 
     public boolean isShadeExpanded() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 4ca33cd..ac2a1e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -120,6 +120,7 @@
         super(context, attrs, defStyleAttr, defStyleRes);
         initDimens();
         mHybridGroupManager = new HybridGroupManager(getContext(), this);
+        setClipChildren(false);
     }
 
     private void initDimens() {
@@ -134,7 +135,7 @@
                 R.dimen.notification_children_container_top_padding);
         mHeaderHeight = mNotificationHeaderMargin + mNotificatonTopPadding;
         mCollapsedBottompadding = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.notification_content_margin_bottom);
+                com.android.internal.R.dimen.notification_content_margin);
         mEnableShadowOnChildNotifications =
                 res.getBoolean(R.bool.config_enableShadowOnChildNotifications);
         mShowDividersWhenExpanded =
@@ -533,11 +534,12 @@
 
     /**
      * Update the state of all its children based on a linear layout algorithm.
-     *
-     * @param resultState the state to update
+     *  @param resultState the state to update
      * @param parentState the state of the parent
+     * @param ambientState
      */
-    public void getState(StackScrollState resultState, ExpandableViewState parentState) {
+    public void getState(StackScrollState resultState, ExpandableViewState parentState,
+            AmbientState ambientState) {
         int childCount = mChildren.size();
         int yPosition = mNotificationHeaderMargin;
         boolean firstChild = true;
@@ -553,6 +555,7 @@
 
         boolean childrenExpandedAndNotAnimating = mChildrenExpanded
                 && !mContainingNotification.isGroupExpansionChanging();
+        int launchTransitionCompensation = 0;
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
             if (!firstChild) {
@@ -577,13 +580,13 @@
             ExpandableViewState childState = resultState.getViewStateForView(child);
             int intrinsicHeight = child.getIntrinsicHeight();
             childState.height = intrinsicHeight;
-            childState.yTranslation = yPosition;
+            childState.yTranslation = yPosition + launchTransitionCompensation;
             childState.hidden = false;
             // When the group is expanded, the children cast the shadows rather than the parent
             // so use the parent's elevation here.
             childState.zTranslation =
                     (childrenExpandedAndNotAnimating && mEnableShadowOnChildNotifications)
-                    ? mContainingNotification.getTranslationZ()
+                    ? parentState.zTranslation
                     : 0;
             childState.dimmed = parentState.dimmed;
             childState.dark = parentState.dark;
@@ -600,6 +603,9 @@
             childState.location = parentState.location;
             childState.inShelf = parentState.inShelf;
             yPosition += intrinsicHeight;
+            if (child.isExpandAnimationRunning()) {
+                launchTransitionCompensation = -ambientState.getExpandAnimationTopChange();
+            }
 
         }
         if (mOverflowNumber != null) {
@@ -637,7 +643,7 @@
             }
             mHeaderViewState.initFrom(mNotificationHeader);
             mHeaderViewState.zTranslation = childrenExpandedAndNotAnimating
-                    ? mContainingNotification.getTranslationZ()
+                    ? parentState.zTranslation
                     : 0;
         }
     }
@@ -727,6 +733,9 @@
     }
 
     private void updateChildrenClipping() {
+        if (mContainingNotification.hasExpandingChild()) {
+            return;
+        }
         int childCount = mChildren.size();
         int layoutEnd = mContainingNotification.getActualHeight() - mClipBottomAmount;
         for (int i = 0; i < childCount; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index d68a7b1..51737a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -91,17 +91,18 @@
         updateClipping(resultState, algorithmState, ambientState);
         updateSpeedBumpState(resultState, algorithmState, ambientState);
         updateShelfState(resultState, ambientState);
-        getNotificationChildrenStates(resultState, algorithmState);
+        getNotificationChildrenStates(resultState, algorithmState, ambientState);
     }
 
     private void getNotificationChildrenStates(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState) {
+            StackScrollAlgorithmState algorithmState,
+            AmbientState ambientState) {
         int childCount = algorithmState.visibleChildren.size();
         for (int i = 0; i < childCount; i++) {
             ExpandableView v = algorithmState.visibleChildren.get(i);
             if (v instanceof ExpandableNotificationRow) {
                 ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-                row.getChildrenStates(resultState);
+                row.getChildrenStates(resultState, ambientState);
             }
         }
     }
@@ -323,7 +324,9 @@
         }
         ExpandableNotificationRow expandingNotification = ambientState.getExpandingNotification();
         state.indexOfExpandingNotification = expandingNotification != null
-                ? state.visibleChildren.indexOf(expandingNotification)
+                ? expandingNotification.isChildInGroup()
+                    ? state.visibleChildren.indexOf(expandingNotification.getNotificationParent())
+                    : state.visibleChildren.indexOf(expandingNotification)
                 : -1;
     }
 
@@ -386,7 +389,7 @@
 
         childViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
         float inset = ambientState.getTopPadding() + ambientState.getStackTranslation();
-        if (i < algorithmState.getIndexOfExpandingNotification()) {
+        if (i <= algorithmState.getIndexOfExpandingNotification()) {
             inset += ambientState.getExpandAnimationTopChange();
         }
         if (child.mustStayOnScreen() && childViewState.yTranslation >= 0) {
@@ -515,7 +518,7 @@
                 - ambientState.getShelf().getIntrinsicHeight();
         childViewState.yTranslation = Math.min(childViewState.yTranslation, shelfStart);
         if (childViewState.yTranslation >= shelfStart) {
-            childViewState.hidden = !child.isExpandAnimationRunning();
+            childViewState.hidden = !child.isExpandAnimationRunning() && !child.hasExpandingChild();
             childViewState.inShelf = true;
             childViewState.headsUpIsVisible = false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
deleted file mode 100644
index 6ed07f8..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
+++ /dev/null
@@ -1,534 +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 com.android.systemui.volume;
-
-import static android.support.v7.media.MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTED;
-import static android.support.v7.media.MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTING;
-import static android.support.v7.media.MediaRouter.UNSELECT_REASON_DISCONNECTED;
-
-import static com.android.settingslib.bluetooth.Utils.getBtClassDrawableWithDescription;
-
-import android.app.Dialog;
-import android.bluetooth.BluetoothClass;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.net.wifi.WifiManager;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.support.v7.media.MediaControlIntent;
-import android.support.v7.media.MediaRouteSelector;
-import android.support.v7.media.MediaRouter;
-import android.telecom.TelecomManager;
-import android.util.Log;
-import android.util.Pair;
-import android.view.Window;
-import android.view.WindowManager;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.settingslib.Utils;
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.systemui.Dependency;
-import com.android.systemui.HardwareUiLayout;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.plugins.VolumeDialogController;
-import com.android.systemui.statusbar.policy.BluetoothController;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.List;
-
-public class OutputChooserDialog extends Dialog
-        implements DialogInterface.OnDismissListener, OutputChooserLayout.Callback {
-
-    private static final String TAG = Util.logTag(OutputChooserDialog.class);
-    private static final int MAX_DEVICES = 10;
-
-    private static final long UPDATE_DELAY_MS = 300L;
-    private static final int MSG_UPDATE_ITEMS = 1;
-
-    private final Context mContext;
-    private final BluetoothController mBluetoothController;
-    private WifiManager mWifiManager;
-    private OutputChooserLayout mView;
-    private final MediaRouterWrapper mRouter;
-    private final MediaRouterCallback mRouterCallback;
-    private long mLastUpdateTime;
-    static final boolean INCLUDE_MEDIA_ROUTES = false;
-    private boolean mIsInCall;
-    protected boolean isAttached;
-
-    private final MediaRouteSelector mRouteSelector;
-    private Drawable mDefaultIcon;
-    private Drawable mTvIcon;
-    private Drawable mSpeakerIcon;
-    private Drawable mSpeakerGroupIcon;
-    private HardwareUiLayout mHardwareLayout;
-    private final VolumeDialogController mController;
-
-    public OutputChooserDialog(Context context, MediaRouterWrapper router) {
-        super(context, com.android.systemui.R.style.qs_theme);
-        mContext = context;
-        mBluetoothController = Dependency.get(BluetoothController.class);
-        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-        TelecomManager tm = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
-        mIsInCall = tm.isInCall();
-        mRouter = router;
-        mRouterCallback = new MediaRouterCallback();
-        mRouteSelector = new MediaRouteSelector.Builder()
-                .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
-                .build();
-
-        final IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        context.registerReceiver(mReceiver, filter);
-
-        mController = Dependency.get(VolumeDialogController.class);
-
-        // Window initialization
-        Window window = getWindow();
-        window.requestFeature(Window.FEATURE_NO_TITLE);
-        window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
-        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
-                | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
-        window.addFlags(
-                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                        | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                        | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
-        window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
-    }
-
-    protected void setIsInCall(boolean inCall) {
-        mIsInCall = inCall;
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.output_chooser);
-        setCanceledOnTouchOutside(true);
-        setOnDismissListener(this::onDismiss);
-
-        mView = findViewById(R.id.output_chooser);
-        mHardwareLayout = HardwareUiLayout.get(mView);
-        mHardwareLayout.setOutsideTouchListener(view -> dismiss());
-        mHardwareLayout.setSwapOrientation(false);
-        mView.setCallback(this);
-
-        if (mIsInCall) {
-            mView.setTitle(R.string.output_calls_title);
-        } else {
-            mView.setTitle(R.string.output_title);
-        }
-
-        mDefaultIcon = mContext.getDrawable(R.drawable.ic_cast);
-        mTvIcon = mContext.getDrawable(R.drawable.ic_tv);
-        mSpeakerIcon = mContext.getDrawable(R.drawable.ic_speaker);
-        mSpeakerGroupIcon = mContext.getDrawable(R.drawable.ic_speaker_group);
-
-        final boolean wifiOff = !mWifiManager.isWifiEnabled();
-        final boolean btOff = !mBluetoothController.isBluetoothEnabled();
-        if (wifiOff && btOff) {
-            mView.setEmptyState(getDisabledServicesMessage(wifiOff, btOff));
-        }
-        // time out after 5 seconds
-        mView.postDelayed(() -> updateItems(true), 5000);
-    }
-
-    protected void cleanUp() {}
-
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        if (!mIsInCall && INCLUDE_MEDIA_ROUTES) {
-            mRouter.addCallback(mRouteSelector, mRouterCallback,
-                    MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
-        }
-        mBluetoothController.addCallback(mCallback);
-        mController.addCallback(mControllerCallbackH, mHandler);
-        isAttached = true;
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        isAttached = false;
-        mRouter.removeCallback(mRouterCallback);
-        mController.removeCallback(mControllerCallbackH);
-        mBluetoothController.removeCallback(mCallback);
-        super.onDetachedFromWindow();
-    }
-
-    @Override
-    public void onDismiss(DialogInterface unused) {
-        mContext.unregisterReceiver(mReceiver);
-        cleanUp();
-    }
-
-    @Override
-    public void show() {
-        super.show();
-        Dependency.get(MetricsLogger.class).visible(MetricsProto.MetricsEvent.OUTPUT_CHOOSER);
-        mHardwareLayout.setTranslationX(getAnimTranslation());
-        mHardwareLayout.setAlpha(0);
-        mHardwareLayout.animate()
-                .alpha(1)
-                .translationX(0)
-                .setDuration(300)
-                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                .withEndAction(() -> getWindow().getDecorView().requestAccessibilityFocus())
-                .start();
-    }
-
-    @Override
-    public void dismiss() {
-        Dependency.get(MetricsLogger.class).hidden(MetricsProto.MetricsEvent.OUTPUT_CHOOSER);
-        mHardwareLayout.setTranslationX(0);
-        mHardwareLayout.setAlpha(1);
-        mHardwareLayout.animate()
-                .alpha(0)
-                .translationX(getAnimTranslation())
-                .setDuration(300)
-                .withEndAction(() -> super.dismiss())
-                .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
-                .start();
-    }
-
-    private float getAnimTranslation() {
-        return getContext().getResources().getDimension(
-                com.android.systemui.R.dimen.output_chooser_panel_width) / 2;
-    }
-
-    @Override
-    public void onDetailItemClick(OutputChooserLayout.Item item) {
-        if (item == null || item.tag == null) return;
-        if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) {
-            final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
-            if (device.getMaxConnectionState() == BluetoothProfile.STATE_DISCONNECTED) {
-                Dependency.get(MetricsLogger.class).action(
-                        MetricsProto.MetricsEvent.ACTION_OUTPUT_CHOOSER_CONNECT);
-                mBluetoothController.connect(device);
-            }
-        } else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) {
-            final MediaRouter.RouteInfo route = (MediaRouter.RouteInfo) item.tag;
-            if (route.isEnabled()) {
-                Dependency.get(MetricsLogger.class).action(
-                        MetricsProto.MetricsEvent.ACTION_OUTPUT_CHOOSER_CONNECT);
-                route.select();
-            }
-        }
-    }
-
-    @Override
-    public void onDetailItemDisconnect(OutputChooserLayout.Item item) {
-        if (item == null || item.tag == null) return;
-        if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) {
-            final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
-            Dependency.get(MetricsLogger.class).action(
-                    MetricsProto.MetricsEvent.ACTION_OUTPUT_CHOOSER_DISCONNECT);
-            mBluetoothController.disconnect(device);
-        } else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) {
-            Dependency.get(MetricsLogger.class).action(
-                    MetricsProto.MetricsEvent.ACTION_OUTPUT_CHOOSER_DISCONNECT);
-            mRouter.unselect(UNSELECT_REASON_DISCONNECTED);
-        }
-    }
-
-    private void updateItems(boolean timeout) {
-        if (SystemClock.uptimeMillis() - mLastUpdateTime < UPDATE_DELAY_MS) {
-            mHandler.removeMessages(MSG_UPDATE_ITEMS);
-            mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ITEMS, timeout),
-                    mLastUpdateTime + UPDATE_DELAY_MS);
-            return;
-        }
-        mLastUpdateTime = SystemClock.uptimeMillis();
-        if (mView == null) return;
-        ArrayList<OutputChooserLayout.Item> items = new ArrayList<>();
-
-        // Add bluetooth devices
-        addBluetoothDevices(items);
-
-        // Add remote displays
-        if (!mIsInCall && INCLUDE_MEDIA_ROUTES) {
-            addRemoteDisplayRoutes(items);
-        }
-
-        items.sort(ItemComparator.sInstance);
-
-        if (items.size() == 0 && timeout) {
-            String emptyMessage = mContext.getString(R.string.output_none_found);
-            final boolean wifiOff = !mWifiManager.isWifiEnabled();
-            final boolean btOff = !mBluetoothController.isBluetoothEnabled();
-            if (wifiOff || btOff) {
-                emptyMessage = getDisabledServicesMessage(wifiOff, btOff);
-            }
-            mView.setEmptyState(emptyMessage);
-        }
-
-        mView.setItems(items.toArray(new OutputChooserLayout.Item[items.size()]));
-    }
-
-    private String getDisabledServicesMessage(boolean wifiOff, boolean btOff) {
-        return mContext.getString(R.string.output_none_found_service_off,
-                wifiOff && btOff ? mContext.getString(R.string.output_service_bt_wifi)
-                        : wifiOff ? mContext.getString(R.string.output_service_wifi)
-                                : mContext.getString(R.string.output_service_bt));
-    }
-
-    private void addBluetoothDevices(List<OutputChooserLayout.Item> items) {
-        final Collection<CachedBluetoothDevice> devices = mBluetoothController.getDevices();
-        if (devices != null) {
-            int connectedDevices = 0;
-            int count = 0;
-            for (CachedBluetoothDevice device : devices) {
-                if (mBluetoothController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
-                final int majorClass = device.getBtClass().getMajorDeviceClass();
-                if (majorClass != BluetoothClass.Device.Major.AUDIO_VIDEO
-                        && majorClass != BluetoothClass.Device.Major.UNCATEGORIZED) {
-                    continue;
-                }
-                final OutputChooserLayout.Item item = new OutputChooserLayout.Item();
-                item.iconResId = R.drawable.ic_qs_bluetooth_on;
-                item.line1 = device.getName();
-                item.tag = device;
-                item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_BT;
-                int state = device.getMaxConnectionState();
-                if (state == BluetoothProfile.STATE_CONNECTED) {
-                    item.iconResId = R.drawable.ic_qs_bluetooth_connected;
-                    int batteryLevel = device.getBatteryLevel();
-                    if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
-                        Pair<Drawable, String> pair =
-                                getBtClassDrawableWithDescription(getContext(), device);
-                        item.icon = pair.first;
-                        item.line2 = mContext.getString(
-                                R.string.quick_settings_connected_battery_level,
-                                Utils.formatPercentage(batteryLevel));
-                    } else {
-                        item.line2 = mContext.getString(R.string.quick_settings_connected);
-                    }
-                    item.canDisconnect = true;
-                    items.add(connectedDevices, item);
-                    connectedDevices++;
-                } else if (state == BluetoothProfile.STATE_CONNECTING) {
-                    item.iconResId = R.drawable.ic_qs_bluetooth_connecting;
-                    item.line2 = mContext.getString(R.string.quick_settings_connecting);
-                    items.add(connectedDevices, item);
-                } else {
-                    items.add(item);
-                }
-                if (++count == MAX_DEVICES) {
-                    break;
-                }
-            }
-        }
-    }
-
-    private void addRemoteDisplayRoutes(List<OutputChooserLayout.Item> items) {
-        List<MediaRouter.RouteInfo> routes = mRouter.getRoutes();
-        for(MediaRouter.RouteInfo route : routes) {
-            if (route.isDefaultOrBluetooth() || !route.isEnabled()
-                    || !route.matchesSelector(mRouteSelector)) {
-                continue;
-            }
-            final OutputChooserLayout.Item item = new OutputChooserLayout.Item();
-            item.icon = getIconDrawable(route);
-            item.line1 = route.getName();
-            item.tag = route;
-            item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER;
-            if (route.getConnectionState() == CONNECTION_STATE_CONNECTING) {
-                mContext.getString(R.string.quick_settings_connecting);
-            } else {
-                item.line2 = route.getDescription();
-            }
-
-            if (route.getConnectionState() == CONNECTION_STATE_CONNECTED) {
-                item.canDisconnect = true;
-            }
-            items.add(item);
-        }
-    }
-
-    private Drawable getIconDrawable(MediaRouter.RouteInfo route) {
-        Uri iconUri = route.getIconUri();
-        if (iconUri != null) {
-            try {
-                InputStream is = getContext().getContentResolver().openInputStream(iconUri);
-                Drawable drawable = Drawable.createFromStream(is, null);
-                if (drawable != null) {
-                    return drawable;
-                }
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to load " + iconUri, e);
-                // Falls back.
-            }
-        }
-        return getDefaultIconDrawable(route);
-    }
-
-    private Drawable getDefaultIconDrawable(MediaRouter.RouteInfo route) {
-        // If the type of the receiver device is specified, use it.
-        switch (route.getDeviceType()) {
-            case  MediaRouter.RouteInfo.DEVICE_TYPE_TV:
-                return mTvIcon;
-            case MediaRouter.RouteInfo.DEVICE_TYPE_SPEAKER:
-                return mSpeakerIcon;
-        }
-
-        // Otherwise, make the best guess based on other route information.
-        if (route instanceof MediaRouter.RouteGroup) {
-            // Only speakers can be grouped for now.
-            return mSpeakerGroupIcon;
-        }
-        return mDefaultIcon;
-    }
-
-    private final class MediaRouterCallback extends MediaRouter.Callback {
-        @Override
-        public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
-            updateItems(false);
-        }
-
-        @Override
-        public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
-            updateItems(false);
-        }
-
-        @Override
-        public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
-            updateItems(false);
-        }
-
-        @Override
-        public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
-            updateItems(false);
-        }
-    }
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
-                if (D.BUG) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
-                cancel();
-                cleanUp();
-            }
-        }
-    };
-
-    private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
-        @Override
-        public void onBluetoothStateChange(boolean enabled) {
-            updateItems(false);
-        }
-
-        @Override
-        public void onBluetoothDevicesChanged() {
-            updateItems(false);
-        }
-    };
-
-    static final class ItemComparator implements Comparator<OutputChooserLayout.Item> {
-        public static final ItemComparator sInstance = new ItemComparator();
-
-        @Override
-        public int compare(OutputChooserLayout.Item lhs, OutputChooserLayout.Item rhs) {
-            // Connected item(s) first
-            if (lhs.canDisconnect != rhs.canDisconnect) {
-                return Boolean.compare(rhs.canDisconnect, lhs.canDisconnect);
-            }
-            // Bluetooth items before media routes
-            if (lhs.deviceType != rhs.deviceType) {
-                return Integer.compare(lhs.deviceType, rhs.deviceType);
-            }
-            // then by name
-            return lhs.line1.toString().compareToIgnoreCase(rhs.line1.toString());
-        }
-    }
-
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message message) {
-            switch (message.what) {
-                case MSG_UPDATE_ITEMS:
-                    updateItems((Boolean) message.obj);
-                    break;
-            }
-        }
-    };
-
-    private final VolumeDialogController.Callbacks mControllerCallbackH
-            = new VolumeDialogController.Callbacks() {
-        @Override
-        public void onShowRequested(int reason) {
-            dismiss();
-        }
-
-        @Override
-        public void onDismissRequested(int reason) {}
-
-        @Override
-        public void onScreenOff() {
-            dismiss();
-        }
-
-        @Override
-        public void onStateChanged(VolumeDialogController.State state) {}
-
-        @Override
-        public void onLayoutDirectionChanged(int layoutDirection) {}
-
-        @Override
-        public void onConfigurationChanged() {}
-
-        @Override
-        public void onShowVibrateHint() {}
-
-        @Override
-        public void onShowSilentHint() {}
-
-        @Override
-        public void onShowSafetyWarning(int flags) {}
-
-        @Override
-        public void onAccessibilityModeChanged(Boolean showA11yStream) {}
-
-        @Override
-        public void onConnectedDeviceChanged(String deviceName) {}
-    };
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java
deleted file mode 100644
index d4c6f89..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java
+++ /dev/null
@@ -1,256 +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 com.android.systemui.volume;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.systemui.FontSizeUtils;
-import com.android.systemui.R;
-import com.android.systemui.qs.AutoSizingList;
-
-/**
- * Limited height list of devices.
- */
-public class OutputChooserLayout extends LinearLayout {
-    private static final String TAG = "OutputChooserLayout";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private final Context mContext;
-    private final H mHandler = new H();
-    private final Adapter mAdapter = new Adapter();
-
-    private String mTag;
-    private Callback mCallback;
-    private boolean mItemsVisible = true;
-    private AutoSizingList mItemList;
-    private View mEmpty;
-    private TextView mEmptyText;
-    private TextView mTitle;
-
-    private Item[] mItems;
-
-    public OutputChooserLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mContext = context;
-        mTag = TAG;
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mItemList = findViewById(android.R.id.list);
-        mItemList.setVisibility(GONE);
-        mItemList.setAdapter(mAdapter);
-        mEmpty = findViewById(android.R.id.empty);
-        mEmpty.setVisibility(GONE);
-        mEmptyText = mEmpty.findViewById(R.id.empty_text);
-        mTitle = findViewById(R.id.title);
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        FontSizeUtils.updateFontSize(mEmptyText, R.dimen.qs_detail_empty_text_size);
-        int count = mItemList.getChildCount();
-        for (int i = 0; i < count; i++) {
-            View item = mItemList.getChildAt(i);
-            FontSizeUtils.updateFontSize(item, R.id.empty_text,
-                    R.dimen.qs_detail_item_primary_text_size);
-            FontSizeUtils.updateFontSize(item, android.R.id.summary,
-                    R.dimen.qs_detail_item_secondary_text_size);
-            FontSizeUtils.updateFontSize(item, android.R.id.title,
-                    R.dimen.qs_detail_header_text_size);
-        }
-    }
-
-    public void setTitle(int title) {
-            mTitle.setText(title);
-    }
-
-    public void setEmptyState(String text) {
-        mEmptyText.setText(text);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        if (DEBUG) Log.d(mTag, "onAttachedToWindow");
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        if (DEBUG) Log.d(mTag, "onDetachedFromWindow");
-        mCallback = null;
-    }
-
-    public void setCallback(Callback callback) {
-        mHandler.removeMessages(H.SET_CALLBACK);
-        mHandler.obtainMessage(H.SET_CALLBACK, callback).sendToTarget();
-    }
-
-    public void setItems(Item[] items) {
-        mHandler.removeMessages(H.SET_ITEMS);
-        mHandler.obtainMessage(H.SET_ITEMS, items).sendToTarget();
-    }
-
-    public void setItemsVisible(boolean visible) {
-        mHandler.removeMessages(H.SET_ITEMS_VISIBLE);
-        mHandler.obtainMessage(H.SET_ITEMS_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
-    }
-
-    private void handleSetCallback(Callback callback) {
-        mCallback = callback;
-    }
-
-    private void handleSetItems(Item[] items) {
-        final int itemCount = items != null ? items.length : 0;
-        mEmpty.setVisibility(itemCount == 0 ? VISIBLE : GONE);
-        mItemList.setVisibility(itemCount == 0 ? GONE : VISIBLE);
-        mItems = items;
-        mAdapter.notifyDataSetChanged();
-    }
-
-    private void handleSetItemsVisible(boolean visible) {
-        if (mItemsVisible == visible) return;
-        mItemsVisible = visible;
-        for (int i = 0; i < mItemList.getChildCount(); i++) {
-            mItemList.getChildAt(i).setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
-        }
-    }
-
-    private class Adapter extends BaseAdapter {
-
-        @Override
-        public int getCount() {
-            return mItems != null ? mItems.length : 0;
-        }
-
-        @Override
-        public Object getItem(int position) {
-            return mItems[position];
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return 0;
-        }
-
-        @Override
-        public View getView(int position, View view, ViewGroup parent) {
-            final Item item = mItems[position];
-            if (view == null) {
-                view = LayoutInflater.from(mContext).inflate(R.layout.output_chooser_item, parent,
-                        false);
-            }
-            view.setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
-            final ImageView iv = view.findViewById(android.R.id.icon);
-            if (item.icon != null) {
-                iv.setImageDrawable(item.icon);
-            } else {
-                iv.setImageResource(item.iconResId);
-            }
-            final TextView title = view.findViewById(android.R.id.title);
-            title.setText(item.line1);
-            final TextView summary =  view.findViewById(android.R.id.summary);
-            final boolean twoLines = !TextUtils.isEmpty(item.line2);
-            title.setMaxLines(twoLines ? 1 : 2);
-            summary.setVisibility(twoLines ? VISIBLE : GONE);
-            summary.setText(twoLines ? item.line2 : null);
-            view.setOnClickListener(v -> {
-                if (mCallback != null) {
-                    mCallback.onDetailItemClick(item);
-                }
-            });
-
-            final ImageView icon2 = view.findViewById(android.R.id.icon2);
-            if (item.canDisconnect) {
-                icon2.setImageResource(R.drawable.ic_qs_cancel);
-                icon2.setVisibility(VISIBLE);
-                icon2.setClickable(true);
-                icon2.setOnClickListener(v -> {
-                    if (mCallback != null) {
-                        mCallback.onDetailItemDisconnect(item);
-                    }
-                });
-            } else if (item.icon2 != -1) {
-                icon2.setVisibility(VISIBLE);
-                icon2.setImageResource(item.icon2);
-                icon2.setClickable(false);
-            } else {
-                icon2.setVisibility(GONE);
-            }
-
-            return view;
-        }
-    };
-
-    private class H extends Handler {
-        private static final int SET_ITEMS = 1;
-        private static final int SET_CALLBACK = 2;
-        private static final int SET_ITEMS_VISIBLE = 3;
-
-        public H() {
-            super(Looper.getMainLooper());
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            if (msg.what == SET_ITEMS) {
-                handleSetItems((Item[]) msg.obj);
-            } else if (msg.what == SET_CALLBACK) {
-                handleSetCallback((OutputChooserLayout.Callback) msg.obj);
-            } else if (msg.what == SET_ITEMS_VISIBLE) {
-                handleSetItemsVisible(msg.arg1 != 0);
-            }
-        }
-    }
-
-    public static class Item {
-        public static int DEVICE_TYPE_BT = 1;
-        public static int DEVICE_TYPE_MEDIA_ROUTER = 2;
-        public int iconResId;
-        public Drawable icon;
-        public Drawable overlay;
-        public CharSequence line1;
-        public CharSequence line2;
-        public Object tag;
-        public boolean canDisconnect;
-        public int icon2 = -1;
-        public int deviceType = 0;
-    }
-
-    public interface Callback {
-        void onDetailItemClick(Item item);
-        void onDetailItemDisconnect(Item item);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 3c29b77..7c71b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -110,11 +110,7 @@
     private boolean mShowA11yStream;
     private boolean mShowVolumeDialog;
     private boolean mShowSafetyWarning;
-    private DeviceCallback mDeviceCallback = new DeviceCallback();
     private final NotificationManager mNotificationManager;
-    @GuardedBy("mLock")
-    private List<AudioDeviceInfo> mConnectedDevices = new ArrayList<>();
-    private Object mLock = new Object();
 
     private boolean mDestroyed;
     private VolumePolicy mVolumePolicy;
@@ -192,7 +188,6 @@
         } catch (SecurityException e) {
             Log.w(TAG, "No access to media sessions", e);
         }
-        mAudio.registerAudioDeviceCallback(mDeviceCallback, mWorker);
     }
 
     public void setVolumePolicy(VolumePolicy policy) {
@@ -218,7 +213,6 @@
         mMediaSessions.destroy();
         mObserver.destroy();
         mReceiver.destroy();
-        mAudio.unregisterAudioDeviceCallback(mDeviceCallback);
         mWorkerThread.quitSafely();
     }
 
@@ -842,18 +836,6 @@
                 });
             }
         }
-
-        @Override
-        public void onConnectedDeviceChanged(String deviceName) {
-            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
-                entry.getValue().post(new Runnable() {
-                    @Override
-                    public void run() {
-                        entry.getKey().onConnectedDeviceChanged(deviceName);
-                    }
-                });
-            }
-        }
     }
 
 
@@ -1060,33 +1042,6 @@
         }
     }
 
-    protected final class DeviceCallback extends AudioDeviceCallback {
-        public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
-            synchronized (mLock) {
-                for (AudioDeviceInfo info : addedDevices) {
-                    if (info.isSink()
-                            && (info.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP
-                            || info.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO)) {
-                        mConnectedDevices.add(info);
-                        mCallbacks.onConnectedDeviceChanged(info.getProductName().toString());
-                    }
-                }
-            }
-        }
-
-        public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
-            synchronized (mLock) {
-                for (AudioDeviceInfo info : removedDevices) {
-                    mConnectedDevices.remove(info);
-                }
-
-                if (mConnectedDevices.size() == 0) {
-                    mCallbacks.onConnectedDeviceChanged(null);
-                }
-            }
-        }
-    }
-
     public interface UserActivityListener {
         void onUserActivity();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 1e8e98c..90a9fc8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -18,22 +18,22 @@
 
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
+import static android.media.AudioManager.RINGER_MODE_MAX;
+import static android.media.AudioManager.RINGER_MODE_NORMAL;
+import static android.media.AudioManager.RINGER_MODE_SILENT;
+import static android.media.AudioManager.RINGER_MODE_VIBRATE;
 import static android.media.AudioManager.STREAM_ACCESSIBILITY;
 
-import static com.android.systemui.volume.Events.DISMISS_REASON_OUTPUT_CHOOSER;
 import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED;
-import static com.android.systemui.volume.Events.DISMISS_REASON_TOUCH_OUTSIDE;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.animation.ObjectAnimator;
-import android.annotation.NonNull;
 import android.annotation.SuppressLint;
 import android.app.Dialog;
 import android.app.KeyguardManager;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Color;
@@ -48,9 +48,7 @@
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.provider.Settings.Global;
-import android.support.v7.media.MediaRouter;
 import android.text.InputFilter;
-import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -60,7 +58,6 @@
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
 import android.view.View.OnAttachStateChangeListener;
-import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
@@ -72,9 +69,11 @@
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
 import android.widget.TextView;
+import android.widget.Toast;
 
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
+import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.VolumeDialog;
@@ -108,18 +107,16 @@
     private CustomDialog mDialog;
     private ViewGroup mDialogView;
     private ViewGroup mDialogRowsView;
-    private ViewGroup mFooter;
+    private ViewGroup mRinger;
     private ImageButton mRingerIcon;
+    private ImageButton mSettingsIcon;
     private ImageView mZenIcon;
-    private TextView mRingerStatus;
-    private TextView mRingerTitle;
     private final List<VolumeRow> mRows = new ArrayList<>();
     private ConfigurableTexts mConfigurableTexts;
     private final SparseBooleanArray mDynamic = new SparseBooleanArray();
     private final KeyguardManager mKeyguard;
     private final AccessibilityManagerWrapper mAccessibilityMgr;
     private final Object mSafetyWarningLock = new Object();
-    private final Object mOutputChooserLock = new Object();
     private final Accessibility mAccessibility = new Accessibility();
     private final ColorStateList mActiveSliderTint;
     private final ColorStateList mInactiveSliderTint;
@@ -133,7 +130,6 @@
     private boolean mSilentMode = VolumePrefs.DEFAULT_ENABLE_SILENT_MODE;
     private State mState;
     private SafetyWarningDialog mSafetyWarning;
-    private OutputChooserDialog mOutputChooserDialog;
     private boolean mHovering = false;
 
     public VolumeDialogImpl(Context context) {
@@ -215,11 +211,10 @@
         uiLayout.updateRotation();
 
         mDialogRowsView = mDialog.findViewById(R.id.volume_dialog_rows);
-        mFooter = mDialog.findViewById(R.id.footer);
-        mRingerIcon = mFooter.findViewById(R.id.ringer_icon);
-        mRingerStatus = mFooter.findViewById(R.id.ringer_status);
-        mRingerTitle = mFooter.findViewById(R.id.ringer_title);
-        mZenIcon = mFooter.findViewById(R.id.dnd_icon);
+        mRinger = mDialog.findViewById(R.id.ringer);
+        mRingerIcon = mRinger.findViewById(R.id.ringer_icon);
+        mZenIcon = mRinger.findViewById(R.id.dnd_icon);
+        mSettingsIcon = mDialog.findViewById(R.id.settings);
 
         if (mRows.isEmpty()) {
             addRow(AudioManager.STREAM_MUSIC,
@@ -244,6 +239,7 @@
 
         updateRowsH(getActiveRow());
         initRingerH();
+        initSettingsH();
     }
 
     protected ViewGroup getDialogView() {
@@ -358,10 +354,6 @@
         row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
         row.anim = null;
 
-        row.outputChooser = row.view.findViewById(R.id.output_chooser);
-        row.outputChooser.setOnClickListener(mClickOutputChooser);
-        row.connectedDevice = row.view.findViewById(R.id.volume_row_connected_device);
-
         row.icon = row.view.findViewById(R.id.volume_row_icon);
         row.icon.setImageResource(iconRes);
         if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) {
@@ -396,6 +388,15 @@
         }
     }
 
+    public void initSettingsH() {
+        mSettingsIcon.setOnClickListener(v -> {
+            Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            dismissH(DISMISS_REASON_SETTINGS_CLICKED);
+            Dependency.get(ActivityStarter.class).startActivity(intent, true /* dismissShade */);
+        });
+    }
+
     public void initRingerH() {
         mRingerIcon.setOnClickListener(v -> {
             Events.writeEvent(mContext, Events.EVENT_ICON_CLICK, AudioManager.STREAM_RING,
@@ -406,34 +407,55 @@
             }
             // normal -> vibrate -> silent -> normal (skip vibrate if device doesn't have
             // a vibrator.
+            int newRingerMode;
             final boolean hasVibrator = mController.hasVibrator();
             if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
                 if (hasVibrator) {
                     mController.vibrate();
-                    mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false);
+                    newRingerMode = AudioManager.RINGER_MODE_VIBRATE;
                 } else {
-                    mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false);
+                    newRingerMode = AudioManager.RINGER_MODE_SILENT;
                 }
             } else if (mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) {
-                mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false);
+                newRingerMode = AudioManager.RINGER_MODE_SILENT;
             } else {
-                mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
+                newRingerMode = AudioManager.RINGER_MODE_NORMAL;
                 if (ss.level == 0) {
                     mController.setStreamVolume(AudioManager.STREAM_RING, 1);
                 }
             }
             updateRingerH();
-        });
-        mRingerIcon.setOnLongClickListener(v -> {
-            Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            dismissH(DISMISS_REASON_SETTINGS_CLICKED);
-            Dependency.get(ActivityStarter.class).startActivity(intent, true /* dismissShade */);
-            return true;
+
+            mController.setRingerMode(newRingerMode, false);
+            maybeShowToastH(newRingerMode);
         });
         updateRingerH();
     }
 
+    private void maybeShowToastH(int newRingerMode) {
+        int seenToastCount = Prefs.getInt(mContext, Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT, 0);
+
+        if (seenToastCount > VolumePrefs.SHOW_RINGER_TOAST_COUNT) {
+            return;
+        }
+        int toastText;
+        switch (newRingerMode) {
+            case RINGER_MODE_NORMAL:
+                toastText = R.string.volume_dialog_ringer_guidance_ring;
+                break;
+            case RINGER_MODE_SILENT:
+                toastText = R.string.volume_dialog_ringer_guidance_silent;
+                break;
+            case RINGER_MODE_VIBRATE:
+            default:
+                toastText = R.string.volume_dialog_ringer_guidance_vibrate;
+        }
+
+        Toast.makeText(mContext, toastText, Toast.LENGTH_SHORT).show();
+        seenToastCount++;
+        Prefs.putInt(mContext, Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT, seenToastCount);
+    }
+
     public void show(int reason) {
         mHandler.obtainMessage(H.SHOW, reason, 0).sendToTarget();
     }
@@ -501,15 +523,6 @@
         }
     }
 
-    private boolean isAttached() {
-        return mDialogView != null && mDialogView.isAttachedToWindow();
-    }
-
-    private boolean hasTouchFeature() {
-        final PackageManager pm = mContext.getPackageManager();
-        return pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
-    }
-
     private boolean shouldBeVisibleH(VolumeRow row, VolumeRow activeRow) {
         boolean isActive = row == activeRow;
         if (row.stream == AudioSystem.STREAM_ACCESSIBILITY) {
@@ -536,20 +549,12 @@
             final boolean isActive = row == activeRow;
             final boolean shouldBeVisible = shouldBeVisibleH(row, activeRow);
             Util.setVisOrGone(row.view, shouldBeVisible);
-            Util.setVisOrGone(row.header, shouldBeVisible);
             if (row.view.isShown()) {
                 updateVolumeRowSliderTintH(row, isActive);
             }
         }
     }
 
-    protected void updateConnectedDeviceH(String deviceName) {
-        for (final VolumeRow row : mRows) {
-            row.connectedDevice.setText(deviceName);
-            Util.setVisOrGone(row.connectedDevice, !TextUtils.isEmpty(deviceName));
-        }
-    }
-
     protected void updateRingerH() {
         if (mState != null) {
             final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
@@ -560,12 +565,10 @@
             enableRingerViewsH(mState.zenMode == Global.ZEN_MODE_OFF || !mState.disallowRinger);
             switch (mState.ringerModeInternal) {
                 case AudioManager.RINGER_MODE_VIBRATE:
-                    mRingerStatus.setText(R.string.volume_ringer_status_vibrate);
                     mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate);
                     mRingerIcon.setTag(Events.ICON_STATE_VIBRATE);
                     break;
                 case AudioManager.RINGER_MODE_SILENT:
-                    mRingerStatus.setText(R.string.volume_ringer_status_silent);
                     mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
                     mRingerIcon.setContentDescription(mContext.getString(
                             R.string.volume_stream_content_description_unmute,
@@ -576,14 +579,12 @@
                 default:
                     boolean muted = (mAutomute && ss.level == 0) || ss.muted;
                     if (muted) {
-                        mRingerStatus.setText(R.string.volume_ringer_status_silent);
                         mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
                         mRingerIcon.setContentDescription(mContext.getString(
                                 R.string.volume_stream_content_description_unmute,
                                 getStreamLabelH(ss)));
                         mRingerIcon.setTag(Events.ICON_STATE_MUTE);
                     } else {
-                        mRingerStatus.setText(R.string.volume_ringer_status_normal);
                         mRingerIcon.setImageResource(R.drawable.ic_volume_ringer);
                         if (mController.hasVibrator()) {
                             mRingerIcon.setContentDescription(mContext.getString(
@@ -603,12 +604,11 @@
     }
 
     /**
-     * Toggles enable state of views in a VolumeRow (not including seekbar, outputChooser or icon)
+     * Toggles enable state of views in a VolumeRow (not including seekbar or icon)
      * Hides/shows zen icon
      * @param enable whether to enable volume row views and hide dnd icon
      */
     private void enableVolumeRowViewsH(VolumeRow row, boolean enable) {
-        row.header.setEnabled(enable);
         row.dndIcon.setVisibility(enable ? View.GONE : View.VISIBLE);
     }
 
@@ -618,8 +618,6 @@
      * @param enable whether to enable ringer views and hide dnd icon
      */
     private void enableRingerViewsH(boolean enable) {
-        mRingerTitle.setEnabled(enable);
-        mRingerStatus.setEnabled(enable);
         mRingerIcon.setEnabled(enable);
         mZenIcon.setVisibility(enable ? View.GONE : View.VISIBLE);
     }
@@ -889,24 +887,6 @@
         rescheduleTimeoutH();
     }
 
-    private void showOutputChooserH() {
-        synchronized (mOutputChooserLock) {
-            if (mOutputChooserDialog != null) {
-                return;
-            }
-            mOutputChooserDialog = new OutputChooserDialog(mContext,
-                    new MediaRouterWrapper(MediaRouter.getInstance(mContext))) {
-                @Override
-                protected void cleanUp() {
-                    synchronized (mOutputChooserLock) {
-                        mOutputChooserDialog = null;
-                    }
-                }
-            };
-            mOutputChooserDialog.show();
-        }
-    }
-
     private String getStreamLabelH(StreamState ss) {
         if (ss.remoteLabel != null) {
             return ss.remoteLabel;
@@ -919,14 +899,6 @@
         }
     }
 
-    private final OnClickListener mClickOutputChooser = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            dismissH(DISMISS_REASON_OUTPUT_CHOOSER);
-            showOutputChooserH();
-        }
-    };
-
     private final VolumeDialogController.Callbacks mControllerCallbackH
             = new VolumeDialogController.Callbacks() {
         @Override
@@ -991,11 +963,6 @@
             }
 
         }
-
-        @Override
-        public void onConnectedDeviceChanged(String deviceName) {
-            updateConnectedDeviceH(deviceName);
-        }
     };
 
     private final class H extends Handler {
@@ -1184,8 +1151,6 @@
         private ObjectAnimator anim;  // slider progress animation for non-touch-related updates
         private int animTargetProgress;
         private int lastAudibleLevel = 1;
-        private View outputChooser;
-        private TextView connectedDevice;
         private ImageView dndIcon;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java
index 04339eb..173400f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java
@@ -43,6 +43,8 @@
     public static final String PREF_ADJUST_ALARMS = "pref_adjust_alarms";
     public static final String PREF_ADJUST_NOTIFICATION = "pref_adjust_notification";
 
+    public static final int SHOW_RINGER_TOAST_COUNT = 9;
+
     public static final boolean DEFAULT_SHOW_HEADERS = true;
     public static final boolean DEFAULT_ENABLE_AUTOMUTE = true;
     public static final boolean DEFAULT_ENABLE_SILENT_MODE = true;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 1c9c794..6764634 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -37,6 +37,7 @@
 import android.content.Intent;
 import android.metrics.LogMaker;
 import android.support.test.filters.SmallTest;
+import android.support.test.InstrumentationRegistry;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
@@ -73,6 +74,7 @@
         mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
         mHost = mock(QSTileHost.class);
         when(mHost.indexOf(spec)).thenReturn(POSITION);
+        when(mHost.getContext()).thenReturn(mContext.getBaseContext());
 
         mTile = spy(new TileImpl(mHost));
         mTile.mHandler = mTile.new H(mTestableLooper.getLooper());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 43e16db..8347fb0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -31,6 +31,7 @@
 import static org.mockito.Mockito.when;
 
 import android.animation.Animator;
+import android.animation.ValueAnimator;
 import android.app.AlarmManager;
 import android.graphics.Color;
 import android.os.Handler;
@@ -180,6 +181,7 @@
 
     @Test
     public void transitionToUnlocked() {
+        mScrimController.setPanelExpansion(0f);
         mScrimController.transitionTo(ScrimState.UNLOCKED);
         mScrimController.finishAnimationsImmediately();
         // Front scrim should be transparent
@@ -197,6 +199,7 @@
     public void transitionToUnlockedFromAod() {
         // Simulate unlock with fingerprint
         mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.setPanelExpansion(0f);
         mScrimController.finishAnimationsImmediately();
         mScrimController.transitionTo(ScrimState.UNLOCKED);
         // Immediately tinted after the transition starts
@@ -324,6 +327,35 @@
         verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
     }
 
+    @Test
+    public void testConservesExpansionOpacityAfterTransition() {
+        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.setPanelExpansion(0.5f);
+        mScrimController.finishAnimationsImmediately();
+
+        final float expandedAlpha = mScrimBehind.getViewAlpha();
+
+        mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
+        mScrimController.finishAnimationsImmediately();
+        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.finishAnimationsImmediately();
+
+        Assert.assertEquals("Scrim expansion opacity wasn't conserved when transitioning back",
+                expandedAlpha, mScrimBehind.getViewAlpha(), 0.01f);
+    }
+
+    @Test
+    public void cancelsOldAnimationBeforeBlanking() {
+        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.finishAnimationsImmediately();
+        // Consume whatever value we had before
+        mScrimController.wasAnimationJustCancelled();
+
+        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.finishAnimationsImmediately();
+        Assert.assertTrue(mScrimController.wasAnimationJustCancelled());
+    }
+
     private void assertScrimTint(ScrimView scrimView, boolean tinted) {
         final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT;
         final String name = scrimView == mScrimInFront ? "front" : "back";
@@ -357,6 +389,7 @@
     private class SynchronousScrimController extends ScrimController {
 
         private FakeHandler mHandler;
+        private boolean mAnimationCancelled;
 
         public SynchronousScrimController(LightBarController lightBarController,
                 ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
@@ -385,6 +418,12 @@
             }
         }
 
+        public boolean wasAnimationJustCancelled() {
+            final boolean wasCancelled = mAnimationCancelled;
+            mAnimationCancelled = false;
+            return wasCancelled;
+        }
+
         private void endAnimation(ScrimView scrimView, int tag) {
             Animator animator = (Animator) scrimView.getTag(tag);
             if (animator != null) {
@@ -393,6 +432,12 @@
         }
 
         @Override
+        protected void cancelAnimator(ValueAnimator previousAnimator) {
+            super.cancelAnimator(previousAnimator);
+            mAnimationCancelled = true;
+        }
+
+        @Override
         protected Handler getHandler() {
             return mHandler;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
deleted file mode 100644
index 922bde7..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * 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.systemui.volume;
-
-import static com.android.systemui.volume.OutputChooserDialog.INCLUDE_MEDIA_ROUTES;
-
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.bluetooth.BluetoothProfile;
-import android.net.wifi.WifiManager;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.v7.media.MediaRouter;
-import android.telecom.TelecomManager;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.widget.TextView;
-
-import com.android.settingslib.bluetooth.CachedBluetoothDevice;
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.VolumeDialogController;
-import com.android.systemui.statusbar.policy.BluetoothController;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@Ignore
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class OutputChooserDialogTest extends SysuiTestCase {
-
-    OutputChooserDialog mDialog;
-
-    @Mock
-    private VolumeDialogController mVolumeController;
-    @Mock
-    private BluetoothController mController;
-    @Mock
-    private WifiManager mWifiManager;
-    @Mock
-    private TelecomManager mTelecomManager;
-
-    @Mock
-    private MediaRouterWrapper mRouter;
-
-
-    @Before
-    public void setup() throws Exception {
-        MockitoAnnotations.initMocks(this);
-
-        mVolumeController = mDependency.injectMockDependency(VolumeDialogController.class);
-        mController = mDependency.injectMockDependency(BluetoothController.class);
-        when(mWifiManager.isWifiEnabled()).thenReturn(true);
-
-        getContext().addMockSystemService(WifiManager.class, mWifiManager);
-        getContext().addMockSystemService(TelecomManager.class, mTelecomManager);
-
-        mDialog = new OutputChooserDialog(getContext(), mRouter);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        TestableLooper.get(this).processAllMessages();
-    }
-/*
-    @Test
-    public void testClickMediaRouterItemConnectsMedia() {
-        mDialog.show();
-
-        OutputChooserLayout.Item item = new OutputChooserLayout.Item();
-        item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER;
-        MediaRouter.RouteInfo info = mock(MediaRouter.RouteInfo.class);
-        when(info.isEnabled()).thenReturn(true);
-        item.tag = info;
-
-        mDialog.onDetailItemClick(item);
-        verify(info, times(1)).select();
-        verify(mController, never()).connect(any());
-        mDialog.dismiss();
-    }
-
-    @Test
-    public void testClickBtItemConnectsBt() {
-        mDialog.show();
-
-        OutputChooserLayout.Item item = new OutputChooserLayout.Item();
-        item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_BT;
-        CachedBluetoothDevice btDevice = mock(CachedBluetoothDevice.class);
-        when(btDevice.getMaxConnectionState()).thenReturn(BluetoothProfile.STATE_DISCONNECTED);
-        item.tag = btDevice;
-
-        mDialog.onDetailItemClick(item);
-        verify(mController, times(1)).connect(any());
-        mDialog.dismiss();
-    }
-
-    @Test
-    public void testTitleNotInCall() {
-        mDialog.show();
-
-        assertTrue(((TextView) mDialog.findViewById(R.id.title))
-                .getText().toString().contains("Media"));
-        mDialog.dismiss();
-    }
-
-    @Test
-    public void testTitleInCall() {
-        mDialog.setIsInCall(true);
-        mDialog.show();
-
-        assertTrue(((TextView) mDialog.findViewById(R.id.title))
-                .getText().toString().contains("Phone"));
-        mDialog.dismiss();
-    }
-*/
-    @Test
-    public void testNoMediaScanIfInCall() {
-        mDialog.setIsInCall(true);
-        mDialog.onAttachedToWindow();
-
-        verify(mRouter, never()).addCallback(any(), any(), anyInt());
-    }
-
-    @Test
-    public void testRegisterCallbacks() {
-        mDialog.setIsInCall(false);
-        mDialog.onAttachedToWindow();
-
-        if (INCLUDE_MEDIA_ROUTES) {
-            verify(mRouter, times(1)).addCallback(any(), any(), anyInt());
-        }
-        verify(mController, times(1)).addCallback(any());
-        verify(mVolumeController, times(1)).addCallback(any(), any());
-    }
-
-    @Test
-    public void testUnregisterCallbacks() {
-        mDialog.setIsInCall(false);
-        mDialog.onDetachedFromWindow();
-
-        verify(mRouter, times(1)).removeCallback(any());
-        verify(mController, times(1)).removeCallback(any());
-        verify(mVolumeController, times(1)).removeCallback(any());
-    }
-}
diff --git a/packages/VpnDialogs/res/values-as/strings.xml b/packages/VpnDialogs/res/values-as/strings.xml
new file mode 100644
index 0000000..4f16c74
--- /dev/null
+++ b/packages/VpnDialogs/res/values-as/strings.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="prompt" msgid="3183836924226407828">"সংযোগৰ অনুৰোধ"</string>
+    <string name="warning" msgid="809658604548412033">"<xliff:g id="APP">%s</xliff:g>এ নেটৱৰ্ক ট্ৰেফিক নিৰীক্ষণ কৰিবলৈ এটা ভিপিএন সংযোগ ছেট আপ কৰিবলৈ বিচাৰিছে৷ আপুনি কেৱল উৎসটোক বিশ্বাস কৰিলেহে অনুৰোধ স্বীকাৰ কৰিব৷ ভিপিএন সক্ৰিয় থকাৰ সময়ত আপোনাৰ স্ক্ৰীণৰ ওপৰত &lt;br /&gt; &lt;br /&gt; &lt;img src=vpn_icon /&gt; দৃশ্যমান হয়৷"</string>
+    <string name="legacy_title" msgid="192936250066580964">"ভিপিএন সংযোগ হৈ আছে"</string>
+    <string name="session" msgid="6470628549473641030">"ছেশ্বন:"</string>
+    <string name="duration" msgid="3584782459928719435">"সময়সীমা:"</string>
+    <string name="data_transmitted" msgid="7988167672982199061">"পঠিওৱা হ\'ল:"</string>
+    <string name="data_received" msgid="4062776929376067820">"পোৱা গ\'ল:"</string>
+    <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_1">%2$s</xliff:g> পেকেট / <xliff:g id="NUMBER_0">%1$s</xliff:g> বাইট"</string>
+    <!-- no translation found for always_on_disconnected_title (1906740176262776166) -->
+    <skip />
+    <!-- no translation found for always_on_disconnected_message (555634519845992917) -->
+    <skip />
+    <!-- no translation found for always_on_disconnected_message_lockdown (4232225539869452120) -->
+    <skip />
+    <!-- no translation found for always_on_disconnected_message_separator (3310614409322581371) -->
+    <skip />
+    <!-- no translation found for always_on_disconnected_message_settings_link (6172280302829992412) -->
+    <skip />
+    <string name="configure" msgid="4905518375574791375">"কনফিগাৰ কৰক"</string>
+    <string name="disconnect" msgid="971412338304200056">"সংযোগ বিচ্ছিন্ন কৰক"</string>
+    <!-- no translation found for open_app (3717639178595958667) -->
+    <skip />
+    <!-- no translation found for dismiss (6192859333764711227) -->
+    <skip />
+</resources>
diff --git a/packages/VpnDialogs/res/values-or/strings.xml b/packages/VpnDialogs/res/values-or/strings.xml
new file mode 100644
index 0000000..3b4eedd
--- /dev/null
+++ b/packages/VpnDialogs/res/values-or/strings.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- no translation found for prompt (3183836924226407828) -->
+    <skip />
+    <!-- no translation found for warning (809658604548412033) -->
+    <skip />
+    <string name="legacy_title" msgid="192936250066580964">"VPN ସଂଯୋଗ ହେଲା"</string>
+    <string name="session" msgid="6470628549473641030">"ସେସନ୍‍:"</string>
+    <string name="duration" msgid="3584782459928719435">"ଅବଧି:"</string>
+    <string name="data_transmitted" msgid="7988167672982199061">"ପଠାଯାଇଛି:"</string>
+    <string name="data_received" msgid="4062776929376067820">"ପ୍ରାପ୍ତ ହୋଇଛି:"</string>
+    <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> ବାଇଟ୍ସ <xliff:g id="NUMBER_1">%2$s</xliff:g> ପ୍ୟାକେଟ୍ସ"</string>
+    <!-- no translation found for always_on_disconnected_title (1906740176262776166) -->
+    <skip />
+    <!-- no translation found for always_on_disconnected_message (555634519845992917) -->
+    <skip />
+    <!-- no translation found for always_on_disconnected_message_lockdown (4232225539869452120) -->
+    <skip />
+    <!-- no translation found for always_on_disconnected_message_separator (3310614409322581371) -->
+    <skip />
+    <!-- no translation found for always_on_disconnected_message_settings_link (6172280302829992412) -->
+    <skip />
+    <string name="configure" msgid="4905518375574791375">"କନଫିଗର୍‍ କରନ୍ତୁ"</string>
+    <string name="disconnect" msgid="971412338304200056">"ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ"</string>
+    <!-- no translation found for open_app (3717639178595958667) -->
+    <skip />
+    <!-- no translation found for dismiss (6192859333764711227) -->
+    <skip />
+</resources>
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 7e86ef5..c0e5960 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5283,6 +5283,40 @@
     // OS: P
     USB_DEFAULT = 1312;
 
+    // CATEGORY: The category for all actions related to TextClassifier generateLinks.
+    // OS: P
+    TEXT_CLASSIFIER_GENERATE_LINKS = 1313;
+
+    // FIELD: milliseconds spent generating links.
+    // CATEGORY: TEXT_CLASSIFIER_GENERATE_LINKS
+    // OS: P
+    FIELD_LINKIFY_LATENCY = 1314;
+
+    // FIELD: length of the input text in characters.
+    // CATEGORY: TEXT_CLASSIFIER_GENERATE_LINKS
+    // OS: P
+    FIELD_LINKIFY_TEXT_LENGTH = 1315;
+
+    // FIELD: number of links detected.
+    // CATEGORY: TEXT_CLASSIFIER_GENERATE_LINKS
+    // OS: P
+    FIELD_LINKIFY_NUM_LINKS = 1316;
+
+    // FIELD: length of all links in characters.
+    // CATEGORY: TEXT_CLASSIFIER_GENERATE_LINKS
+    // OS: P
+    FIELD_LINKIFY_LINK_LENGTH = 1317;
+
+    // FIELD: the type of entity the stats are for.
+    // CATEGORY: TEXT_CLASSIFIER_GENERATE_LINKS
+    // OS: P
+    FIELD_LINKIFY_ENTITY_TYPE = 1318;
+
+    // FIELD: a random uid for a single call to generateLinks
+    // CATEGORY: TEXT_CLASSIFIER_GENERATE_LINKS
+    // OS: P
+    FIELD_LINKIFY_CALL_ID = 1319;
+
     // ---- End P Constants, all P constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index c201de4..8622dbe 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -41,6 +41,7 @@
 import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteCallbackList;
@@ -76,7 +77,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.os.HandlerCaller;
 import com.android.server.LocalServices;
 import com.android.server.autofill.ui.AutoFillUI;
 
@@ -99,8 +99,6 @@
     /** Minimum interval to prune abandoned sessions */
     private static final int MAX_ABANDONED_SESSION_MILLIS = 30000;
 
-    static final int MSG_SERVICE_SAVE = 1;
-
     private final int mUserId;
     private final Context mContext;
     private final Object mLock;
@@ -151,18 +149,7 @@
     @GuardedBy("mLock")
     private boolean mSetupComplete;
 
-    private final HandlerCaller.Callback mHandlerCallback = (msg) -> {
-        switch (msg.what) {
-            case MSG_SERVICE_SAVE:
-                handleSessionSave(msg.arg1);
-                break;
-            default:
-                Slog.w(TAG, "invalid msg on handler: " + msg);
-        }
-    };
-
-    private final HandlerCaller mHandlerCaller = new HandlerCaller(null, Looper.getMainLooper(),
-            mHandlerCallback, true);
+    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
 
     /**
      * Cache of pending {@link Session}s, keyed by sessionId.
@@ -508,7 +495,7 @@
 
         assertCallerLocked(componentName);
 
-        final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock,
+        final Session newSession = new Session(this, mUi, mContext, mHandler, mUserId, mLock,
                 sessionId, uid, activityToken, appCallbackToken, hasCallback, mUiLatencyHistory,
                 mWtfHistory, mInfo.getServiceInfo().getComponentName(), componentName, compatMode,
                 flags);
@@ -597,11 +584,10 @@
         mSessions.remove(sessionId);
     }
 
-    private void handleSessionSave(int sessionId) {
+    void handleSessionSave(Session session) {
         synchronized (mLock) {
-            final Session session = mSessions.get(sessionId);
-            if (session == null) {
-                Slog.w(TAG, "handleSessionSave(): already gone: " + sessionId);
+            if (mSessions.get(session.id) == null) {
+                Slog.w(TAG, "handleSessionSave(): already gone: " + session.id);
 
                 return;
             }
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index fe6d4c4..d4ecc28 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -18,6 +18,7 @@
 
 import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
 
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sVerbose;
 
@@ -32,7 +33,6 @@
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
 import android.os.ICancellationSignal;
-import android.os.Message;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -47,7 +47,6 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.HandlerCaller;
 import com.android.server.FgThread;
 
 import java.io.PrintWriter;
@@ -63,13 +62,14 @@
  */
 final class RemoteFillService implements DeathRecipient {
     private static final String LOG_TAG = "RemoteFillService";
-
     // How long after the last interaction with the service we would unbind
     private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
 
     // How long after we make a remote request to a fill service we timeout
     private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
 
+    private static final int MSG_UNBIND = 3;
+
     private final Context mContext;
 
     private final ComponentName mComponentName;
@@ -82,7 +82,7 @@
 
     private final ServiceConnection mServiceConnection = new RemoteServiceConnection();
 
-    private final HandlerCaller mHandler;
+    private final Handler mHandler;
 
     private IAutoFillService mAutoFillService;
 
@@ -115,14 +115,16 @@
         mComponentName = componentName;
         mIntent = new Intent(AutofillService.SERVICE_INTERFACE).setComponent(mComponentName);
         mUserId = userId;
-        mHandler = new MyHandler(context);
+        mHandler = new Handler(FgThread.getHandler().getLooper());
     }
 
     public void destroy() {
-        mHandler.obtainMessage(MyHandler.MSG_DESTROY).sendToTarget();
+        mHandler.sendMessage(obtainMessage(
+                RemoteFillService::handleDestroy, this));
     }
 
     private void handleDestroy() {
+        if (checkIfDestroyed()) return;
         if (mPendingRequest != null) {
             mPendingRequest.cancel();
             mPendingRequest = null;
@@ -133,10 +135,12 @@
 
     @Override
     public void binderDied() {
-        mHandler.obtainMessage(MyHandler.MSG_BINDER_DIED).sendToTarget();
+        mHandler.sendMessage(obtainMessage(
+                RemoteFillService::handleBinderDied, this));
     }
 
     private void handleBinderDied() {
+        if (checkIfDestroyed()) return;
         if (mAutoFillService != null) {
             mAutoFillService.asBinder().unlinkToDeath(this, 0);
         }
@@ -174,14 +178,17 @@
 
     public void onFillRequest(@NonNull FillRequest request) {
         cancelScheduledUnbind();
-        final PendingFillRequest pendingRequest = new PendingFillRequest(request, this);
-        mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, pendingRequest).sendToTarget();
+        scheduleRequest(new PendingFillRequest(request, this));
     }
 
     public void onSaveRequest(@NonNull SaveRequest request) {
         cancelScheduledUnbind();
-        final PendingSaveRequest pendingRequest = new PendingSaveRequest(request, this);
-        mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, pendingRequest).sendToTarget();
+        scheduleRequest(new PendingSaveRequest(request, this));
+    }
+
+    private void scheduleRequest(PendingRequest pendingRequest) {
+        mHandler.sendMessage(obtainMessage(
+                RemoteFillService::handlePendingRequest, this, pendingRequest));
     }
 
     // Note: we are dumping without a lock held so this is a bit racy but
@@ -204,21 +211,25 @@
     }
 
     private void cancelScheduledUnbind() {
-        mHandler.removeMessages(MyHandler.MSG_UNBIND);
+        mHandler.removeMessages(MSG_UNBIND);
     }
 
     private void scheduleUnbind() {
         cancelScheduledUnbind();
-        Message message = mHandler.obtainMessage(MyHandler.MSG_UNBIND);
-        mHandler.sendMessageDelayed(message, TIMEOUT_IDLE_BIND_MILLIS);
+        mHandler.sendMessageDelayed(
+                obtainMessage(RemoteFillService::handleUnbind, this)
+                        .setWhat(MSG_UNBIND),
+                TIMEOUT_IDLE_BIND_MILLIS);
     }
 
     private void handleUnbind() {
+        if (checkIfDestroyed()) return;
         ensureUnbound();
     }
 
     private void handlePendingRequest(PendingRequest pendingRequest) {
-        if (mDestroyed || mCompleted) {
+        if (checkIfDestroyed()) return;
+        if (mCompleted) {
             return;
         }
         if (!isBound()) {
@@ -283,7 +294,7 @@
 
     private void dispatchOnFillRequestSuccess(PendingRequest pendingRequest, int requestFlags,
             FillResponse response) {
-        mHandler.getHandler().post(() -> {
+        mHandler.post(() -> {
             if (handleResponseCallbackCommon(pendingRequest)) {
                 mCallbacks.onFillRequestSuccess(requestFlags, response,
                         mComponentName.getPackageName());
@@ -293,7 +304,7 @@
 
     private void dispatchOnFillRequestFailure(PendingRequest pendingRequest,
             @Nullable CharSequence message) {
-        mHandler.getHandler().post(() -> {
+        mHandler.post(() -> {
             if (handleResponseCallbackCommon(pendingRequest)) {
                 mCallbacks.onFillRequestFailure(message, mComponentName.getPackageName());
             }
@@ -301,7 +312,7 @@
     }
 
     private void dispatchOnFillTimeout(@NonNull ICancellationSignal cancellationSignal) {
-        mHandler.getHandler().post(() -> {
+        mHandler.post(() -> {
             try {
                 cancellationSignal.cancel();
             } catch (RemoteException e) {
@@ -312,7 +323,7 @@
 
     private void dispatchOnSaveRequestSuccess(PendingRequest pendingRequest,
             IntentSender intentSender) {
-        mHandler.getHandler().post(() -> {
+        mHandler.post(() -> {
             if (handleResponseCallbackCommon(pendingRequest)) {
                 mCallbacks.onSaveRequestSuccess(mComponentName.getPackageName(), intentSender);
             }
@@ -321,7 +332,7 @@
 
     private void dispatchOnSaveRequestFailure(PendingRequest pendingRequest,
             @Nullable CharSequence message) {
-        mHandler.getHandler().post(() -> {
+        mHandler.post(() -> {
             if (handleResponseCallbackCommon(pendingRequest)) {
                 mCallbacks.onSaveRequestFailure(message, mComponentName.getPackageName());
             }
@@ -378,44 +389,14 @@
         }
     }
 
-    private final class MyHandler extends HandlerCaller {
-        public static final int MSG_DESTROY = 1;
-        public static final int MSG_BINDER_DIED = 2;
-        public static final int MSG_UNBIND = 3;
-        public static final int MSG_ON_PENDING_REQUEST = 4;
-
-        public MyHandler(Context context) {
-            // Cannot use lambda - doesn't compile
-            super(context, FgThread.getHandler().getLooper(), new Callback() {
-                @Override
-                public void executeMessage(Message message) {
-                    if (mDestroyed) {
-                        if (sVerbose) {
-                            Slog.v(LOG_TAG, "Not handling " + message + " as service for "
-                                    + mComponentName + " is already destroyed");
-                        }
-                        return;
-                    }
-                    switch (message.what) {
-                        case MSG_DESTROY: {
-                            handleDestroy();
-                        } break;
-
-                        case MSG_BINDER_DIED: {
-                            handleBinderDied();
-                        } break;
-
-                        case MSG_UNBIND: {
-                            handleUnbind();
-                        } break;
-
-                        case MSG_ON_PENDING_REQUEST: {
-                            handlePendingRequest((PendingRequest) message.obj);
-                        } break;
-                    }
-                }
-            }, false);
+    private boolean checkIfDestroyed() {
+        if (mDestroyed) {
+            if (sVerbose) {
+                Slog.v(LOG_TAG, "Not handling operation as service for "
+                        + mComponentName + " is already destroyed");
+            }
         }
+        return mDestroyed;
     }
 
     private static abstract class PendingRequest implements Runnable {
@@ -433,7 +414,7 @@
 
         PendingRequest(RemoteFillService service) {
             mWeakService = new WeakReference<>(service);
-            mServiceHandler = service.mHandler.getHandler();
+            mServiceHandler = service.mHandler;
             mTimeoutTrigger = () -> {
                 synchronized (mLock) {
                     if (mCancelled) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index ef6ed08..32f03b3 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -26,6 +26,7 @@
 import static android.view.autofill.AutofillManager.ACTION_VIEW_ENTERED;
 import static android.view.autofill.AutofillManager.ACTION_VIEW_EXITED;
 
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sPartitionMaxCount;
 import static com.android.server.autofill.Helper.sVerbose;
@@ -49,6 +50,7 @@
 import android.metrics.LogMaker;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.Parcelable;
 import android.os.RemoteCallback;
@@ -117,7 +119,7 @@
     private static final String EXTRA_REQUEST_ID = "android.service.autofill.extra.REQUEST_ID";
 
     private final AutofillManagerServiceImpl mService;
-    private final HandlerCaller mHandlerCaller;
+    private final Handler mHandler;
     private final Object mLock;
     private final AutoFillUI mUi;
 
@@ -485,7 +487,7 @@
     }
 
     Session(@NonNull AutofillManagerServiceImpl service, @NonNull AutoFillUI ui,
-            @NonNull Context context, @NonNull HandlerCaller handlerCaller, int userId,
+            @NonNull Context context, @NonNull Handler handler, int userId,
             @NonNull Object lock, int sessionId, int uid, @NonNull IBinder activityToken,
             @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory,
             @NonNull LocalLog wtfHistory,
@@ -498,7 +500,7 @@
         mService = service;
         mLock = lock;
         mUi = ui;
-        mHandlerCaller = handlerCaller;
+        mHandler = handler;
         mRemoteFillService = new RemoteFillService(context, serviceComponentName, userId, this);
         mActivityToken = activityToken;
         mHasCallback = hasCallback;
@@ -726,8 +728,9 @@
         mService.setAuthenticationSelected(id, mClientState);
 
         final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex);
-        mHandlerCaller.getHandler().post(() -> startAuthentication(authenticationId,
-                intent, fillInIntent));
+        mHandler.sendMessage(obtainMessage(
+                Session::startAuthentication,
+                this, authenticationId, intent, fillInIntent));
     }
 
     // FillServiceCallbacks
@@ -746,7 +749,9 @@
                 return;
             }
         }
-        mHandlerCaller.getHandler().post(() -> autoFill(requestId, datasetIndex, dataset, true));
+        mHandler.sendMessage(obtainMessage(
+                Session::autoFill,
+                this, requestId, datasetIndex, dataset, true));
     }
 
     // AutoFillUiCallback
@@ -759,9 +764,9 @@
                 return;
             }
         }
-        mHandlerCaller.getHandler()
-                .obtainMessage(AutofillManagerServiceImpl.MSG_SERVICE_SAVE, id, 0)
-                .sendToTarget();
+        mHandler.sendMessage(obtainMessage(
+                AutofillManagerServiceImpl::handleSessionSave,
+                mService, this));
     }
 
     // AutoFillUiCallback
@@ -776,7 +781,8 @@
                 return;
             }
         }
-        mHandlerCaller.getHandler().post(() -> removeSelf());
+        mHandler.sendMessage(obtainMessage(
+                Session::removeSelf, this));
     }
 
     // AutoFillUiCallback
@@ -831,15 +837,19 @@
             }
             removeSelfLocked();
         }
-        mHandlerCaller.getHandler().post(() -> {
-            try {
-                synchronized (mLock) {
-                    mClient.startIntentSender(intentSender, null);
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Error launching auth intent", e);
+        mHandler.sendMessage(obtainMessage(
+                Session::doStartIntentSender,
+                this, intentSender));
+    }
+
+    private void doStartIntentSender(IntentSender intentSender) {
+        try {
+            synchronized (mLock) {
+                mClient.startIntentSender(intentSender, null);
             }
-        });
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Error launching auth intent", e);
+        }
     }
 
     @GuardedBy("mLock")
@@ -960,11 +970,14 @@
      * when necessary.
      */
     public void logContextCommitted() {
-        mHandlerCaller.getHandler().post(() -> {
-            synchronized (mLock) {
-                logContextCommittedLocked();
-            }
-        });
+        mHandler.sendMessage(obtainMessage(
+                Session::doLogContextCommitted, this));
+    }
+
+    private void doLogContextCommitted() {
+        synchronized (mLock) {
+            logContextCommittedLocked();
+        }
     }
 
     @GuardedBy("mLock")
@@ -1486,7 +1499,8 @@
                 }
 
                 // Use handler so logContextCommitted() is logged first
-                mHandlerCaller.getHandler().post(() -> mService.logSaveShown(id, mClientState));
+                mHandler.sendMessage(obtainMessage(
+                        Session::logSaveShown, this));
 
                 final IAutoFillManagerClient client = getClient();
                 mPendingSaveUi = new PendingUi(mActivityToken, id, client);
@@ -1514,6 +1528,10 @@
         return true;
     }
 
+    private void logSaveShown() {
+        mService.logSaveShown(id, mClientState);
+    }
+
     @Nullable
     private ArrayMap<AutofillId, InternalSanitizer> createSanitizers(@Nullable SaveInfo saveInfo) {
         if (saveInfo == null) return null;
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 1d5e47a..c570005 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -1706,6 +1706,13 @@
             if (cs != null) {
                 clearClientSessionLocked(cs);
                 if (mCurClient == cs) {
+                    if (mBoundToMethod) {
+                        mBoundToMethod = false;
+                        if (mCurMethod != null) {
+                            executeOrSendMessage(mCurMethod, mCaller.obtainMessageO(
+                                    MSG_UNBIND_INPUT, mCurMethod));
+                        }
+                    }
                     mCurClient = null;
                 }
                 if (mCurFocusedWindowClient == cs) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 5e5eacb..8b5176e 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -35,6 +35,7 @@
 import android.telephony.CellInfo;
 import android.telephony.CellLocation;
 import android.telephony.DisconnectCause;
+import android.telephony.LocationAccessPolicy;
 import android.telephony.PhoneStateListener;
 import android.telephony.PreciseCallState;
 import android.telephony.PreciseDataConnectionState;
@@ -55,6 +56,7 @@
 import com.android.internal.telephony.PhoneConstantConversions;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyPermissions;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.am.BatteryStatsService;
@@ -96,7 +98,8 @@
         IPhoneStateListener callback;
         IOnSubscriptionsChangedListener onSubscriptionsChangedListenerCallback;
 
-        int callerUserId;
+        int callerUid;
+        int callerPid;
 
         int events;
 
@@ -120,7 +123,7 @@
                     + " callback=" + callback
                     + " onSubscriptionsChangedListenererCallback="
                                             + onSubscriptionsChangedListenerCallback
-                    + " callerUserId=" + callerUserId + " subId=" + subId + " phoneId=" + phoneId
+                    + " callerUid=" + callerUid + " subId=" + subId + " phoneId=" + phoneId
                     + " events=" + Integer.toHexString(events)
                     + " canReadPhoneState=" + canReadPhoneState + "}";
         }
@@ -374,26 +377,17 @@
     public void addOnSubscriptionsChangedListener(String callingPackage,
             IOnSubscriptionsChangedListener callback) {
         int callerUserId = UserHandle.getCallingUserId();
+        mContext.getSystemService(AppOpsManager.class)
+                .checkPackage(Binder.getCallingUid(), callingPackage);
         if (VDBG) {
             log("listen oscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId()
                 + " callerUserId="  + callerUserId + " callback=" + callback
                 + " callback.asBinder=" + callback.asBinder());
         }
 
-        try {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
-                    "addOnSubscriptionsChangedListener");
-            // SKIP checking for run-time permission since caller or self has PRIVILEGED permission
-        } catch (SecurityException e) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.READ_PHONE_STATE,
-                    "addOnSubscriptionsChangedListener");
-
-            if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
-                    callingPackage) != AppOpsManager.MODE_ALLOWED) {
-                return;
-            }
+        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+                mContext, callingPackage, "addOnSubscriptionsChangedListener")) {
+            return;
         }
 
 
@@ -408,7 +402,8 @@
 
             r.onSubscriptionsChangedListenerCallback = callback;
             r.callingPackage = callingPackage;
-            r.callerUserId = callerUserId;
+            r.callerUid = Binder.getCallingUid();
+            r.callerPid = Binder.getCallingPid();
             r.events = 0;
             r.canReadPhoneState = true; // permission has been enforced above
             if (DBG) {
@@ -479,6 +474,8 @@
     private void listen(String callingPackage, IPhoneStateListener callback, int events,
             boolean notifyNow, int subId) {
         int callerUserId = UserHandle.getCallingUserId();
+        mContext.getSystemService(AppOpsManager.class)
+                .checkPackage(Binder.getCallingUid(), callingPackage);
         if (VDBG) {
             log("listen: E pkg=" + callingPackage + " events=0x" + Integer.toHexString(events)
                 + " notifyNow=" + notifyNow + " subId=" + subId + " myUserId="
@@ -486,23 +483,14 @@
         }
 
         if (events != PhoneStateListener.LISTEN_NONE) {
-            /* Checks permission and throws Security exception */
-            checkListenerPermission(events);
-
-            if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
-                try {
-                    mContext.enforceCallingOrSelfPermission(
-                            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
-                    // SKIP checking for run-time permission since caller or self has PRIVILEGED
-                    // permission
-                } catch (SecurityException e) {
-                    if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
-                            callingPackage) != AppOpsManager.MODE_ALLOWED) {
-                        return;
-                    }
-                }
+            // Checks permission and throws SecurityException for disallowed operations. For pre-M
+            // apps whose runtime permission has been revoked, we return immediately to skip sending
+            // events to the app without crashing it.
+            if (!checkListenerPermission(events, callingPackage, "listen")) {
+                return;
             }
 
+            int phoneId = SubscriptionManager.getPhoneId(subId);
             synchronized (mRecords) {
                 // register
                 IBinder b = callback.asBinder();
@@ -514,10 +502,12 @@
 
                 r.callback = callback;
                 r.callingPackage = callingPackage;
-                r.callerUserId = callerUserId;
+                r.callerUid = Binder.getCallingUid();
+                r.callerPid = Binder.getCallingPid();
                 boolean isPhoneStateEvent = (events & (CHECK_PHONE_STATE_PERMISSION_MASK
                         | ENFORCE_PHONE_STATE_PERMISSION_MASK)) != 0;
-                r.canReadPhoneState = isPhoneStateEvent && canReadPhoneState(callingPackage);
+                r.canReadPhoneState =
+                        isPhoneStateEvent && canReadPhoneState(callingPackage, "listen");
                 // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
                 // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
                 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -525,9 +515,7 @@
                  } else {//APP specify subID
                     r.subId = subId;
                 }
-                r.phoneId = SubscriptionManager.getPhoneId(r.subId);
-
-                int phoneId = r.phoneId;
+                r.phoneId = phoneId;
                 r.events = events;
                 if (DBG) {
                     log("listen:  Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
@@ -572,8 +560,10 @@
                         try {
                             if (DBG_LOC) log("listen: mCellLocation = "
                                     + mCellLocation[phoneId]);
-                            r.callback.onCellLocationChanged(
-                                    new Bundle(mCellLocation[phoneId]));
+                            if (checkLocationAccess(r)) {
+                                r.callback.onCellLocationChanged(
+                                        new Bundle(mCellLocation[phoneId]));
+                            }
                         } catch (RemoteException ex) {
                             remove(r.binder);
                         }
@@ -619,7 +609,9 @@
                         try {
                             if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
                                     + mCellInfo.get(phoneId));
-                            r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+                            if (checkLocationAccess(r)) {
+                                r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+                            }
                         } catch (RemoteException ex) {
                             remove(r.binder);
                         }
@@ -675,21 +667,13 @@
         }
     }
 
-    private boolean canReadPhoneState(String callingPackage) {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) ==
-                PackageManager.PERMISSION_GRANTED) {
-            // SKIP checking for run-time permission since caller or self has PRIVILEGED permission
-            return true;
-        }
-        boolean canReadPhoneState = mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED;
-        if (canReadPhoneState &&
-                mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
-                        callingPackage) != AppOpsManager.MODE_ALLOWED) {
+    private boolean canReadPhoneState(String callingPackage, String message) {
+        try {
+            return TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+                    mContext, callingPackage, message);
+        } catch (SecurityException e) {
             return false;
         }
-        return canReadPhoneState;
     }
 
     private String getCallIncomingNumber(Record record, int phoneId) {
@@ -1013,14 +997,14 @@
             log("notifyCellInfoForSubscriber: subId=" + subId
                 + " cellInfo=" + cellInfo);
         }
-
+        int phoneId = SubscriptionManager.getPhoneId(subId);
         synchronized (mRecords) {
-            int phoneId = SubscriptionManager.getPhoneId(subId);
             if (validatePhoneId(phoneId)) {
                 mCellInfo.set(phoneId, cellInfo);
                 for (Record r : mRecords) {
                     if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) &&
-                            idMatch(r.subId, subId, phoneId)) {
+                            idMatch(r.subId, subId, phoneId) &&
+                            checkLocationAccess(r)) {
                         try {
                             if (DBG_LOC) {
                                 log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r);
@@ -1103,8 +1087,8 @@
             log("notifyCallForwardingChangedForSubscriber: subId=" + subId
                 + " cfi=" + cfi);
         }
+        int phoneId = SubscriptionManager.getPhoneId(subId);
         synchronized (mRecords) {
-            int phoneId = SubscriptionManager.getPhoneId(subId);
             if (validatePhoneId(phoneId)) {
                 mCallForwarding[phoneId] = cfi;
                 for (Record r : mRecords) {
@@ -1131,8 +1115,8 @@
         if (!checkNotifyPermission("notifyDataActivity()" )) {
             return;
         }
+        int phoneId = SubscriptionManager.getPhoneId(subId);
         synchronized (mRecords) {
-            int phoneId = SubscriptionManager.getPhoneId(subId);
             if (validatePhoneId(phoneId)) {
                 mDataActivity[phoneId] = state;
                 for (Record r : mRecords) {
@@ -1173,8 +1157,8 @@
                 + "' apn='" + apn + "' apnType=" + apnType + " networkType=" + networkType
                 + " mRecords.size()=" + mRecords.size());
         }
+        int phoneId = SubscriptionManager.getPhoneId(subId);
         synchronized (mRecords) {
-            int phoneId = SubscriptionManager.getPhoneId(subId);
             if (validatePhoneId(phoneId)) {
                 boolean modified = false;
                 if (state == TelephonyManager.DATA_CONNECTED) {
@@ -1297,13 +1281,14 @@
             log("notifyCellLocationForSubscriber: subId=" + subId
                 + " cellLocation=" + cellLocation);
         }
+        int phoneId = SubscriptionManager.getPhoneId(subId);
         synchronized (mRecords) {
-            int phoneId = SubscriptionManager.getPhoneId(subId);
             if (validatePhoneId(phoneId)) {
                 mCellLocation[phoneId] = cellLocation;
                 for (Record r : mRecords) {
                     if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) &&
-                            idMatch(r.subId, subId, phoneId)) {
+                            idMatch(r.subId, subId, phoneId) &&
+                            checkLocationAccess(r)) {
                         try {
                             if (DBG_LOC) {
                                 log("notifyCellLocation: cellLocation=" + cellLocation
@@ -1660,11 +1645,12 @@
     }
 
     private void enforceNotifyPermissionOrCarrierPrivilege(String method) {
-        if  (checkNotifyPermission()) {
+        if (checkNotifyPermission()) {
             return;
         }
 
-        enforceCarrierPrivilege();
+        TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+                SubscriptionManager.getDefaultSubscriptionId(), method);
     }
 
     private boolean checkNotifyPermission(String method) {
@@ -1682,23 +1668,7 @@
                 == PackageManager.PERMISSION_GRANTED;
     }
 
-    private void enforceCarrierPrivilege() {
-        TelephonyManager tm = TelephonyManager.getDefault();
-        String[] pkgs = mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
-        for (String pkg : pkgs) {
-            if (tm.checkCarrierPrivilegesForPackage(pkg) ==
-                    TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
-                return;
-            }
-        }
-
-        String msg = "Carrier Privilege Permission Denial: from pid=" + Binder.getCallingPid()
-                + ", uid=" + Binder.getCallingUid();
-        if (DBG) log(msg);
-        throw new SecurityException(msg);
-    }
-
-    private void checkListenerPermission(int events) {
+    private boolean checkListenerPermission(int events, String callingPackage, String message) {
         if ((events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
@@ -1712,22 +1682,18 @@
         }
 
         if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
-            try {
-                mContext.enforceCallingOrSelfPermission(
-                        android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
-                // SKIP checking for run-time permission since caller or self has PRIVILEGED
-                // permission
-            } catch (SecurityException e) {
-                mContext.enforceCallingOrSelfPermission(
-                        android.Manifest.permission.READ_PHONE_STATE, null);
+            if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+                    mContext, callingPackage, message)) {
+                return false;
             }
         }
 
         if ((events & PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
-
         }
+
+        return true;
     }
 
     private void handleRemoveListLocked() {
@@ -1747,10 +1713,11 @@
         boolean valid = false;
         try {
             foregroundUser = ActivityManager.getCurrentUser();
-            valid = r.callerUserId ==  foregroundUser && r.matchPhoneStateListenerEvent(events);
+            valid = UserHandle.getUserId(r.callerUid) == foregroundUser
+                    && r.matchPhoneStateListenerEvent(events);
             if (DBG | DBG_LOC) {
                 log("validateEventsAndUserLocked: valid=" + valid
-                        + " r.callerUserId=" + r.callerUserId + " foregroundUser=" + foregroundUser
+                        + " r.callerUid=" + r.callerUid + " foregroundUser=" + foregroundUser
                         + " r.events=" + r.events + " events=" + events);
             }
         } finally {
@@ -1782,6 +1749,16 @@
         }
     }
 
+    private boolean checkLocationAccess(Record r) {
+        long token = Binder.clearCallingIdentity();
+        try {
+            return LocationAccessPolicy.canAccessCellLocation(mContext,
+                    r.callingPackage, r.callerUid, r.callerPid);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     private void checkPossibleMissNotify(Record r, int phoneId) {
         int events = r.events;
 
@@ -1829,7 +1806,9 @@
                     log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = "
                             + mCellInfo.get(phoneId));
                 }
-                r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+                if (checkLocationAccess(r)) {
+                    r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+                }
             } catch (RemoteException ex) {
                 mRemoveList.add(r.binder);
             }
@@ -1877,7 +1856,9 @@
             try {
                 if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = "
                         + mCellLocation[phoneId]);
-                r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
+                if (checkLocationAccess(r)) {
+                    r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
+                }
             } catch (RemoteException ex) {
                 mRemoveList.add(r.binder);
             }
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 8367916..4901192 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -65,7 +65,7 @@
     static final boolean DEBUG_NETWORK = DEBUG_ALL || false;
     static final boolean DEBUG_OOM_ADJ = DEBUG_ALL || false;
     static final boolean DEBUG_OOM_ADJ_REASON = DEBUG_ALL || false;
-    static final boolean DEBUG_PAUSE = DEBUG_ALL || true;
+    static final boolean DEBUG_PAUSE = DEBUG_ALL || false;
     static final boolean DEBUG_POWER = DEBUG_ALL || false;
     static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
     static final boolean DEBUG_PROCESS_OBSERVERS = DEBUG_ALL || false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e8f0113..633c165 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5451,24 +5451,6 @@
         }
     }
 
-    @Override
-    public final void requestActivityRelaunch(IBinder token) {
-        synchronized(this) {
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                return;
-            }
-            final long origId = Binder.clearCallingIdentity();
-            try {
-                r.forceNewConfig = true;
-                r.ensureActivityConfigurationLocked(0 /* globalChanges */,
-                        true /* preserveWindow */);
-            } finally {
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
     /**
      * This is the internal entry point for handling Activity.finish().
      *
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index fa0df56..dadd869 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -442,12 +442,11 @@
                     options.setTaskOverlay(true, true /* canResume */);
                 }
             }
-            android.util.Log.d("bfranz", "I was here: " + mIsLockTask);
             if (mIsLockTask) {
                 if (options == null) {
                     options = ActivityOptions.makeBasic();
                 }
-                options.setLockTaskMode(true);
+                options.setLockTaskEnabled(true);
             }
             if (mWaitOption) {
                 result = mInterface.startActivityAndWait(null, null, intent, mimeType,
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index e2ceb31..db86f1a 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -2,7 +2,6 @@
 
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -89,8 +88,10 @@
     private int mCurrentTransitionDelayMs;
     private boolean mLoggedTransitionStarting;
 
-    private final SparseArray<StackTransitionInfo> mStackTransitionInfo = new SparseArray<>();
-    private final SparseArray<StackTransitionInfo> mLastStackTransitionInfo = new SparseArray<>();
+    private final SparseArray<WindowingModeTransitionInfo> mWindowingModeTransitionInfo =
+            new SparseArray<>();
+    private final SparseArray<WindowingModeTransitionInfo> mLastWindowingModeTransitionInfo =
+            new SparseArray<>();
     private final H mHandler;
     private final class H extends Handler {
 
@@ -106,13 +107,13 @@
                     checkVisibility((TaskRecord) args.arg1, (ActivityRecord) args.arg2);
                     break;
                 case MSG_LOG_APP_START_MEMORY_STATE_CAPTURE:
-                    logAppStartMemoryStateCapture((StackTransitionInfo) msg.obj);
+                    logAppStartMemoryStateCapture((WindowingModeTransitionInfo) msg.obj);
                     break;
             }
         }
     };
 
-    private final class StackTransitionInfo {
+    private final class WindowingModeTransitionInfo {
         private ActivityRecord launchedActivity;
         private int startResult;
         private boolean currentTransitionProcessRunning;
@@ -242,56 +243,57 @@
 
         // If we are already in an existing transition, only update the activity name, but not the
         // other attributes.
-        final int stackId = launchedActivity != null && launchedActivity.getStack() != null
-                ? launchedActivity.getStack().mStackId
-                : INVALID_STACK_ID;
+        final int windowingMode = launchedActivity != null
+                ? launchedActivity.getWindowingMode()
+                : WINDOWING_MODE_UNDEFINED;
 
         if (mCurrentTransitionStartTime == INVALID_START_TIME) {
             return;
         }
 
-        final StackTransitionInfo info = mStackTransitionInfo.get(stackId);
+        final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
         if (launchedActivity != null && info != null) {
             info.launchedActivity = launchedActivity;
             return;
         }
 
-        final boolean otherStacksLaunching = mStackTransitionInfo.size() > 0 && info == null;
+        final boolean otherWindowModesLaunching =
+                mWindowingModeTransitionInfo.size() > 0 && info == null;
         if ((resultCode < 0 || launchedActivity == null || !processSwitch
-                || stackId == INVALID_STACK_ID) && !otherStacksLaunching) {
+                || windowingMode == WINDOWING_MODE_UNDEFINED) && !otherWindowModesLaunching) {
 
             // Failed to launch or it was not a process switch, so we don't care about the timing.
             reset(true /* abort */);
             return;
-        } else if (otherStacksLaunching) {
-            // Don't log this stack but continue with the other stacks.
+        } else if (otherWindowModesLaunching) {
+            // Don't log this windowing mode but continue with the other windowing modes.
             return;
         }
 
         if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched successful");
 
-        final StackTransitionInfo newInfo = new StackTransitionInfo();
+        final WindowingModeTransitionInfo newInfo = new WindowingModeTransitionInfo();
         newInfo.launchedActivity = launchedActivity;
         newInfo.currentTransitionProcessRunning = processRunning;
         newInfo.startResult = resultCode;
-        mStackTransitionInfo.put(stackId, newInfo);
-        mLastStackTransitionInfo.put(stackId, newInfo);
+        mWindowingModeTransitionInfo.put(windowingMode, newInfo);
+        mLastWindowingModeTransitionInfo.put(windowingMode, newInfo);
         mCurrentTransitionDeviceUptime = (int) (SystemClock.uptimeMillis() / 1000);
     }
 
     /**
      * Notifies the tracker that all windows of the app have been drawn.
      */
-    void notifyWindowsDrawn(int stackId, long timestamp) {
-        if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn stackId=" + stackId);
+    void notifyWindowsDrawn(int windowingMode, long timestamp) {
+        if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn windowingMode=" + windowingMode);
 
-        final StackTransitionInfo info = mStackTransitionInfo.get(stackId);
+        final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
         if (info == null || info.loggedWindowsDrawn) {
             return;
         }
         info.windowsDrawnDelayMs = calculateDelay(timestamp);
         info.loggedWindowsDrawn = true;
-        if (allStacksWindowsDrawn() && mLoggedTransitionStarting) {
+        if (allWindowsDrawn() && mLoggedTransitionStarting) {
             reset(false /* abort */);
         }
     }
@@ -299,8 +301,8 @@
     /**
      * Notifies the tracker that the starting window was drawn.
      */
-    void notifyStartingWindowDrawn(int stackId, long timestamp) {
-        final StackTransitionInfo info = mStackTransitionInfo.get(stackId);
+    void notifyStartingWindowDrawn(int windowingMode, long timestamp) {
+        final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
         if (info == null || info.loggedStartingWindowDrawn) {
             return;
         }
@@ -311,25 +313,26 @@
     /**
      * Notifies the tracker that the app transition is starting.
      *
-     * @param stackIdReasons A map from stack id to a reason integer, which must be on of
-     *                       ActivityManagerInternal.APP_TRANSITION_* reasons.
+     * @param windowingModeToReason A map from windowing mode to a reason integer, which must be on
+     *                              of ActivityManagerInternal.APP_TRANSITION_* reasons.
      */
-    void notifyTransitionStarting(SparseIntArray stackIdReasons, long timestamp) {
+    void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestamp) {
         if (!isAnyTransitionActive() || mLoggedTransitionStarting) {
             return;
         }
         if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");
         mCurrentTransitionDelayMs = calculateDelay(timestamp);
         mLoggedTransitionStarting = true;
-        for (int index = stackIdReasons.size() - 1; index >= 0; index--) {
-            final int stackId = stackIdReasons.keyAt(index);
-            final StackTransitionInfo info = mStackTransitionInfo.get(stackId);
+        for (int index = windowingModeToReason.size() - 1; index >= 0; index--) {
+            final int windowingMode = windowingModeToReason.keyAt(index);
+            final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
+                    windowingMode);
             if (info == null) {
                 continue;
             }
-            info.reason = stackIdReasons.valueAt(index);
+            info.reason = windowingModeToReason.valueAt(index);
         }
-        if (allStacksWindowsDrawn()) {
+        if (allWindowsDrawn()) {
             reset(false /* abort */);
         }
     }
@@ -340,7 +343,8 @@
      * @param activityRecord the app that is changing its visibility
      */
     void notifyVisibilityChanged(ActivityRecord activityRecord) {
-        final StackTransitionInfo info = mStackTransitionInfo.get(activityRecord.getStackId());
+        final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
+                activityRecord.getWindowingMode());
         if (info == null) {
             return;
         }
@@ -357,7 +361,8 @@
     private void checkVisibility(TaskRecord t, ActivityRecord r) {
         synchronized (mSupervisor.mService) {
 
-            final StackTransitionInfo info = mStackTransitionInfo.get(r.getStackId());
+            final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
+                    r.getWindowingMode());
 
             // If we have an active transition that's waiting on a certain activity that will be
             // invisible now, we'll never get onWindowsDrawn, so abort the transition if necessary.
@@ -365,8 +370,8 @@
                 if (DEBUG_METRICS) Slog.i(TAG, "notifyVisibilityChanged to invisible"
                         + " activity=" + r);
                 logAppTransitionCancel(info);
-                mStackTransitionInfo.remove(r.getStackId());
-                if (mStackTransitionInfo.size() == 0) {
+                mWindowingModeTransitionInfo.remove(r.getWindowingMode());
+                if (mWindowingModeTransitionInfo.size() == 0) {
                     reset(true /* abort */);
                 }
             }
@@ -379,8 +384,8 @@
      * @param app The client into which we'll call bindApplication.
      */
     void notifyBindApplication(ProcessRecord app) {
-        for (int i = mStackTransitionInfo.size() - 1; i >= 0; i--) {
-            final StackTransitionInfo info = mStackTransitionInfo.valueAt(i);
+        for (int i = mWindowingModeTransitionInfo.size() - 1; i >= 0; i--) {
+            final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.valueAt(i);
 
             // App isn't attached to record yet, so match with info.
             if (info.launchedActivity.appInfo == app.info) {
@@ -389,9 +394,9 @@
         }
     }
 
-    private boolean allStacksWindowsDrawn() {
-        for (int index = mStackTransitionInfo.size() - 1; index >= 0; index--) {
-            if (!mStackTransitionInfo.valueAt(index).loggedWindowsDrawn) {
+    private boolean allWindowsDrawn() {
+        for (int index = mWindowingModeTransitionInfo.size() - 1; index >= 0; index--) {
+            if (!mWindowingModeTransitionInfo.valueAt(index).loggedWindowsDrawn) {
                 return false;
             }
         }
@@ -400,7 +405,7 @@
 
     private boolean isAnyTransitionActive() {
         return mCurrentTransitionStartTime != INVALID_START_TIME
-                && mStackTransitionInfo.size() > 0;
+                && mWindowingModeTransitionInfo.size() > 0;
     }
 
     private void reset(boolean abort) {
@@ -411,7 +416,7 @@
         mCurrentTransitionStartTime = INVALID_START_TIME;
         mCurrentTransitionDelayMs = -1;
         mLoggedTransitionStarting = false;
-        mStackTransitionInfo.clear();
+        mWindowingModeTransitionInfo.clear();
     }
 
     private int calculateCurrentDelay() {
@@ -425,7 +430,7 @@
         return (int) (timestamp - mCurrentTransitionStartTime);
     }
 
-    private void logAppTransitionCancel(StackTransitionInfo info) {
+    private void logAppTransitionCancel(WindowingModeTransitionInfo info) {
         final int type = getTransitionType(info);
         if (type == -1) {
             return;
@@ -445,8 +450,8 @@
 
     private void logAppTransitionMultiEvents() {
         if (DEBUG_METRICS) Slog.i(TAG, "logging transition events");
-        for (int index = mStackTransitionInfo.size() - 1; index >= 0; index--) {
-            final StackTransitionInfo info = mStackTransitionInfo.valueAt(index);
+        for (int index = mWindowingModeTransitionInfo.size() - 1; index >= 0; index--) {
+            final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.valueAt(index);
             final int type = getTransitionType(info);
             if (type == -1) {
                 return;
@@ -513,7 +518,8 @@
      }
 
     void logAppTransitionReportedDrawn(ActivityRecord r, boolean restoredFromBundle) {
-        final StackTransitionInfo info = mLastStackTransitionInfo.get(r.getStackId());
+        final WindowingModeTransitionInfo info = mLastWindowingModeTransitionInfo.get(
+                r.getWindowingMode());
         if (info == null) {
             return;
         }
@@ -540,7 +546,7 @@
                 startupTimeMs);
     }
 
-    private int getTransitionType(StackTransitionInfo info) {
+    private int getTransitionType(WindowingModeTransitionInfo info) {
         if (info.currentTransitionProcessRunning) {
             if (info.startResult == START_SUCCESS) {
                 return TYPE_TRANSITION_WARM_LAUNCH;
@@ -553,7 +559,7 @@
         return -1;
     }
 
-    private void logAppStartMemoryStateCapture(StackTransitionInfo info) {
+    private void logAppStartMemoryStateCapture(WindowingModeTransitionInfo info) {
         final ProcessRecord processRecord = findProcessForActivity(info.launchedActivity);
         if (processRecord == null) {
             if (DEBUG_METRICS) Slog.i(TAG, "logAppStartMemoryStateCapture processRecord null");
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index ddba349..4e60924 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1625,20 +1625,6 @@
             // The activity may be waiting for stop, but that is no longer appropriate for it.
             mStackSupervisor.mStoppingActivities.remove(this);
             mStackSupervisor.mGoingToSleepActivities.remove(this);
-
-            // If the activity is stopped or stopping, cycle to the paused state.
-            if (state == STOPPED || state == STOPPING) {
-                // Capture reason before state change
-                final String reason = getLifecycleDescription("makeVisibleIfNeeded");
-
-                // An activity must be in the {@link PAUSING} state for the system to validate
-                // the move to {@link PAUSED}.
-                state = PAUSING;
-                service.mLifecycleManager.scheduleTransaction(app.thread, appToken,
-                        PauseActivityItem.obtain(finishing, false /* userLeaving */,
-                                configChangeFlags, false /* dontReport */)
-                                .setDescription(reason));
-            }
         } catch (Exception e) {
             // Just skip on any failure; we'll make it visible when it next restarts.
             Slog.w(TAG, "Exception thrown making visibile: " + intent.getComponent(), e);
@@ -1901,14 +1887,15 @@
     public void onStartingWindowDrawn(long timestamp) {
         synchronized (service) {
             mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(
-                    getStackId(), timestamp);
+                    getWindowingMode(), timestamp);
         }
     }
 
     @Override
     public void onWindowsDrawn(long timestamp) {
         synchronized (service) {
-            mStackSupervisor.getActivityMetricsLogger().notifyWindowsDrawn(getStackId(), timestamp);
+            mStackSupervisor.getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(),
+                    timestamp);
             if (displayStartTime != 0) {
                 reportLaunchTimeLocked(timestamp);
             }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 812de88..fe10670 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1419,6 +1419,11 @@
             return false;
         }
 
+        if (prev == resuming) {
+            Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed");
+            return false;
+        }
+
         if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSING: " + prev);
         else if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Start pausing: " + prev);
         mResumedActivity = null;
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index a82facd..9a3b102 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3341,7 +3341,11 @@
                     stack.goToSleepIfPossible(false /* shuttingDown */);
                 } else {
                     stack.awakeFromSleepingLocked();
-                    if (isFocusedStack(stack)) {
+                    if (isFocusedStack(stack)
+                            && !mKeyguardController.isKeyguardActive(display.mDisplayId)) {
+                        // If there is no keyguard on this display - resume immediately. Otherwise
+                        // we'll wait for keyguard visibility callback and resume while ensuring
+                        // activities visibility
                         resumeFocusedStackTopActivityLocked();
                     }
                 }
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 9776c4d..ed09879 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -793,10 +793,20 @@
         AppErrorDialog.Data data = (AppErrorDialog.Data) msg.obj;
         boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
+
+        AppErrorDialog dialogToShow = null;
+        final String packageName;
+        final int userId;
         synchronized (mService) {
-            ProcessRecord proc = data.proc;
-            AppErrorResult res = data.result;
-            if (proc != null && proc.crashDialog != null) {
+            final ProcessRecord proc = data.proc;
+            final AppErrorResult res = data.result;
+            if (proc == null) {
+                Slog.e(TAG, "handleShowAppErrorUi: proc is null");
+                return;
+            }
+            packageName = proc.info.packageName;
+            userId = proc.userId;
+            if (proc.crashDialog != null) {
                 Slog.e(TAG, "App already has crash dialog: " + proc);
                 if (res != null) {
                     res.set(AppErrorDialog.ALREADY_SHOWING);
@@ -806,8 +816,8 @@
             boolean isBackground = (UserHandle.getAppId(proc.uid)
                     >= Process.FIRST_APPLICATION_UID
                     && proc.pid != MY_PID);
-            for (int userId : mService.mUserController.getCurrentProfileIds()) {
-                isBackground &= (proc.userId != userId);
+            for (int profileId : mService.mUserController.getCurrentProfileIds()) {
+                isBackground &= (userId != profileId);
             }
             if (isBackground && !showBackground) {
                 Slog.w(TAG, "Skipping crash dialog of " + proc + ": background");
@@ -828,7 +838,7 @@
                     mAppsNotReportingCrashes.contains(proc.info.packageName);
             if ((mService.canShowErrorDialogs() || showBackground) && !crashSilenced
                     && (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
-                proc.crashDialog = new AppErrorDialog(mContext, mService, data);
+                proc.crashDialog = dialogToShow = new AppErrorDialog(mContext, mService, data);
             } else {
                 // The device is asleep, so just pretend that the user
                 // saw a crash dialog and hit "force quit".
@@ -838,10 +848,9 @@
             }
         }
         // If we've created a crash dialog, show it without the lock held
-        if(data.proc.crashDialog != null) {
-            Slog.i(TAG, "Showing crash dialog for package " + data.proc.info.packageName
-                    + " u" + data.proc.userId);
-            data.proc.crashDialog.show();
+        if (dialogToShow != null) {
+            Slog.i(TAG, "Showing crash dialog for package " + packageName + " u" + userId);
+            dialogToShow.show();
         }
     }
 
@@ -1071,14 +1080,8 @@
 
             // Bring up the infamous App Not Responding dialog
             Message msg = Message.obtain();
-            HashMap<String, Object> map = new HashMap<String, Object>();
             msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
-            msg.obj = map;
-            msg.arg1 = aboveSystem ? 1 : 0;
-            map.put("app", app);
-            if (activity != null) {
-                map.put("activity", activity);
-            }
+            msg.obj = new AppNotRespondingDialog.Data(app, activity, aboveSystem);
 
             mService.mUiHandler.sendMessage(msg);
         }
@@ -1095,11 +1098,15 @@
     }
 
     void handleShowAnrUi(Message msg) {
-        Dialog d = null;
+        Dialog dialogToShow = null;
         synchronized (mService) {
-            HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
-            ProcessRecord proc = (ProcessRecord)data.get("app");
-            if (proc != null && proc.anrDialog != null) {
+            AppNotRespondingDialog.Data data = (AppNotRespondingDialog.Data) msg.obj;
+            final ProcessRecord proc = data.proc;
+            if (proc == null) {
+                Slog.e(TAG, "handleShowAnrUi: proc is null");
+                return;
+            }
+            if (proc.anrDialog != null) {
                 Slog.e(TAG, "App already has anr dialog: " + proc);
                 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
                         AppNotRespondingDialog.ALREADY_SHOWING);
@@ -1118,10 +1125,8 @@
             boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
                     Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
             if (mService.canShowErrorDialogs() || showBackground) {
-                d = new AppNotRespondingDialog(mService,
-                        mContext, proc, (ActivityRecord)data.get("activity"),
-                        msg.arg1 != 0);
-                proc.anrDialog = d;
+                dialogToShow = new AppNotRespondingDialog(mService, mContext, data);
+                proc.anrDialog = dialogToShow;
             } else {
                 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
                         AppNotRespondingDialog.CANT_SHOW);
@@ -1130,8 +1135,8 @@
             }
         }
         // If we've created a crash dialog, show it without the lock held
-        if (d != null) {
-            d.show();
+        if (dialogToShow != null) {
+            dialogToShow.show();
         }
     }
 
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index d9c6a30..8a88a69 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -21,7 +21,6 @@
 
 import android.content.ActivityNotFoundException;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.os.Bundle;
@@ -49,36 +48,35 @@
     private final ActivityManagerService mService;
     private final ProcessRecord mProc;
 
-    public AppNotRespondingDialog(ActivityManagerService service, Context context,
-            ProcessRecord app, ActivityRecord activity, boolean aboveSystem) {
+    public AppNotRespondingDialog(ActivityManagerService service, Context context, Data data) {
         super(context);
 
         mService = service;
-        mProc = app;
+        mProc = data.proc;
         Resources res = context.getResources();
 
         setCancelable(false);
 
         int resid;
-        CharSequence name1 = activity != null
-                ? activity.info.loadLabel(context.getPackageManager())
+        CharSequence name1 = data.activity != null
+                ? data.activity.info.loadLabel(context.getPackageManager())
                 : null;
         CharSequence name2 = null;
-        if ((app.pkgList.size() == 1) &&
-                (name2=context.getPackageManager().getApplicationLabel(app.info)) != null) {
+        if ((mProc.pkgList.size() == 1) &&
+                (name2=context.getPackageManager().getApplicationLabel(mProc.info)) != null) {
             if (name1 != null) {
                 resid = com.android.internal.R.string.anr_activity_application;
             } else {
                 name1 = name2;
-                name2 = app.processName;
+                name2 = mProc.processName;
                 resid = com.android.internal.R.string.anr_application_process;
             }
         } else {
             if (name1 != null) {
-                name2 = app.processName;
+                name2 = mProc.processName;
                 resid = com.android.internal.R.string.anr_activity_process;
             } else {
-                name1 = app.processName;
+                name1 = mProc.processName;
                 resid = com.android.internal.R.string.anr_process;
             }
         }
@@ -89,11 +87,11 @@
                 ? res.getString(resid, bidi.unicodeWrap(name1.toString()), bidi.unicodeWrap(name2.toString()))
                 : res.getString(resid, bidi.unicodeWrap(name1.toString())));
 
-        if (aboveSystem) {
+        if (data.aboveSystem) {
             getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
         }
         WindowManager.LayoutParams attrs = getWindow().getAttributes();
-        attrs.setTitle("Application Not Responding: " + app.info.processName);
+        attrs.setTitle("Application Not Responding: " + mProc.info.processName);
         attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR |
                 WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
         getWindow().setAttributes(attrs);
@@ -180,4 +178,16 @@
             dismiss();
         }
     };
+
+    static class Data {
+        final ProcessRecord proc;
+        final ActivityRecord activity;
+        final boolean aboveSystem;
+
+        Data(ProcessRecord proc, ActivityRecord activity, boolean aboveSystem) {
+            this.proc = proc;
+            this.activity = activity;
+            this.aboveSystem = aboveSystem;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 05305f3..e361a70 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -86,8 +86,16 @@
      *         display, false otherwise
      */
     boolean isKeyguardShowing(int displayId) {
-        return mKeyguardShowing && !mKeyguardGoingAway &&
-                (displayId == DEFAULT_DISPLAY ? !mOccluded : displayId == mSecondaryDisplayShowing);
+        return isKeyguardActive(displayId) && !mKeyguardGoingAway;
+    }
+
+    /**
+     * @return true if Keyguard is showing and not occluded. We ignore whether it is going away or
+     *         not here.
+     */
+    boolean isKeyguardActive(int displayId) {
+        return mKeyguardShowing && (displayId == DEFAULT_DISPLAY ? !mOccluded
+                : displayId == mSecondaryDisplayShowing);
     }
 
     /**
@@ -114,6 +122,9 @@
                 mDismissalRequested = false;
             }
         }
+        if (!showing) {
+            mStackSupervisor.resumeFocusedStackTopActivityLocked();
+        }
         mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         updateKeyguardSleepToken();
     }
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index e5762d2..af99111 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -38,7 +38,6 @@
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE;
 import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_WHITELISTED;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
@@ -114,7 +113,7 @@
         STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_HOME,
                 new Pair<>(StatusBarManager.DISABLE_HOME, StatusBarManager.DISABLE2_NONE));
 
-        STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS,
+        STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW,
                 new Pair<>(StatusBarManager.DISABLE_RECENT, StatusBarManager.DISABLE2_NONE));
 
         STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS,
@@ -308,7 +307,7 @@
 
     private boolean isRecentsAllowed(int userId) {
         return (getLockTaskFeaturesForUser(userId)
-                & DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS) != 0;
+                & DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW) != 0;
     }
 
     private boolean isKeyguardAllowed(int userId) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 52f5917..f3f4dfd 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -43,8 +43,8 @@
 import android.content.IntentFilter;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ServiceInfo;
 import android.database.ContentObserver;
 import android.net.Uri;
@@ -65,6 +65,7 @@
 import android.provider.Settings;
 import android.text.format.DateUtils;
 import android.util.KeyValueListParser;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -79,9 +80,9 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
+import com.android.server.AppStateTracker;
 import com.android.server.DeviceIdleController;
 import com.android.server.FgThread;
-import com.android.server.AppStateTracker;
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
 import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
@@ -125,8 +126,8 @@
  */
 public final class JobSchedulerService extends com.android.server.SystemService
         implements StateChangedListener, JobCompletedListener {
-    static final String TAG = "JobSchedulerService";
-    public static final boolean DEBUG = false;
+    public static final String TAG = "JobScheduler";
+    public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     public static final boolean DEBUG_STANDBY = DEBUG || false;
 
     /** The maximum number of concurrent jobs we run at one time. */
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index 8d11d1e..021eccd 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -19,6 +19,7 @@
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.Context;
 import android.os.UserHandle;
+import android.util.Log;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
@@ -36,9 +37,9 @@
  * out of idle state, it will be allowed to run scheduled jobs.
  */
 public final class AppIdleController extends StateController {
-
-    private static final String LOG_TAG = "AppIdleController";
-    private static final boolean DEBUG = false;
+    private static final String TAG = "JobScheduler.AppIdle";
+    private static final boolean DEBUG = JobSchedulerService.DEBUG
+            || Log.isLoggable(TAG, Log.DEBUG);
 
     // Singleton factory
     private static Object sCreationLock = new Object();
@@ -56,7 +57,7 @@
             final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
                     jobStatus.getSourceUid(), jobStatus.getSourceUserId());
             if (DEBUG) {
-                Slog.d(LOG_TAG, "Setting idle state of " + packageName + " to " + appIdle);
+                Slog.d(TAG, "Setting idle state of " + packageName + " to " + appIdle);
             }
             if (jobStatus.setAppNotIdleConstraintSatisfied(!appIdle)) {
                 mChanged = true;
@@ -81,7 +82,7 @@
                     && jobStatus.getSourceUserId() == mUserId) {
                 if (jobStatus.setAppNotIdleConstraintSatisfied(!mIdle)) {
                     if (DEBUG) {
-                        Slog.d(LOG_TAG, "App Idle state changed, setting idle state of "
+                        Slog.d(TAG, "App Idle state changed, setting idle state of "
                                 + mPackage + " to " + mIdle);
                     }
                     mChanged = true;
@@ -118,7 +119,7 @@
         final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
                 jobStatus.getSourceUid(), jobStatus.getSourceUserId());
         if (DEBUG) {
-            Slog.d(LOG_TAG, "Start tracking, setting idle state of "
+            Slog.d(TAG, "Start tracking, setting idle state of "
                     + packageName + " to " + appIdle);
         }
         jobStatus.setAppNotIdleConstraintSatisfied(!appIdle);
@@ -229,7 +230,7 @@
         @Override
         public void onParoleStateChanged(boolean isParoleOn) {
             if (DEBUG) {
-                Slog.d(LOG_TAG, "Parole on: " + isParoleOn);
+                Slog.d(TAG, "Parole on: " + isParoleOn);
             }
             setAppIdleParoleOn(isParoleOn);
         }
diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
index e8057fb..46ec5e5 100644
--- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.util.Log;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
@@ -34,9 +35,9 @@
 import java.io.PrintWriter;
 
 public final class BackgroundJobsController extends StateController {
-
-    private static final String LOG_TAG = "BackgroundJobsController";
-    private static final boolean DEBUG = JobSchedulerService.DEBUG;
+    private static final String TAG = "JobScheduler.Background";
+    private static final boolean DEBUG = JobSchedulerService.DEBUG
+            || Log.isLoggable(TAG, Log.DEBUG);
 
     // Singleton factory
     private static final Object sCreationLock = new Object();
@@ -179,7 +180,7 @@
 
         final long time = DEBUG ? (SystemClock.elapsedRealtimeNanos() - start) : 0;
         if (DEBUG) {
-            Slog.d(LOG_TAG, String.format(
+            Slog.d(TAG, String.format(
                     "Job status updated: %d/%d checked/total jobs, %d us",
                     updateTrackedJobs.mCheckedCount,
                     updateTrackedJobs.mTotalCount,
diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java
index 8d3d116..263d99b 100644
--- a/services/core/java/com/android/server/job/controllers/BatteryController.java
+++ b/services/core/java/com/android/server/job/controllers/BatteryController.java
@@ -26,6 +26,7 @@
 import android.os.BatteryManagerInternal;
 import android.os.UserHandle;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
@@ -43,7 +44,9 @@
  * ACTION_BATTERY_OK.
  */
 public final class BatteryController extends StateController {
-    private static final String TAG = "JobScheduler.Batt";
+    private static final String TAG = "JobScheduler.Battery";
+    private static final boolean DEBUG = JobSchedulerService.DEBUG
+            || Log.isLoggable(TAG, Log.DEBUG);
 
     private static final Object sCreationLock = new Object();
     private static volatile BatteryController sController;
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 77e813e..d7ef124 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -35,6 +35,7 @@
 import android.os.UserHandle;
 import android.text.format.DateUtils;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
@@ -56,8 +57,9 @@
  */
 public final class ConnectivityController extends StateController implements
         ConnectivityManager.OnNetworkActiveListener {
-    private static final String TAG = "JobScheduler.Conn";
-    private static final boolean DEBUG = false;
+    private static final String TAG = "JobScheduler.Connectivity";
+    private static final boolean DEBUG = JobSchedulerService.DEBUG
+            || Log.isLoggable(TAG, Log.DEBUG);
 
     private final ConnectivityManager mConnManager;
     private final NetworkPolicyManager mNetPolicyManager;
diff --git a/services/core/java/com/android/server/job/controllers/ContentObserverController.java b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
index 7394e23f..90edde9 100644
--- a/services/core/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
@@ -23,11 +23,12 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -43,8 +44,9 @@
  * Controller for monitoring changes to content URIs through a ContentObserver.
  */
 public final class ContentObserverController extends StateController {
-    private static final String TAG = "JobScheduler.Content";
-    private static final boolean DEBUG = false;
+    private static final String TAG = "JobScheduler.ContentObserver";
+    private static final boolean DEBUG = JobSchedulerService.DEBUG
+            || Log.isLoggable(TAG, Log.DEBUG);
 
     /**
      * Maximum number of changing URIs we will batch together to report.
diff --git a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index 0dbcbee..323a126 100644
--- a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -27,6 +27,7 @@
 import android.os.PowerManager;
 import android.os.UserHandle;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.proto.ProtoOutputStream;
@@ -47,9 +48,10 @@
  * When device is not dozing, set constraint for all jobs as satisfied.
  */
 public final class DeviceIdleJobsController extends StateController {
+    private static final String TAG = "JobScheduler.DeviceIdle";
+    private static final boolean DEBUG = JobSchedulerService.DEBUG
+            || Log.isLoggable(TAG, Log.DEBUG);
 
-    private static final String LOG_TAG = "DeviceIdleJobsController";
-    private static final boolean LOG_DEBUG = false;
     private static final long BACKGROUND_JOBS_DELAY = 3000;
 
     static final int PROCESS_BACKGROUND_JOBS = 1;
@@ -104,8 +106,8 @@
                     synchronized (mLock) {
                         mDeviceIdleWhitelistAppIds =
                                 mLocalDeviceIdleController.getPowerSaveWhitelistUserAppIds();
-                        if (LOG_DEBUG) {
-                            Slog.d(LOG_TAG, "Got whitelist "
+                        if (DEBUG) {
+                            Slog.d(TAG, "Got whitelist "
                                     + Arrays.toString(mDeviceIdleWhitelistAppIds));
                         }
                     }
@@ -114,8 +116,8 @@
                     synchronized (mLock) {
                         mPowerSaveTempWhitelistAppIds =
                                 mLocalDeviceIdleController.getPowerSaveTempWhitelistAppIds();
-                        if (LOG_DEBUG) {
-                            Slog.d(LOG_TAG, "Got temp whitelist "
+                        if (DEBUG) {
+                            Slog.d(TAG, "Got temp whitelist "
                                     + Arrays.toString(mPowerSaveTempWhitelistAppIds));
                         }
                         boolean changed = false;
@@ -163,7 +165,7 @@
                 changed = true;
             }
             mDeviceIdleMode = enabled;
-            if (LOG_DEBUG) Slog.d(LOG_TAG, "mDeviceIdleMode=" + mDeviceIdleMode);
+            if (DEBUG) Slog.d(TAG, "mDeviceIdleMode=" + mDeviceIdleMode);
             if (enabled) {
                 mHandler.removeMessages(PROCESS_BACKGROUND_JOBS);
                 mJobSchedulerService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor);
@@ -193,8 +195,8 @@
         if (!changed) {
             return;
         }
-        if (LOG_DEBUG) {
-            Slog.d(LOG_TAG, "uid " + uid + " going " + (active ? "active" : "inactive"));
+        if (DEBUG) {
+            Slog.d(TAG, "uid " + uid + " going " + (active ? "active" : "inactive"));
         }
         mForegroundUids.put(uid, active);
         mDeviceIdleUpdateFunctor.mChanged = false;
diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java
index a9bc7e0..78284e5 100644
--- a/services/core/java/com/android/server/job/controllers/IdleController.java
+++ b/services/core/java/com/android/server/job/controllers/IdleController.java
@@ -26,6 +26,7 @@
 import android.content.IntentFilter;
 import android.os.UserHandle;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
@@ -37,7 +38,9 @@
 import java.io.PrintWriter;
 
 public final class IdleController extends StateController {
-    private static final String TAG = "IdleController";
+    private static final String TAG = "JobScheduler.Idle";
+    private static final boolean DEBUG = JobSchedulerService.DEBUG
+            || Log.isLoggable(TAG, Log.DEBUG);
 
     // Policy: we decide that we're "idle" if the device has been unused /
     // screen off or dreaming for at least this long
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index d3055e6..88d6bea 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -30,7 +30,6 @@
  * are ready to run, or whether they must be stopped.
  */
 public abstract class StateController {
-    protected static final boolean DEBUG = JobSchedulerService.DEBUG;
     protected final Context mContext;
     protected final Object mLock;
     protected final StateChangedListener mStateChangedListener;
diff --git a/services/core/java/com/android/server/job/controllers/StorageController.java b/services/core/java/com/android/server/job/controllers/StorageController.java
index 0519b63..5b79f39 100644
--- a/services/core/java/com/android/server/job/controllers/StorageController.java
+++ b/services/core/java/com/android/server/job/controllers/StorageController.java
@@ -24,6 +24,7 @@
 import android.content.IntentFilter;
 import android.os.UserHandle;
 import android.util.ArraySet;
+import android.util.Log;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
@@ -39,7 +40,9 @@
  * Simple controller that tracks the status of the device's storage.
  */
 public final class StorageController extends StateController {
-    private static final String TAG = "JobScheduler.Stor";
+    private static final String TAG = "JobScheduler.Storage";
+    private static final boolean DEBUG = JobSchedulerService.DEBUG
+            || Log.isLoggable(TAG, Log.DEBUG);
 
     private static final Object sCreationLock = new Object();
     private static volatile StorageController sController;
diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java
index a91f5a4..cdafc3b 100644
--- a/services/core/java/com/android/server/job/controllers/TimeController.java
+++ b/services/core/java/com/android/server/job/controllers/TimeController.java
@@ -25,6 +25,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.util.Log;
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
@@ -45,6 +46,8 @@
  */
 public final class TimeController extends StateController {
     private static final String TAG = "JobScheduler.Time";
+    private static final boolean DEBUG = JobSchedulerService.DEBUG
+            || Log.isLoggable(TAG, Log.DEBUG);
 
     /** Deadline alarm tag for logging purposes */
     private final String DEADLINE_TAG = "*job.deadline*";
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 9d2a8e2..0dab528 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -215,6 +215,8 @@
     private static final int REQUEST_SUPL_CONNECTION = 14;
     private static final int RELEASE_SUPL_CONNECTION = 15;
     private static final int REQUEST_LOCATION = 16;
+    private static final int REPORT_LOCATION = 17; // HAL reports location
+    private static final int REPORT_SV_STATUS = 18; // HAL reports SV status
 
     // Request setid
     private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
@@ -266,7 +268,7 @@
         }
     }
 
-    // Simple class to hold stats reported in the Extras Bundle
+    // Threadsafe class to hold stats reported in the Extras Bundle
     private static class LocationExtras {
         private int mSvCount;
         private int mMeanCn0;
@@ -278,9 +280,11 @@
         }
 
         public void set(int svCount, int meanCn0, int maxCn0) {
-            mSvCount = svCount;
-            mMeanCn0 = meanCn0;
-            mMaxCn0 = maxCn0;
+            synchronized(this) {
+                mSvCount = svCount;
+                mMeanCn0 = meanCn0;
+                mMaxCn0 = maxCn0;
+            }
             setBundle(mBundle);
         }
 
@@ -291,14 +295,18 @@
         // Also used by outside methods to add to other bundles
         public void setBundle(Bundle extras) {
             if (extras != null) {
-                extras.putInt("satellites", mSvCount);
-                extras.putInt("meanCn0", mMeanCn0);
-                extras.putInt("maxCn0", mMaxCn0);
+                synchronized (this) {
+                    extras.putInt("satellites", mSvCount);
+                    extras.putInt("meanCn0", mMeanCn0);
+                    extras.putInt("maxCn0", mMaxCn0);
+                }
             }
         }
 
         public Bundle getBundle() {
-            return mBundle;
+            synchronized (this) {
+                return new Bundle(mBundle);
+            }
         }
     }
 
@@ -411,7 +419,6 @@
     private final Context mContext;
     private final NtpTrustedTime mNtpTime;
     private final ILocationManager mILocationManager;
-    private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
     private final LocationExtras mLocationExtras = new LocationExtras();
     private final GnssStatusListenerHelper mListenerHelper;
     private final GnssMeasurementsProvider mGnssMeasurementsProvider;
@@ -758,8 +765,6 @@
         mNtpTime = NtpTrustedTime.getInstance(context);
         mILocationManager = ilocationManager;
 
-        mLocation.setExtras(mLocationExtras.getBundle());
-
         // Create a wake lock
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
@@ -1726,6 +1731,10 @@
      * called from native code to update our position.
      */
     private void reportLocation(boolean hasLatLong, Location location) {
+        sendMessage(REPORT_LOCATION, hasLatLong ? 1 : 0, location);
+    }
+
+    private void handleReportLocation(boolean hasLatLong, Location location) {
         if (location.hasSpeed()) {
             mItarSpeedLimitExceeded = location.getSpeed() > ITAR_SPEED_LIMIT_METERS_PER_SECOND;
         }
@@ -1739,18 +1748,15 @@
 
         if (VERBOSE) Log.v(TAG, "reportLocation " + location.toString());
 
-        synchronized (mLocation) {
-            mLocation = location;
-            // It would be nice to push the elapsed real-time timestamp
-            // further down the stack, but this is still useful
-            mLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
-            mLocation.setExtras(mLocationExtras.getBundle());
+        // It would be nice to push the elapsed real-time timestamp
+        // further down the stack, but this is still useful
+        location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+        location.setExtras(mLocationExtras.getBundle());
 
-            try {
-                mILocationManager.reportLocation(mLocation, false);
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException calling reportLocation");
-            }
+        try {
+            mILocationManager.reportLocation(location, false);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException calling reportLocation");
         }
 
         mGnssMetrics.logReceivedLocationStatus(hasLatLong);
@@ -1835,54 +1841,73 @@
         }
     }
 
+    // Helper class to carry data to handler for reportSvStatus
+    private static class SvStatusInfo {
+        public int mSvCount;
+        public int[] mSvidWithFlags;
+        public float[] mCn0s;
+        public float[] mSvElevations;
+        public float[] mSvAzimuths;
+        public float[] mSvCarrierFreqs;
+    }
+
     /**
      * called from native code to update SV info
      */
-    private void reportSvStatus() {
-        int svCount = native_read_sv_status(mSvidWithFlags,
-                mCn0s,
-                mSvElevations,
-                mSvAzimuths,
-                mSvCarrierFreqs);
+    private void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0s,
+            float[] svElevations, float[] svAzimuths, float[] svCarrierFreqs) {
+        SvStatusInfo svStatusInfo = new SvStatusInfo();
+        svStatusInfo.mSvCount = svCount;
+        svStatusInfo.mSvidWithFlags = svidWithFlags;
+        svStatusInfo.mCn0s = cn0s;
+        svStatusInfo.mSvElevations = svElevations;
+        svStatusInfo.mSvAzimuths = svAzimuths;
+        svStatusInfo.mSvCarrierFreqs = svCarrierFreqs;
+
+        sendMessage(REPORT_SV_STATUS, 0, svStatusInfo);
+    }
+
+    private void handleReportSvStatus(SvStatusInfo info) {
         mListenerHelper.onSvStatusChanged(
-                svCount,
-                mSvidWithFlags,
-                mCn0s,
-                mSvElevations,
-                mSvAzimuths,
-                mSvCarrierFreqs);
+                info.mSvCount,
+                info.mSvidWithFlags,
+                info.mCn0s,
+                info.mSvElevations,
+                info.mSvAzimuths,
+                info.mSvCarrierFreqs);
 
         // Log CN0 as part of GNSS metrics
-        mGnssMetrics.logCn0(mCn0s, svCount);
+        mGnssMetrics.logCn0(info.mCn0s, info.mSvCount);
 
         if (VERBOSE) {
-            Log.v(TAG, "SV count: " + svCount);
+            Log.v(TAG, "SV count: " + info.mSvCount);
         }
         // Calculate number of satellites used in fix.
         int usedInFixCount = 0;
         int maxCn0 = 0;
         int meanCn0 = 0;
-        for (int i = 0; i < svCount; i++) {
-            if ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
+        for (int i = 0; i < info.mSvCount; i++) {
+            if ((info.mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) {
                 ++usedInFixCount;
-                if (mCn0s[i] > maxCn0) {
-                    maxCn0 = (int) mCn0s[i];
+                if (info.mCn0s[i] > maxCn0) {
+                    maxCn0 = (int) info.mCn0s[i];
                 }
-                meanCn0 += mCn0s[i];
+                meanCn0 += info.mCn0s[i];
             }
             if (VERBOSE) {
-                Log.v(TAG, "svid: " + (mSvidWithFlags[i] >> GnssStatus.SVID_SHIFT_WIDTH) +
-                        " cn0: " + mCn0s[i] +
-                        " elev: " + mSvElevations[i] +
-                        " azimuth: " + mSvAzimuths[i] +
-                        " carrier frequency: " + mSvCarrierFreqs[i] +
-                        ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) == 0
+                Log.v(TAG, "svid: " + (info.mSvidWithFlags[i] >> GnssStatus.SVID_SHIFT_WIDTH) +
+                        " cn0: " + info.mCn0s[i] +
+                        " elev: " + info.mSvElevations[i] +
+                        " azimuth: " + info.mSvAzimuths[i] +
+                        " carrier frequency: " + info.mSvCarrierFreqs[i] +
+                        ((info.mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) == 0
                                 ? "  " : " E") +
-                        ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) == 0
+                        ((info.mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) == 0
                                 ? "  " : " A") +
-                        ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) == 0
+                        ((info.mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) == 0
                                 ? "" : "U") +
-                        ((mSvidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY) == 0
+                        ((info.mSvidWithFlags[i] &
+                                GnssStatus.GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY) == 0
                                 ? "" : "F"));
             }
         }
@@ -2479,6 +2504,12 @@
                 case INITIALIZE_HANDLER:
                     handleInitialize();
                     break;
+                case REPORT_LOCATION:
+                    handleReportLocation(msg.arg1 == 1, (Location) msg.obj);
+                    break;
+                case REPORT_SV_STATUS:
+                    handleReportSvStatus((SvStatusInfo) msg.obj);
+                    break;
             }
             if (msg.arg2 == 1) {
                 // wakelock was taken for this message, release it
@@ -2798,6 +2829,10 @@
                 return "SUBSCRIPTION_OR_SIM_CHANGED";
             case INITIALIZE_HANDLER:
                 return "INITIALIZE_HANDLER";
+            case REPORT_LOCATION:
+                return "REPORT_LOCATION";
+            case REPORT_SV_STATUS:
+                return "REPORT_SV_STATUS";
             default:
                 return "<Unknown>";
         }
@@ -2862,15 +2897,6 @@
         }
     }
 
-    // for GPS SV statistics
-    private static final int MAX_SVS = 64;
-
-    // preallocated arrays, to avoid memory allocation in reportStatus()
-    private int mSvidWithFlags[] = new int[MAX_SVS];
-    private float mCn0s[] = new float[MAX_SVS];
-    private float mSvElevations[] = new float[MAX_SVS];
-    private float mSvAzimuths[] = new float[MAX_SVS];
-    private float mSvCarrierFreqs[] = new float[MAX_SVS];
     // preallocated to avoid memory allocation in reportNmea()
     private byte[] mNmeaBuffer = new byte[120];
 
@@ -2899,11 +2925,6 @@
 
     private native void native_delete_aiding_data(int flags);
 
-    // returns number of SVs
-    // mask[0] is ephemeris mask and mask[1] is almanac mask
-    private native int native_read_sv_status(int[] prnWithFlags, float[] cn0s, float[] elevations,
-            float[] azimuths, float[] carrierFrequencies);
-
     private native int native_read_nmea(byte[] buffer, int bufferSize);
 
     private native void native_inject_best_location(
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 6deff36..838aa70 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2001,9 +2001,8 @@
     }
 
     @Override
-    public void setRecoveryStatus(@NonNull String packageName, @Nullable String[] aliases,
-            int status) throws RemoteException {
-        mRecoverableKeyStoreManager.setRecoveryStatus(packageName, aliases, status);
+    public void setRecoveryStatus(String alias, int status) throws RemoteException {
+        mRecoverableKeyStoreManager.setRecoveryStatus(alias, status);
     }
 
     public Map getRecoveryStatus(@Nullable String packageName) throws RemoteException {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 5460756..35954a5 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -277,29 +277,11 @@
     }
 
     /**
-     * Updates recovery status for the application given its {@code packageName}.
-     *
-     * @param packageName which recoverable key statuses will be returned
-     * @param aliases - KeyStore aliases or {@code null} for all aliases of the app
-     * @param status - new status
+     * Sets the recovery status of key with {@code alias} to {@code status}.
      */
-    public void setRecoveryStatus(
-            @NonNull String packageName, @Nullable String[] aliases, int status)
-            throws RemoteException {
+    public void setRecoveryStatus(String alias, int status) throws RemoteException {
         checkRecoverKeyStorePermission();
-        int uid = Binder.getCallingUid();
-        if (packageName != null) {
-            // TODO: get uid for package name, when many apps are supported.
-        }
-        if (aliases == null) {
-            // Get all keys for the app.
-            Map<String, Integer> allKeys = mDatabase.getStatusForAllKeys(uid);
-            aliases = new String[allKeys.size()];
-            allKeys.keySet().toArray(aliases);
-        }
-        for (String alias: aliases) {
-            mDatabase.setRecoveryStatus(uid, alias, status);
-        }
+        mDatabase.setRecoveryStatus(Binder.getCallingUid(), alias, status);
     }
 
     /**
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 21d86c4..ecff334 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -1512,6 +1512,17 @@
             }
         }
 
+        /**
+         * Called when a {@link android.media.MediaSession2} instance is created.
+         * <p>
+         * This does two things.
+         *   1. Keep the newly created session in the service
+         *   2. Do sanity check to ensure unique id per package, and return result
+         *
+         * @param sessionToken SessionToken2 object in bundled form
+         * @return {@code true} if the session's id isn't used by the package now. {@code false}
+         *     otherwise.
+         */
         @Override
         public boolean onSessionCreated(Bundle sessionToken) {
             final int uid = Binder.getCallingUid();
@@ -1538,6 +1549,19 @@
             }
         }
 
+        /**
+         * Called when a {@link android.media.MediaSession2} instance is closed. (i.e. destroyed)
+         * <p>
+         * Ideally service should know that a session is destroyed through the
+         * {@link android.media.MediaController2.ControllerCallback#onDisconnected()}, which is
+         * asynchronous call. However, we also need synchronous way together to address timing
+         * issue. If the package recreates the session almost immediately, which happens commonly
+         * for tests, service will reject the creation through {@link #onSessionCreated(Bundle)}
+         * if the service hasn't notified previous destroy yet. This synchronous API will address
+         * the issue.
+         *
+         * @param sessionToken SessionToken2 object in bundled form
+         */
         @Override
         public void onSessionDestroyed(Bundle sessionToken) {
             final int uid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/notification/BadgeExtractor.java b/services/core/java/com/android/server/notification/BadgeExtractor.java
index 184f8b2..d8a30ae 100644
--- a/services/core/java/com/android/server/notification/BadgeExtractor.java
+++ b/services/core/java/com/android/server/notification/BadgeExtractor.java
@@ -61,4 +61,9 @@
     public void setConfig(RankingConfig config) {
         mConfig = config;
     }
+
+    @Override
+    public void setZenHelper(ZenModeHelper helper) {
+
+    }
 }
diff --git a/services/core/java/com/android/server/notification/ImportanceExtractor.java b/services/core/java/com/android/server/notification/ImportanceExtractor.java
index 452121c..dfdd55b 100644
--- a/services/core/java/com/android/server/notification/ImportanceExtractor.java
+++ b/services/core/java/com/android/server/notification/ImportanceExtractor.java
@@ -50,4 +50,9 @@
     public void setConfig(RankingConfig config) {
         mConfig = config;
     }
+
+    @Override
+    public void setZenHelper(ZenModeHelper helper) {
+
+    }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java b/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
index 3bfd93f..97bbc23 100644
--- a/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
@@ -44,4 +44,9 @@
     public void setConfig(RankingConfig config) {
         // config is not used
     }
+
+    @Override
+    public void setZenHelper(ZenModeHelper helper) {
+
+    }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
index 11c7ab7..d66fd57 100644
--- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -52,4 +52,9 @@
     public void setConfig(RankingConfig config) {
         mConfig = config;
     }
+
+    @Override
+    public void setZenHelper(ZenModeHelper helper) {
+
+    }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index 91fee46..bf777e0 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -85,4 +85,9 @@
     public void setConfig(RankingConfig config) {
         // ignore: config has no relevant information yet.
     }
+
+    @Override
+    public void setZenHelper(ZenModeHelper helper) {
+
+    }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c4953b5..ef354cd 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1312,11 +1312,6 @@
         }
         mUsageStats = usageStats;
         mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
-        mRankingHelper = new RankingHelper(getContext(),
-                mPackageManagerClient,
-                mRankingHandler,
-                mUsageStats,
-                extractorNames);
         mConditionProviders = conditionProviders;
         mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
         mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
@@ -1335,6 +1330,7 @@
                 synchronized (mNotificationLock) {
                     updateInterruptionFilterLocked();
                 }
+                mRankingHandler.requestSort();
             }
 
             @Override
@@ -1342,6 +1338,12 @@
                 sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED);
             }
         });
+        mRankingHelper = new RankingHelper(getContext(),
+                mPackageManagerClient,
+                mRankingHandler,
+                mZenModeHelper,
+                mUsageStats,
+                extractorNames);
         mSnoozeHelper = snoozeHelper;
         mGroupHelper = groupHelper;
 
@@ -4734,6 +4736,7 @@
             ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
             ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
             ArrayList<Integer> userSentimentBefore = new ArrayList<>(N);
+            ArrayList<Integer> suppressVisuallyBefore = new ArrayList<>(N);
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
                 orderBefore.add(r.getKey());
@@ -4744,6 +4747,7 @@
                 overridePeopleBefore.add(r.getPeopleOverride());
                 snoozeCriteriaBefore.add(r.getSnoozeCriteria());
                 userSentimentBefore.add(r.getUserSentiment());
+                suppressVisuallyBefore.add(r.getSuppressedVisualEffects());
                 mRankingHelper.extractSignals(r);
             }
             mRankingHelper.sort(mNotificationList);
@@ -4756,7 +4760,9 @@
                         || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
                         || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
                         || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())
-                        || !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment())) {
+                        || !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment())
+                        || !Objects.equals(suppressVisuallyBefore.get(i),
+                        r.getSuppressedVisualEffects())) {
                     mHandler.scheduleSendRankingUpdate();
                     return;
                 }
diff --git a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
index 31f8b27..24c1d59 100644
--- a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
@@ -22,6 +22,8 @@
  * Extracts signals that will be useful to the {@link NotificationComparator} and caches them
  *  on the {@link NotificationRecord} object. These annotations will
  *  not be passed on to {@link android.service.notification.NotificationListenerService}s.
+ *
+ *  If you add a new Extractor be sure to add it to R.array.config_notificationSignalExtractors.
  */
 public interface NotificationSignalExtractor {
 
@@ -44,4 +46,10 @@
      * @param config information about which signals are important.
      */
     void setConfig(RankingConfig config);
+
+    /**
+     * @param helper Helper to determine what components of notifications should be blocked due to
+     *               DND.
+     */
+    void setZenHelper(ZenModeHelper helper);
 }
diff --git a/services/core/java/com/android/server/notification/PriorityExtractor.java b/services/core/java/com/android/server/notification/PriorityExtractor.java
index 7a287db..612c624 100644
--- a/services/core/java/com/android/server/notification/PriorityExtractor.java
+++ b/services/core/java/com/android/server/notification/PriorityExtractor.java
@@ -53,4 +53,9 @@
     public void setConfig(RankingConfig config) {
         mConfig = config;
     }
+
+    @Override
+    public void setZenHelper(ZenModeHelper helper) {
+
+    }
 }
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index dc936d2..b280bde 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -102,7 +102,7 @@
     private SparseBooleanArray mBadgingEnabled;
 
     public RankingHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
-            NotificationUsageStats usageStats, String[] extractorNames) {
+            ZenModeHelper zenHelper, NotificationUsageStats usageStats, String[] extractorNames) {
         mContext = context;
         mRankingHandler = rankingHandler;
         mPm = pm;
@@ -120,6 +120,7 @@
                         (NotificationSignalExtractor) extractorClass.newInstance();
                 extractor.initialize(mContext, usageStats);
                 extractor.setConfig(this);
+                extractor.setZenHelper(zenHelper);
                 mSignalExtractors[i] = extractor;
             } catch (ClassNotFoundException e) {
                 Slog.w(TAG, "Couldn't find extractor " + extractorNames[i] + ".", e);
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index fd9ffb2..896480f 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -144,6 +144,11 @@
         // ignore: config has no relevant information yet.
     }
 
+    @Override
+    public void setZenHelper(ZenModeHelper helper) {
+
+    }
+
     /**
      * @param extras extras of the notification with EXTRA_PEOPLE populated
      * @param timeoutMs timeout in milliseconds to wait for contacts response
@@ -444,7 +449,8 @@
         private float mContactAffinity = NONE;
         private NotificationRecord mRecord;
 
-        private PeopleRankingReconsideration(Context context, String key, LinkedList<String> pendingLookups) {
+        private PeopleRankingReconsideration(Context context, String key,
+                LinkedList<String> pendingLookups) {
             super(key);
             mContext = context;
             mPendingLookups = pendingLookups;
@@ -478,7 +484,9 @@
                         final String cacheKey = getCacheKey(mContext.getUserId(), handle);
                         mPeopleCache.put(cacheKey, lookupResult);
                     }
-                    if (DEBUG) Slog.d(TAG, "lookup contactAffinity is " + lookupResult.getAffinity());
+                    if (DEBUG) {
+                        Slog.d(TAG, "lookup contactAffinity is " + lookupResult.getAffinity());
+                    }
                     mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
                 } else {
                     if (DEBUG) Slog.d(TAG, "lookupResult is null");
diff --git a/services/core/java/com/android/server/notification/VisibilityExtractor.java b/services/core/java/com/android/server/notification/VisibilityExtractor.java
index 37dbe3a..db54846 100644
--- a/services/core/java/com/android/server/notification/VisibilityExtractor.java
+++ b/services/core/java/com/android/server/notification/VisibilityExtractor.java
@@ -52,4 +52,9 @@
     public void setConfig(RankingConfig config) {
         mConfig = config;
     }
+
+    @Override
+    public void setZenHelper(ZenModeHelper helper) {
+
+    }
 }
diff --git a/services/core/java/com/android/server/notification/ZenModeExtractor.java b/services/core/java/com/android/server/notification/ZenModeExtractor.java
new file mode 100644
index 0000000..74f9806
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ZenModeExtractor.java
@@ -0,0 +1,73 @@
+/*
+* 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.notification;
+
+import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
+import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
+
+import android.content.Context;
+import android.util.Log;
+import android.util.Slog;
+
+/**
+ * This {@link ZenModeExtractor} updates intercepted and visual interruption states.
+ */
+public class ZenModeExtractor implements NotificationSignalExtractor {
+    private static final String TAG = "ZenModeExtractor";
+    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private ZenModeHelper mZenModeHelper;
+
+    public void initialize(Context ctx, NotificationUsageStats usageStats) {
+        if (DBG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
+    }
+
+    public RankingReconsideration process(NotificationRecord record) {
+        if (record == null || record.getNotification() == null) {
+            if (DBG) Slog.d(TAG, "skipping empty notification");
+            return null;
+        }
+
+        if (mZenModeHelper == null) {
+            if (DBG) Slog.d(TAG, "skipping - no zen info available");
+            return null;
+        }
+
+        record.setIntercepted(mZenModeHelper.shouldIntercept(record));
+        if (record.isIntercepted()) {
+            int suppressed = (mZenModeHelper.shouldSuppressWhenScreenOff()
+                    ? SUPPRESSED_EFFECT_SCREEN_OFF : 0)
+                    | (mZenModeHelper.shouldSuppressWhenScreenOn()
+                    ? SUPPRESSED_EFFECT_SCREEN_ON : 0);
+            record.setSuppressedVisualEffects(suppressed);
+        } else {
+            record.setSuppressedVisualEffects(0);
+        }
+
+        return null;
+    }
+
+    @Override
+    public void setConfig(RankingConfig config) {
+        // ignore: config has no relevant information yet.
+    }
+
+    @Override
+    public void setZenHelper(ZenModeHelper helper) {
+        mZenModeHelper = helper;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4a2f577..256fb42 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -237,6 +237,7 @@
 import android.security.KeyStore;
 import android.security.SystemKeyStore;
 import android.service.pm.PackageServiceDumpProto;
+import android.service.textclassifier.TextClassifierService;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.TextUtils;
@@ -1392,6 +1393,7 @@
     final @NonNull String mRequiredUninstallerPackage;
     final @Nullable String mSetupWizardPackage;
     final @Nullable String mStorageManagerPackage;
+    final @Nullable String mSystemTextClassifierPackage;
     final @NonNull String mServicesSystemSharedLibraryPackageName;
     final @NonNull String mSharedSystemSharedLibraryPackageName;
 
@@ -2952,6 +2954,9 @@
                     filter.setPriority(0);
                 }
             }
+
+            mSystemTextClassifierPackage = getSystemTextClassifierPackageName();
+
             mDeferProtectedFilters = false;
             mProtectedFilters.clear();
 
@@ -20251,6 +20256,11 @@
     }
 
     @Override
+    public String getSystemTextClassifierPackageName() {
+        return mContext.getString(R.string.config_defaultTextClassifierPackage);
+    }
+
+    @Override
     public void setApplicationEnabledSetting(String appPackageName,
             int newState, int flags, int userId, String callingPackage) {
         if (!sUserManager.exists(userId)) return;
@@ -23385,6 +23395,8 @@
                     return "android";
                 case PackageManagerInternal.PACKAGE_VERIFIER:
                     return mRequiredVerifierPackage;
+                case PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER:
+                    return mSystemTextClassifierPackage;
             }
             return null;
         }
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index e4c74ed..c11c099 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -490,7 +490,7 @@
      * <p>This takes care of the resetting the counter for foreground apps as well as after
      * locale changes.
      */
-    public int getApiCallCount() {
+    public int getApiCallCount(boolean unlimited) {
         final ShortcutService s = mShortcutUser.mService;
 
         // Reset the counter if:
@@ -498,8 +498,9 @@
         // - the package is *not* in foreground now, but was in foreground at some point
         // since the previous time it had been.
         if (s.isUidForegroundLocked(mPackageUid)
-                || mLastKnownForegroundElapsedTime
-                    < s.getUidLastForegroundElapsedTimeLocked(mPackageUid)) {
+                || (mLastKnownForegroundElapsedTime
+                    < s.getUidLastForegroundElapsedTimeLocked(mPackageUid))
+                || unlimited) {
             mLastKnownForegroundElapsedTime = s.injectElapsedRealtime();
             resetRateLimiting();
         }
@@ -538,10 +539,10 @@
      * <p>This takes care of the resetting the counter for foreground apps as well as after
      * locale changes, which is done internally by {@link #getApiCallCount}.
      */
-    public boolean tryApiCall() {
+    public boolean tryApiCall(boolean unlimited) {
         final ShortcutService s = mShortcutUser.mService;
 
-        if (getApiCallCount() >= s.mMaxUpdatesPerInterval) {
+        if (getApiCallCount(unlimited) >= s.mMaxUpdatesPerInterval) {
             return false;
         }
         mApiCallCount++;
@@ -1248,7 +1249,7 @@
         pw.print(prefix);
         pw.print("  ");
         pw.print("Calls: ");
-        pw.print(getApiCallCount());
+        pw.print(getApiCallCount(/*unlimited=*/ false));
         pw.println();
 
         // getApiCallCount() may have updated mLastKnownForegroundElapsedTime.
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 034fd23..076f81f 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.pm;
 
+import android.Manifest.permission;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1726,6 +1727,9 @@
         final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
         final int size = newShortcuts.size();
 
+        final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
+                injectBinderCallingPid(), injectBinderCallingUid());
+
         synchronized (mLock) {
             throwIfUserLockedL(userId);
 
@@ -1738,7 +1742,7 @@
             ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_SET);
 
             // Throttling.
-            if (!ps.tryApiCall()) {
+            if (!ps.tryApiCall(unlimited)) {
                 return false;
             }
 
@@ -1777,6 +1781,9 @@
         final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
         final int size = newShortcuts.size();
 
+        final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
+                injectBinderCallingPid(), injectBinderCallingUid());
+
         synchronized (mLock) {
             throwIfUserLockedL(userId);
 
@@ -1790,7 +1797,7 @@
             ps.enforceShortcutCountsBeforeOperation(newShortcuts, OPERATION_UPDATE);
 
             // Throttling.
-            if (!ps.tryApiCall()) {
+            if (!ps.tryApiCall(unlimited)) {
                 return false;
             }
 
@@ -1859,6 +1866,9 @@
         final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
         final int size = newShortcuts.size();
 
+        final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
+                injectBinderCallingPid(), injectBinderCallingUid());
+
         synchronized (mLock) {
             throwIfUserLockedL(userId);
 
@@ -1875,7 +1885,7 @@
             assignImplicitRanks(newShortcuts);
 
             // Throttling.
-            if (!ps.tryApiCall()) {
+            if (!ps.tryApiCall(unlimited)) {
                 return false;
             }
             for (int i = 0; i < size; i++) {
@@ -2144,11 +2154,14 @@
     public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
         verifyCaller(packageName, userId);
 
+        final boolean unlimited = injectHasUnlimitedShortcutsApiCallsPermission(
+                injectBinderCallingPid(), injectBinderCallingUid());
+
         synchronized (mLock) {
             throwIfUserLockedL(userId);
 
             final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
-            return mMaxUpdatesPerInterval - ps.getApiCallCount();
+            return mMaxUpdatesPerInterval - ps.getApiCallCount(unlimited);
         }
     }
 
@@ -2298,6 +2311,15 @@
                 callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
     }
 
+    /**
+     * Returns true if the caller has the "UNLIMITED_SHORTCUTS_API_CALLS" permission.
+     */
+    @VisibleForTesting
+    boolean injectHasUnlimitedShortcutsApiCallsPermission(int callingPid, int callingUid) {
+        return mContext.checkPermission(permission.UNLIMITED_SHORTCUTS_API_CALLS,
+                callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
+    }
+
     // This method is extracted so we can directly call this method from unit tests,
     // even when hasShortcutPermission() is overridden.
     @VisibleForTesting
@@ -4197,6 +4219,11 @@
         return getCallingUid();
     }
 
+    @VisibleForTesting
+    int injectBinderCallingPid() {
+        return getCallingPid();
+    }
+
     private int getCallingUserId() {
         return UserHandle.getUserId(injectBinderCallingUid());
     }
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 75a6106..bcf4b07 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -228,6 +228,10 @@
     public boolean isVendorPrivileged() {
         return (protectionLevel & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0;
     }
+    public boolean isSystemTextClassifier() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER)
+                != 0;
+    }
 
     public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
         if (!origPackageName.equals(sourcePackageName)) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index afaafbc..afa9dd0 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1148,6 +1148,13 @@
                 // this app is a setup wizard, then it gets the permission.
                 allowed = true;
             }
+            if (!allowed && bp.isSystemTextClassifier()
+                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                            PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER,
+                            UserHandle.USER_SYSTEM))) {
+                // Special permissions for the system default text classifier.
+                allowed = true;
+            }
         }
         return allowed;
     }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 844aafb..397c50f 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -859,6 +859,17 @@
             }
         }
 
+        public void scheduleTimeoutLocked() {
+            // If we didn't reset it right away, do so after we couldn't connect to
+            // it for an extended amount of time to avoid having a black wallpaper.
+            final Handler fgHandler = FgThread.getHandler();
+            fgHandler.removeCallbacks(mResetRunnable);
+            fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
+            if (DEBUG_LIVE) {
+                Slog.i(TAG, "Started wallpaper reconnect timeout for " + mWallpaper.wallpaperComponent);
+            }
+        }
+
         private void processDisconnect(final ServiceConnection connection) {
             synchronized (mLock) {
                 // The wallpaper disappeared.  If this isn't a system-default one, track
@@ -883,13 +894,13 @@
                         } else {
                             mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
 
-                            // If we didn't reset it right away, do so after we couldn't connect to
-                            // it for an extended amount of time to avoid having a black wallpaper.
-                            final Handler fgHandler = FgThread.getHandler();
-                            fgHandler.removeCallbacks(mResetRunnable);
-                            fgHandler.postDelayed(mResetRunnable, WALLPAPER_RECONNECT_TIMEOUT_MS);
-                            if (DEBUG_LIVE) {
-                                Slog.i(TAG, "Started wallpaper reconnect timeout for " + wpService);
+                            clearWallpaperComponentLocked(mWallpaper);
+                            if (bindWallpaperComponentLocked(
+                                    wpService, false, false, mWallpaper, null)) {
+                                mWallpaper.connection.scheduleTimeoutLocked();
+                            } else {
+                                Slog.w(TAG, "Reverting to built-in wallpaper!");
+                                clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
                             }
                         }
                         final String flattened = wpService.flattenToString();
diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java
index ed4543e..64f77a2 100644
--- a/services/core/java/com/android/server/wm/AnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/AnimationAdapter.java
@@ -39,6 +39,12 @@
     boolean getDetachWallpaper();
 
     /**
+     * @return Whether we should show the wallpaper during the animation.
+     * @see Animation#getShowWallpaper()
+     */
+    boolean getShowWallpaper();
+
+    /**
      * @return The background color behind the animation.
      */
     @ColorInt int getBackgroundColor();
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 0d36145..f0ca2ef 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -29,6 +29,7 @@
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
@@ -2138,6 +2139,10 @@
                     && isTransitionEqual(TRANSIT_ACTIVITY_CLOSE)) {
                 // Opening a new activity always supersedes a close for the anim.
                 setAppTransition(transit, flags);
+            } else if (isTaskTransit(transit) && isActivityTransit(mNextAppTransition)) {
+                // Task animations always supersede activity animations, because if we have both, it
+                // usually means that activity transition were just trampoline activities.
+                setAppTransition(transit, flags);
             }
         }
         boolean prepared = prepare();
@@ -2162,6 +2167,21 @@
                 || transit == TRANSIT_KEYGUARD_UNOCCLUDE;
     }
 
+    private static boolean isTaskTransit(int transit) {
+        return transit == TRANSIT_TASK_OPEN
+                || transit == TRANSIT_TASK_CLOSE
+                || transit == TRANSIT_TASK_OPEN_BEHIND
+                || transit == TRANSIT_TASK_TO_BACK
+                || transit == TRANSIT_TASK_TO_FRONT
+                || transit == TRANSIT_TASK_IN_PLACE;
+    }
+
+    private static boolean isActivityTransit(int transit) {
+        return transit == TRANSIT_ACTIVITY_OPEN
+                || transit == TRANSIT_ACTIVITY_CLOSE
+                || transit == TRANSIT_ACTIVITY_RELAUNCH;
+    }
+
     /**
      * @return whether the transition should show the thumbnail being scaled down.
      */
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 8155656..42d6ec0 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -469,7 +469,7 @@
             // never gets updated.
             // If we're becoming invisible, update the client visibility if we are not running an
             // animation. Otherwise, we'll update client visibility in onAnimationFinished.
-            if (visible || !delayed) {
+            if (visible || !isReallyAnimating()) {
                 setClientHidden(!visible);
             }
 
@@ -1668,6 +1668,9 @@
             }
             if (adapter != null) {
                 startAnimation(getPendingTransaction(), adapter, !isVisible());
+                if (adapter.getShowWallpaper()) {
+                    mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+                }
             }
         } else {
             cancelAnimation();
@@ -1793,7 +1796,7 @@
                 "AppWindowToken");
 
         clearThumbnail();
-        setClientHidden(isHidden());
+        setClientHidden(hiddenRequested);
 
         if (mService.mInputMethodTarget != null && mService.mInputMethodTarget.mAppToken == this) {
             getDisplayContent().computeImeTarget(true /* updateImeTarget */);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 473eeda..13357b8 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -154,6 +154,7 @@
 import com.android.internal.util.ToBooleanFunction;
 import com.android.internal.view.IInputMethodClient;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.utils.RotationCache;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -215,7 +216,8 @@
     int mInitialDisplayDensity = 0;
 
     DisplayCutout mInitialDisplayCutout;
-    DisplayCutout mDisplayCutoutOverride;
+    private final RotationCache<DisplayCutout, DisplayCutout> mDisplayCutoutCache
+            = new RotationCache<>(this::calculateDisplayCutoutForRotationUncached);
 
     /**
      * Overridden display size. Initialized with {@link #mInitialDisplayWidth}
@@ -1198,7 +1200,11 @@
     }
 
     DisplayCutout calculateDisplayCutoutForRotation(int rotation) {
-        final DisplayCutout cutout = mInitialDisplayCutout;
+        return mDisplayCutoutCache.getOrCompute(mInitialDisplayCutout, rotation);
+    }
+
+    private DisplayCutout calculateDisplayCutoutForRotationUncached(
+            DisplayCutout cutout, int rotation) {
         if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) {
             return cutout;
         }
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 423be63..5c8fadb 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -174,7 +174,7 @@
             final int position = DockedDividerUtils.calculatePositionForBounds(mTmpRect, dockSide,
                     getContentWidth());
 
-            DisplayCutout displayCutout = mDisplayContent.calculateDisplayCutoutForRotation(
+            final DisplayCutout displayCutout = mDisplayContent.calculateDisplayCutoutForRotation(
                     rotation);
 
             // Since we only care about feasible states, snap to the closest snap target, like it
diff --git a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
index 2173fa3..1b41cb8 100644
--- a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
@@ -43,6 +43,11 @@
     }
 
     @Override
+    public boolean getShowWallpaper() {
+        return mSpec.getShowWallpaper();
+    }
+
+    @Override
     public int getBackgroundColor() {
         return mSpec.getBackgroundColor();
     }
@@ -82,6 +87,13 @@
         }
 
         /**
+         * @see AnimationAdapter#getShowWallpaper
+         */
+        default boolean getShowWallpaper() {
+            return false;
+        }
+
+        /**
          * @see AnimationAdapter#getBackgroundColor
          */
         default int getBackgroundColor() {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 78dd580..31b5c69 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -368,6 +368,11 @@
         }
 
         @Override
+        public boolean getShowWallpaper() {
+            return false;
+        }
+
+        @Override
         public int getBackgroundColor() {
             return 0;
         }
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 35fc99f..ed6e606 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -221,6 +221,11 @@
         }
 
         @Override
+        public boolean getShowWallpaper() {
+            return false;
+        }
+
+        @Override
         public int getBackgroundColor() {
             return 0;
         }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index a4f20b01..6356a35 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -606,8 +606,11 @@
         // If we are ready to perform an app transition, check through all of the app tokens to be
         // shown and see if they are ready to go.
         if (mService.mAppTransition.isReady()) {
-            defaultDisplay.pendingLayoutChanges |=
-                    surfacePlacer.handleAppTransitionReadyLocked();
+            // This needs to be split into two expressions, as handleAppTransitionReadyLocked may
+            // modify dc.pendingLayoutChanges, which would get lost when writing
+            // defaultDisplay.pendingLayoutChanges |= handleAppTransitionReadyLocked()
+            final int layoutChanges = surfacePlacer.handleAppTransitionReadyLocked();
+            defaultDisplay.pendingLayoutChanges |= layoutChanges;
             if (DEBUG_LAYOUT_REPEATS)
                 surfacePlacer.debugLayoutRepeats("after handleAppTransitionReadyLocked",
                         defaultDisplay.pendingLayoutChanges);
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index f2ad6fb..a7d51f1 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -151,7 +151,10 @@
 
         final RecentsAnimationController recentsAnimationController =
                 mService.getRecentsAnimationController();
-        final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
+        final boolean animationWallpaper = w.mAppToken != null && w.mAppToken.getAnimation() != null
+                && w.mAppToken.getAnimation().getShowWallpaper();
+        final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
+                || animationWallpaper;
         final boolean isRecentsTransitionTarget = (recentsAnimationController != null
                 && recentsAnimationController.isWallpaperVisible(w));
         if (isRecentsTransitionTarget) {
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 0863ee9..43fa3d5 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -69,6 +69,11 @@
     }
 
     @Override
+    public boolean getShowWallpaper() {
+        return mAnimation.getShowWallpaper();
+    }
+
+    @Override
     public int getBackgroundColor() {
         return mAnimation.getBackgroundColor();
     }
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 7364e87..66c7293 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -566,14 +566,14 @@
                 if (!allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
                     return false;
                 }
-                final TaskStack stack = wtoken.getStack();
-                final int stackId = stack != null ? stack.mStackId : INVALID_STACK_ID;
+                final int windowingMode = wtoken.getWindowingMode();
                 if (allDrawn) {
-                    outReasons.put(stackId,  APP_TRANSITION_WINDOWS_DRAWN);
+                    outReasons.put(windowingMode,  APP_TRANSITION_WINDOWS_DRAWN);
                 } else {
-                    outReasons.put(stackId, wtoken.startingData instanceof SplashScreenStartingData
-                            ? APP_TRANSITION_SPLASH_SCREEN
-                            : APP_TRANSITION_SNAPSHOT);
+                    outReasons.put(windowingMode,
+                            wtoken.startingData instanceof SplashScreenStartingData
+                                    ? APP_TRANSITION_SPLASH_SCREEN
+                                    : APP_TRANSITION_SNAPSHOT);
                 }
             }
 
diff --git a/services/core/java/com/android/server/wm/utils/RotationCache.java b/services/core/java/com/android/server/wm/utils/RotationCache.java
new file mode 100644
index 0000000..7a06cbc
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/RotationCache.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.wm.utils;
+
+import android.util.SparseArray;
+
+import java.util.Arrays;
+
+/**
+ * Caches the result of a rotation-dependent computation.
+ *
+ * The cache is discarded once the identity of the other parameter changes.
+ *
+ * @param <T> type of the parameter to the computation
+ * @param <R> type of the result of the computation
+ */
+public class RotationCache<T,R> {
+
+    private final RotationDependentComputation<T,R> mComputation;
+    private final SparseArray<R> mCache = new SparseArray<>(4);
+    private T mCachedFor;
+
+    public RotationCache(RotationDependentComputation<T, R> computation) {
+        mComputation = computation;
+    }
+
+    /**
+     * Looks up the result of the computation, or calculates it if needed.
+     *
+     * @param t a parameter to the rotation-dependent computation.
+     * @param rotation the rotation for which to perform the rotation-dependent computation.
+     * @return the result of the rotation-dependent computation.
+     */
+    public R getOrCompute(T t, int rotation) {
+        if (t != mCachedFor) {
+            mCache.clear();
+            mCachedFor = t;
+        }
+        final int idx = mCache.indexOfKey(rotation);
+        if (idx >= 0) {
+            return mCache.valueAt(idx);
+        }
+        final R result = mComputation.compute(t, rotation);
+        mCache.put(rotation, result);
+        return result;
+    }
+
+    /**
+     * A computation that takes a generic input and is dependent on the rotation. The result can
+     * be cached by {@link RotationCache}.
+     */
+    @FunctionalInterface
+    public interface RotationDependentComputation<T, R> {
+        R compute(T t, int rotation);
+    }
+}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index c2771e4..0a1da57 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -99,14 +99,16 @@
 using android::hardware::gnss::V1_0::IGnssDebug;
 using android::hardware::gnss::V1_0::IGnssGeofenceCallback;
 using android::hardware::gnss::V1_0::IGnssGeofencing;
-using android::hardware::gnss::V1_0::IGnssMeasurementCallback;
+using IGnssMeasurementCallback_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurementCallback;
 using android::hardware::gnss::V1_0::IGnssNavigationMessage;
 using android::hardware::gnss::V1_0::IGnssNavigationMessageCallback;
 using android::hardware::gnss::V1_0::IGnssNi;
 using android::hardware::gnss::V1_0::IGnssNiCallback;
 using android::hardware::gnss::V1_0::IGnssXtra;
 using android::hardware::gnss::V1_0::IGnssXtraCallback;
+
 using android::hardware::gnss::V1_1::IGnssCallback;
+using android::hardware::gnss::V1_1::IGnssMeasurementCallback;
 
 struct GnssDeathRecipient : virtual public hidl_death_recipient
 {
@@ -393,10 +395,7 @@
     // New in 1.1
     Return<void> gnssNameCb(const android::hardware::hidl_string& name) override;
 
-    static GnssSvInfo sGnssSvList[static_cast<uint32_t>(
-            android::hardware::gnss::V1_0::GnssMax::SVS_COUNT)];
-    static size_t sGnssSvListSize;
-
+    // TODO(b/73306084): Reconsider allocation cost vs threadsafety on these statics
     static const char* sNmeaString;
     static size_t sNmeaStringLength;
 };
@@ -412,11 +411,8 @@
     return Void();
 }
 
-IGnssCallback::GnssSvInfo GnssCallback::sGnssSvList[static_cast<uint32_t>(
-        android::hardware::gnss::V1_0::GnssMax::SVS_COUNT)];
 const char* GnssCallback::sNmeaString = nullptr;
 size_t GnssCallback::sNmeaStringLength = 0;
-size_t GnssCallback::sGnssSvListSize = 0;
 
 Return<void> GnssCallback::gnssLocationCb(const GnssLocation& location) {
     JNIEnv* env = getJniEnv();
@@ -430,6 +426,7 @@
                         boolToJbool(hasLatLong),
                         jLocation);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    env->DeleteLocalRef(jLocation);
     return Void();
 }
 
@@ -443,20 +440,55 @@
 Return<void> GnssCallback::gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) {
     JNIEnv* env = getJniEnv();
 
-    sGnssSvListSize = svStatus.numSvs;
-    if (sGnssSvListSize > static_cast<uint32_t>(
+    uint32_t listSize = svStatus.numSvs;
+    if (listSize > static_cast<uint32_t>(
             android::hardware::gnss::V1_0::GnssMax::SVS_COUNT)) {
-        ALOGD("Too many satellites %zd. Clamps to %u.", sGnssSvListSize,
+        ALOGD("Too many satellites %u. Clamps to %u.", listSize,
               static_cast<uint32_t>(android::hardware::gnss::V1_0::GnssMax::SVS_COUNT));
-        sGnssSvListSize = static_cast<uint32_t>(android::hardware::gnss::V1_0::GnssMax::SVS_COUNT);
+        listSize = static_cast<uint32_t>(android::hardware::gnss::V1_0::GnssMax::SVS_COUNT);
     }
 
-    // Copy GNSS SV info into sGnssSvList, if any.
-    if (svStatus.numSvs > 0) {
-        memcpy(sGnssSvList, svStatus.gnssSvList.data(), sizeof(GnssSvInfo) * sGnssSvListSize);
+    jintArray svidWithFlagArray = env->NewIntArray(listSize);
+    jfloatArray cn0Array = env->NewFloatArray(listSize);
+    jfloatArray elevArray = env->NewFloatArray(listSize);
+    jfloatArray azimArray = env->NewFloatArray(listSize);
+    jfloatArray carrierFreqArray = env->NewFloatArray(listSize);
+
+    jint* svidWithFlags = env->GetIntArrayElements(svidWithFlagArray, 0);
+    jfloat* cn0s = env->GetFloatArrayElements(cn0Array, 0);
+    jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
+    jfloat* azim = env->GetFloatArrayElements(azimArray, 0);
+    jfloat* carrierFreq = env->GetFloatArrayElements(carrierFreqArray, 0);
+
+    /*
+     * Read GNSS SV info.
+     */
+    for (size_t i = 0; i < listSize; ++i) {
+        enum ShiftWidth: uint8_t {
+            SVID_SHIFT_WIDTH = 8,
+            CONSTELLATION_TYPE_SHIFT_WIDTH = 4
+        };
+
+        const IGnssCallback::GnssSvInfo& info = svStatus.gnssSvList.data()[i];
+        svidWithFlags[i] = (info.svid << SVID_SHIFT_WIDTH) |
+            (static_cast<uint32_t>(info.constellation) << CONSTELLATION_TYPE_SHIFT_WIDTH) |
+            static_cast<uint32_t>(info.svFlag);
+        cn0s[i] = info.cN0Dbhz;
+        elev[i] = info.elevationDegrees;
+        azim[i] = info.azimuthDegrees;
+        carrierFreq[i] = info.carrierFrequencyHz;
     }
 
-    env->CallVoidMethod(mCallbacksObj, method_reportSvStatus);
+    env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0);
+    env->ReleaseFloatArrayElements(cn0Array, cn0s, 0);
+    env->ReleaseFloatArrayElements(elevArray, elev, 0);
+    env->ReleaseFloatArrayElements(azimArray, azim, 0);
+    env->ReleaseFloatArrayElements(carrierFreqArray, carrierFreq, 0);
+
+    env->CallVoidMethod(mCallbacksObj, method_reportSvStatus,
+            static_cast<jint>(listSize), svidWithFlagArray, cn0Array, elevArray, azimArray,
+            carrierFreqArray);
+
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
     return Void();
 }
@@ -575,6 +607,7 @@
                         timestamp);
 
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    env->DeleteLocalRef(jLocation);
     return Void();
 }
 
@@ -590,6 +623,7 @@
                         status,
                         jLocation);
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    env->DeleteLocalRef(jLocation);
     return Void();
 }
 
@@ -699,21 +733,24 @@
  * GnssMeasurement interface.
  */
 struct GnssMeasurementCallback : public IGnssMeasurementCallback {
-    Return<void> GnssMeasurementCb(const IGnssMeasurementCallback::GnssData& data);
+    Return<void> gnssMeasurementCb(const IGnssMeasurementCallback::GnssData& data) override;
+    Return<void> GnssMeasurementCb(const IGnssMeasurementCallback_V1_0::GnssData& data) override;
  private:
-    jobject translateGnssMeasurement(
-            JNIEnv* env, const IGnssMeasurementCallback::GnssMeasurement* measurement);
-    jobject translateGnssClock(
-            JNIEnv* env, const IGnssMeasurementCallback::GnssClock* clock);
+    void translateGnssMeasurement_V1_0(
+            JNIEnv* env, const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurement,
+            JavaObject object);
     jobjectArray translateGnssMeasurements(
             JNIEnv* env,
             const IGnssMeasurementCallback::GnssMeasurement* measurements,
+            const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurements_v1_0,
             size_t count);
+    jobject translateGnssClock(
+            JNIEnv* env, const IGnssMeasurementCallback::GnssClock* clock);
     void setMeasurementData(JNIEnv* env, jobject clock, jobjectArray measurementArray);
 };
 
 
-Return<void> GnssMeasurementCallback::GnssMeasurementCb(
+Return<void> GnssMeasurementCallback::gnssMeasurementCb(
         const IGnssMeasurementCallback::GnssData& data) {
     JNIEnv* env = getJniEnv();
 
@@ -722,7 +759,7 @@
 
     clock = translateGnssClock(env, &data.clock);
     measurementArray = translateGnssMeasurements(
-        env, data.measurements.data(), data.measurementCount);
+        env, data.measurements.data(), NULL, data.measurements.size());
     setMeasurementData(env, clock, measurementArray);
 
     env->DeleteLocalRef(clock);
@@ -730,10 +767,27 @@
     return Void();
 }
 
-jobject GnssMeasurementCallback::translateGnssMeasurement(
-        JNIEnv* env, const IGnssMeasurementCallback::GnssMeasurement* measurement) {
-    JavaObject object(env, "android/location/GnssMeasurement");
+Return<void> GnssMeasurementCallback::GnssMeasurementCb(
+        const IGnssMeasurementCallback_V1_0::GnssData& data) {
+    JNIEnv* env = getJniEnv();
 
+    jobject clock;
+    jobjectArray measurementArray;
+
+    clock = translateGnssClock(env, &data.clock);
+    measurementArray = translateGnssMeasurements(
+        env, NULL, data.measurements.data(), data.measurementCount);
+    setMeasurementData(env, clock, measurementArray);
+
+    env->DeleteLocalRef(clock);
+    env->DeleteLocalRef(measurementArray);
+    return Void();
+}
+
+// preallocate object as: JavaObject object(env, "android/location/GnssMeasurement");
+void GnssMeasurementCallback::translateGnssMeasurement_V1_0(
+        JNIEnv* env, const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurement,
+        JavaObject object) {
     uint32_t flags = static_cast<uint32_t>(measurement->flags);
 
     SET(Svid, static_cast<int32_t>(measurement->svid));
@@ -757,13 +811,8 @@
         SET(CarrierFrequencyHz, measurement->carrierFrequencyHz);
     }
 
-    if (flags & static_cast<uint32_t>(GnssMeasurementFlags::HAS_CARRIER_PHASE)) {
-        SET(CarrierPhase, measurement->carrierPhase);
-    }
-
-    if (flags & static_cast<uint32_t>(GnssMeasurementFlags::HAS_CARRIER_PHASE_UNCERTAINTY)) {
-        SET(CarrierPhaseUncertainty, measurement->carrierPhaseUncertainty);
-    }
+    // Intentionally not copying deprecated fields of carrierCycles,
+    // carrierPhase, carrierPhaseUncertainty
 
     SET(MultipathIndicator, static_cast<int32_t>(measurement->multipathIndicator));
 
@@ -774,8 +823,6 @@
     if (flags & static_cast<uint32_t>(GnssMeasurementFlags::HAS_AUTOMATIC_GAIN_CONTROL)) {
         SET(AutomaticGainControlLevelInDb, measurement->agcLevelDb);
     }
-
-    return object.get();
 }
 
 jobject GnssMeasurementCallback::translateGnssClock(
@@ -818,8 +865,9 @@
 }
 
 jobjectArray GnssMeasurementCallback::translateGnssMeasurements(JNIEnv* env,
-                                       const IGnssMeasurementCallback::GnssMeasurement*
-                                       measurements, size_t count) {
+         const IGnssMeasurementCallback::GnssMeasurement* measurements,
+         const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurements_v1_0,
+         size_t count) {
     if (count == 0) {
         return NULL;
     }
@@ -831,11 +879,18 @@
             NULL /* initialElement */);
 
     for (uint16_t i = 0; i < count; ++i) {
-        jobject gnssMeasurement = translateGnssMeasurement(
-            env,
-            &measurements[i]);
-        env->SetObjectArrayElement(gnssMeasurementArray, i, gnssMeasurement);
-        env->DeleteLocalRef(gnssMeasurement);
+        JavaObject object(env, "android/location/GnssMeasurement");
+        if (measurements != NULL) {
+            translateGnssMeasurement_V1_0(env, &(measurements[i].v1_0), object);
+
+            // Set the V1_1 flag
+            SET(AccumulatedDeltaRangeState,
+                    static_cast<int32_t>(measurements[i].accumulatedDeltaRangeState));
+        } else {
+            translateGnssMeasurement_V1_0(env, &(measurements_v1_0[i]), object);
+        }
+
+        env->SetObjectArrayElement(gnssMeasurementArray, i, object.get());
     }
 
     env->DeleteLocalRef(gnssMeasurementClass);
@@ -1063,7 +1118,7 @@
     method_reportLocation = env->GetMethodID(clazz, "reportLocation",
             "(ZLandroid/location/Location;)V");
     method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
-    method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V");
+    method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "(I[I[F[F[F[F)V");
     method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(II[B)V");
     method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
     method_setEngineCapabilities = env->GetMethodID(clazz, "setEngineCapabilities", "(I)V");
@@ -1369,49 +1424,6 @@
     }
 }
 
-/*
- * This enum is used by the read_sv_status method to combine the svid,
- * constellation and svFlag fields.
- */
-enum ShiftWidth: uint8_t {
-    SVID_SHIFT_WIDTH = 8,
-    CONSTELLATION_TYPE_SHIFT_WIDTH = 4
-};
-
-static jint android_location_GnssLocationProvider_read_sv_status(JNIEnv* env, jobject /* obj */,
-        jintArray svidWithFlagArray, jfloatArray cn0Array, jfloatArray elevArray,
-        jfloatArray azumArray, jfloatArray carrierFreqArray) {
-    /*
-     * This method should only be called from within a call to reportSvStatus.
-     */
-    jint* svidWithFlags = env->GetIntArrayElements(svidWithFlagArray, 0);
-    jfloat* cn0s = env->GetFloatArrayElements(cn0Array, 0);
-    jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
-    jfloat* azim = env->GetFloatArrayElements(azumArray, 0);
-    jfloat* carrierFreq = env->GetFloatArrayElements(carrierFreqArray, 0);
-
-    /*
-     * Read GNSS SV info.
-     */
-    for (size_t i = 0; i < GnssCallback::sGnssSvListSize; ++i) {
-        const IGnssCallback::GnssSvInfo& info = GnssCallback::sGnssSvList[i];
-        svidWithFlags[i] = (info.svid << SVID_SHIFT_WIDTH) |
-            (static_cast<uint32_t>(info.constellation) << CONSTELLATION_TYPE_SHIFT_WIDTH) |
-            static_cast<uint32_t>(info.svFlag);
-        cn0s[i] = info.cN0Dbhz;
-        elev[i] = info.elevationDegrees;
-        azim[i] = info.azimuthDegrees;
-        carrierFreq[i] = info.carrierFrequencyHz;
-    }
-
-    env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0);
-    env->ReleaseFloatArrayElements(cn0Array, cn0s, 0);
-    env->ReleaseFloatArrayElements(elevArray, elev, 0);
-    env->ReleaseFloatArrayElements(azumArray, azim, 0);
-    env->ReleaseFloatArrayElements(carrierFreqArray, carrierFreq, 0);
-    return static_cast<jint>(GnssCallback::sGnssSvListSize);
-}
-
 static void android_location_GnssLocationProvider_agps_set_reference_location_cellid(
         JNIEnv* /* env */, jobject /* obj */, jint type, jint mcc, jint mnc, jint lac, jint cid) {
     IAGnssRil::AGnssRefLocation location;
@@ -2057,9 +2069,6 @@
     {"native_delete_aiding_data",
             "(I)V",
             reinterpret_cast<void*>(android_location_GnssLocationProvider_delete_aiding_data)},
-    {"native_read_sv_status",
-            "([I[F[F[F[F)I",
-            reinterpret_cast<void *>(android_location_GnssLocationProvider_read_sv_status)},
     {"native_read_nmea", "([BI)I", reinterpret_cast<void *>(
             android_location_GnssLocationProvider_read_nmea)},
     {"native_inject_time", "(JJI)V", reinterpret_cast<void *>(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 8753344..29d5d54 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -50,6 +50,8 @@
 import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID;
 import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL;
 import static android.app.admin.DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
 import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
@@ -9817,6 +9819,13 @@
     @Override
     public void setLockTaskFeatures(ComponentName who, int flags) {
         Preconditions.checkNotNull(who, "ComponentName is null");
+
+        // Throw if Overview is used without Home.
+        boolean hasHome = (flags & LOCK_TASK_FEATURE_HOME) != 0;
+        boolean hasOverview = (flags & LOCK_TASK_FEATURE_OVERVIEW) != 0;
+        Preconditions.checkArgument(hasHome || !hasOverview,
+                "Cannot use LOCK_TASK_FEATURE_OVERVIEW without LOCK_TASK_FEATURE_HOME");
+
         final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
             enforceCanCallLockTaskLocked(who);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 9923fa8..d3df924 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -108,7 +108,8 @@
         assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0);
     }
 
-    @Test
+    // TODO: b/71582913
+    //@Test
     public void testPausingWhenVisibleFromStopped() throws Exception {
         final MutableBoolean pauseFound = new MutableBoolean(false);
         doAnswer((InvocationOnMock invocationOnMock) -> {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 00a85a5..f58766f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -94,7 +94,6 @@
 
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
@@ -3765,19 +3764,22 @@
         // The DO can still set lock task packages
         final String[] doPackages = {"doPackage1", "doPackage2"};
         final int flags = DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS
-                | DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS;
+                | DevicePolicyManager.LOCK_TASK_FEATURE_HOME
+                | DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
         verifyCanSetLockTask(DpmMockContext.CALLER_SYSTEM_USER_UID, UserHandle.USER_SYSTEM, admin1, doPackages, flags);
 
         final String[] secondaryPoPackages = {"secondaryPoPackage1", "secondaryPoPackage2"};
         final int secondaryPoFlags = DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS
-                | DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS;
+                | DevicePolicyManager.LOCK_TASK_FEATURE_HOME
+                | DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
         verifyCanNotSetLockTask(DpmMockContext.CALLER_UID, admin3, secondaryPoPackages, secondaryPoFlags);
 
         // Managed profile is unaffiliated - shouldn't be able to setLockTaskPackages.
         mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
         final String[] poPackages = {"poPackage1", "poPackage2"};
         final int poFlags = DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS
-                | DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS;
+                | DevicePolicyManager.LOCK_TASK_FEATURE_HOME
+                | DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
         verifyCanNotSetLockTask(MANAGED_PROFILE_ADMIN_UID, adminDifferentPackage, poPackages, poFlags);
 
         // Setting same affiliation ids
@@ -3820,7 +3822,8 @@
 
         final String[] poPackages = {"poPackage1", "poPackage2"};
         final int poFlags = DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS
-                | DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS;
+                | DevicePolicyManager.LOCK_TASK_FEATURE_HOME
+                | DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
         verifyCanSetLockTask(DpmMockContext.CALLER_UID, DpmMockContext.CALLER_USER_HANDLE, admin1,
                 poPackages, poFlags);
 
@@ -3836,10 +3839,25 @@
         mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
         final String[] mpoPackages = {"poPackage1", "poPackage2"};
         final int mpoFlags = DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS
-                | DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS;
+                | DevicePolicyManager.LOCK_TASK_FEATURE_HOME
+                | DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
         verifyCanNotSetLockTask(MANAGED_PROFILE_ADMIN_UID, adminDifferentPackage, mpoPackages, mpoFlags);
     }
 
+    public void testLockTaskFeatures_IllegalArgumentException() throws Exception {
+        // Setup a device owner.
+        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+        setupDeviceOwner();
+        // Lock task policy is updated when loading user data.
+        verifyLockTaskState(UserHandle.USER_SYSTEM);
+
+        final int flags = DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS
+                | DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
+        assertExpectException(IllegalArgumentException.class,
+                "Cannot use LOCK_TASK_FEATURE_OVERVIEW without LOCK_TASK_FEATURE_HOME",
+                () -> dpm.setLockTaskFeatures(admin1, flags));
+    }
+
     public void testIsDeviceManaged() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 8bd0df4..3c35c5b 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -639,7 +639,7 @@
     }
 
     @Test
-    public void setRecoveryStatus_forOneAlias() throws Exception {
+    public void setRecoveryStatus() throws Exception {
         int userId = UserHandle.getCallingUserId();
         int uid = Binder.getCallingUid();
         int status = 100;
@@ -652,55 +652,12 @@
         assertThat(statuses).hasSize(1);
         assertThat(statuses).containsEntry(alias, status);
 
-        mRecoverableKeyStoreManager.setRecoveryStatus(
-                /*packageName=*/ null, new String[] {alias}, status2);
+        mRecoverableKeyStoreManager.setRecoveryStatus(alias, status2);
         statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null);
         assertThat(statuses).hasSize(1);
         assertThat(statuses).containsEntry(alias, status2); // updated
     }
 
-    @Test
-    public void setRecoveryStatus_for2Aliases() throws Exception {
-        int userId = UserHandle.getCallingUserId();
-        int uid = Binder.getCallingUid();
-        int status = 100;
-        int status2 = 200;
-        int status3 = 300;
-        String alias = "key1";
-        String alias2 = "key2";
-        WrappedKey wrappedKey = new WrappedKey(NONCE, KEY_MATERIAL, GENERATION_ID, status);
-        mRecoverableKeyStoreDb.insertKey(userId, uid, alias, wrappedKey);
-        mRecoverableKeyStoreDb.insertKey(userId, uid, alias2, wrappedKey);
-        Map<String, Integer> statuses =
-                mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null);
-        assertThat(statuses).hasSize(2);
-        assertThat(statuses).containsEntry(alias, status);
-        assertThat(statuses).containsEntry(alias2, status);
-
-        mRecoverableKeyStoreManager.setRecoveryStatus(
-                /*packageName=*/ null, /*aliases=*/ null, status2);
-        statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null);
-        assertThat(statuses).hasSize(2);
-        assertThat(statuses).containsEntry(alias, status2); // updated
-        assertThat(statuses).containsEntry(alias2, status2); // updated
-
-        mRecoverableKeyStoreManager.setRecoveryStatus(
-                /*packageName=*/ null, new String[] {alias2}, status3);
-
-        statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null);
-        assertThat(statuses).hasSize(2);
-        assertThat(statuses).containsEntry(alias, status2);
-        assertThat(statuses).containsEntry(alias2, status3); // updated
-
-        mRecoverableKeyStoreManager.setRecoveryStatus(
-                /*packageName=*/ null, new String[] {alias, alias2}, status);
-
-        statuses = mRecoverableKeyStoreManager.getRecoveryStatus(/*packageName=*/ null);
-        assertThat(statuses).hasSize(2);
-        assertThat(statuses).containsEntry(alias, status); // updated
-        assertThat(statuses).containsEntry(alias2, status); // updated
-    }
-
     private static byte[] encryptedApplicationKey(
             SecretKey recoveryKey, byte[] applicationKey) throws Exception {
         return KeySyncUtils.encryptKeysWithRecoveryKey(recoveryKey, ImmutableMap.of(
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 8cf575e..4ca1647 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -289,6 +289,12 @@
         }
 
         @Override
+        int injectBinderCallingPid() {
+            // Note it's not used in tests, so just return a "random" value.
+            return mInjectedCallingUid * 123;
+        }
+
+        @Override
         int injectGetPackageUid(String packageName, int userId) {
             return getInjectedPackageInfo(packageName, userId, false).applicationInfo.uid;
         }
@@ -325,6 +331,11 @@
         }
 
         @Override
+        boolean injectHasUnlimitedShortcutsApiCallsPermission(int callingPid, int callingUid) {
+            return mInjectHasUnlimitedShortcutsApiCallsPermission;
+        }
+
+        @Override
         ComponentName getDefaultLauncher(@UserIdInt int userId) {
             final ComponentName activity = mDefaultLauncher.get(userId);
             if (activity != null) {
@@ -519,6 +530,12 @@
         }
 
         @Override
+        int injectBinderCallingPid() {
+            // Note it's not used in tests, so just return a "random" value.
+            return mInjectedCallingUid * 123;
+        }
+
+        @Override
         long injectClearCallingIdentity() {
             final int prevCallingUid = mInjectedCallingUid;
             mInjectedCallingUid = Process.SYSTEM_UID;
@@ -705,6 +722,8 @@
 
     protected boolean mInjectCheckAccessShortcutsPermission = false;
 
+    protected boolean mInjectHasUnlimitedShortcutsApiCallsPermission = false;
+
     static {
         QUERY_ALL.setQueryFlags(
                 ShortcutQuery.FLAG_GET_ALL_KINDS);
@@ -1207,7 +1226,7 @@
     }
 
     /**
-     * This controls {@link ShortcutService#hasShortcutHostPermission(String, int)}, but
+     * This controls {@link ShortcutService#hasShortcutHostPermission}, but
      * not {@link ShortcutService#getDefaultLauncher(int)}.  To control the later, use
      * {@link #setDefaultLauncher(int, ComponentName)}.
      */
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 857925b..845e05d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -366,7 +366,38 @@
         });
     }
 
-    public void testPublishWithNoActivity() {
+    public void testUnlimitedCalls() {
+        setCaller(CALLING_PACKAGE_1, USER_0);
+
+        final ShortcutInfo si1 = makeShortcut("shortcut1");
+
+        assertEquals(3, mManager.getRemainingCallCount());
+
+        assertTrue(mManager.setDynamicShortcuts(list(si1)));
+        assertEquals(2, mManager.getRemainingCallCount());
+
+        assertTrue(mManager.addDynamicShortcuts(list(si1)));
+        assertEquals(1, mManager.getRemainingCallCount());
+
+        assertTrue(mManager.updateShortcuts(list(si1)));
+        assertEquals(0, mManager.getRemainingCallCount());
+
+        // Unlimited now.
+        mInjectHasUnlimitedShortcutsApiCallsPermission = true;
+
+        assertEquals(3, mManager.getRemainingCallCount());
+
+        assertTrue(mManager.setDynamicShortcuts(list(si1)));
+        assertEquals(3, mManager.getRemainingCallCount());
+
+        assertTrue(mManager.addDynamicShortcuts(list(si1)));
+        assertEquals(3, mManager.getRemainingCallCount());
+
+        assertTrue(mManager.updateShortcuts(list(si1)));
+        assertEquals(3, mManager.getRemainingCallCount());
+    }
+
+   public void testPublishWithNoActivity() {
         // If activity is not explicitly set, use the default one.
 
         mRunningUsers.put(USER_10, true);
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/RotationCacheTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/RotationCacheTest.java
new file mode 100644
index 0000000..d3cf978
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/utils/RotationCacheTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.wm.utils;
+
+import static android.util.Pair.create;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Pair;
+
+import com.android.server.wm.utils.RotationCache.RotationDependentComputation;
+
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class RotationCacheTest {
+
+    private RotationCache<Object, Pair<Object, Integer>> mCache;
+    private boolean mComputationCalled;
+
+    @Before
+    public void setUp() throws Exception {
+        mComputationCalled = false;
+        mCache = new RotationCache<>((o, rot) -> {
+            mComputationCalled = true;
+            return create(o, rot);
+        });
+    }
+
+    @Test
+    public void getOrCompute_computes() throws Exception {
+        assertThat(mCache.getOrCompute("hello", 0), equalTo(create("hello", 0)));
+        assertThat(mCache.getOrCompute("hello", 1), equalTo(create("hello", 1)));
+        assertThat(mCache.getOrCompute("hello", 2), equalTo(create("hello", 2)));
+        assertThat(mCache.getOrCompute("hello", 3), equalTo(create("hello", 3)));
+    }
+
+    @Test
+    public void getOrCompute_sameParam_sameRot_hitsCache() throws Exception {
+        assertNotNull(mCache.getOrCompute("hello", 1));
+
+        mComputationCalled = false;
+        assertThat(mCache.getOrCompute("hello", 1), equalTo(create("hello", 1)));
+        assertThat(mComputationCalled, is(false));
+    }
+
+    @Test
+    public void getOrCompute_sameParam_hitsCache_forAllRots() throws Exception {
+        assertNotNull(mCache.getOrCompute("hello", 3));
+        assertNotNull(mCache.getOrCompute("hello", 2));
+        assertNotNull(mCache.getOrCompute("hello", 1));
+        assertNotNull(mCache.getOrCompute("hello", 0));
+
+        mComputationCalled = false;
+        assertThat(mCache.getOrCompute("hello", 1), equalTo(create("hello", 1)));
+        assertThat(mCache.getOrCompute("hello", 0), equalTo(create("hello", 0)));
+        assertThat(mCache.getOrCompute("hello", 2), equalTo(create("hello", 2)));
+        assertThat(mCache.getOrCompute("hello", 3), equalTo(create("hello", 3)));
+        assertThat(mComputationCalled, is(false));
+    }
+
+    @Test
+    public void getOrCompute_changingParam_recomputes() throws Exception {
+        assertNotNull(mCache.getOrCompute("hello", 1));
+
+        assertThat(mCache.getOrCompute("world", 1), equalTo(create("world", 1)));
+    }
+
+    @Test
+    public void getOrCompute_changingParam_clearsCacheForDifferentRots() throws Exception {
+        assertNotNull(mCache.getOrCompute("hello", 1));
+        assertNotNull(mCache.getOrCompute("world", 2));
+
+        mComputationCalled = false;
+        assertThat(mCache.getOrCompute("hello", 1), equalTo(create("hello", 1)));
+        assertThat(mComputationCalled, is(true));
+    }
+}
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 9ebce71..354d2d5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -161,8 +161,8 @@
         when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
                 .thenReturn(SOUND_URI);
 
-        mHelper = new RankingHelper(getContext(), mPm, mHandler, mUsageStats,
-                new String[] {ImportanceExtractor.class.getName()});
+        mHelper = new RankingHelper(getContext(), mPm, mHandler, mock(ZenModeHelper.class),
+                mUsageStats, new String[] {ImportanceExtractor.class.getName()});
 
         mNotiGroupGSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID)
                 .setContentTitle("A")
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeExtractorTest.java
new file mode 100644
index 0000000..faba6b6
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeExtractorTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class ZenModeExtractorTest extends UiServiceTestCase {
+
+    @Mock
+    ZenModeHelper mZenModeHelper;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testExtractIntercepted() {
+        ZenModeExtractor extractor = new ZenModeExtractor();
+        extractor.setZenHelper(mZenModeHelper);
+        NotificationRecord r = generateRecord();
+
+        assertFalse(r.isIntercepted());
+
+        when(mZenModeHelper.shouldIntercept(any())).thenReturn(true);
+
+        extractor.process(r);
+
+        assertTrue(r.isIntercepted());
+    }
+
+    @Test
+    public void testExtractVisualDisturbancesNotIntercepted() {
+        ZenModeExtractor extractor = new ZenModeExtractor();
+        extractor.setZenHelper(mZenModeHelper);
+        NotificationRecord r = generateRecord();
+
+        when(mZenModeHelper.shouldIntercept(any())).thenReturn(false);
+        when(mZenModeHelper.shouldSuppressWhenScreenOff()).thenReturn(false);
+
+        extractor.process(r);
+
+        assertEquals(0, r.getSuppressedVisualEffects());
+    }
+
+    @Test
+    public void testExtractVisualDisturbancesIntercepted() {
+        ZenModeExtractor extractor = new ZenModeExtractor();
+        extractor.setZenHelper(mZenModeHelper);
+        NotificationRecord r = generateRecord();
+
+        when(mZenModeHelper.shouldIntercept(any())).thenReturn(true);
+        when(mZenModeHelper.shouldSuppressWhenScreenOff()).thenReturn(true);
+        when(mZenModeHelper.shouldSuppressWhenScreenOn()).thenReturn(true);
+
+        extractor.process(r);
+
+        assertEquals(NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF
+                | NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON,
+                r.getSuppressedVisualEffects());
+    }
+
+    private NotificationRecord generateRecord() {
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+        final Notification.Builder builder = new Notification.Builder(getContext())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        Notification n = builder.build();
+        StatusBarNotification sbn = new StatusBarNotification("", "", 0, "", 0,
+                0, n, UserHandle.ALL, null, System.currentTimeMillis());
+        return new NotificationRecord(getContext(), sbn, channel);
+    }
+}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 25c54b3..f4bb32d 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -34,6 +34,7 @@
 import android.hardware.soundtrigger.SoundTrigger.SoundModel;
 import android.hardware.soundtrigger.SoundTrigger.SoundModelEvent;
 import android.hardware.soundtrigger.SoundTriggerModule;
+import android.os.Binder;
 import android.os.DeadObjectException;
 import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
@@ -880,21 +881,26 @@
     }
 
     private void initializeTelephonyAndPowerStateListeners() {
-        // Get the current call state synchronously for the first recognition.
-        mCallActive = mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE;
+        long token = Binder.clearCallingIdentity();
+        try {
+            // Get the current call state synchronously for the first recognition.
+            mCallActive = mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE;
 
-        // Register for call state changes when the first call to start recognition occurs.
-        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+            // Register for call state changes when the first call to start recognition occurs.
+            mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
 
-        // Register for power saver mode changes when the first call to start recognition
-        // occurs.
-        if (mPowerSaveModeListener == null) {
-            mPowerSaveModeListener = new PowerSaveModeListener();
-            mContext.registerReceiver(mPowerSaveModeListener,
-                    new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
+            // Register for power saver mode changes when the first call to start recognition
+            // occurs.
+            if (mPowerSaveModeListener == null) {
+                mPowerSaveModeListener = new PowerSaveModeListener();
+                mContext.registerReceiver(mPowerSaveModeListener,
+                        new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
+            }
+            mIsPowerSaveMode = mPowerManager.getPowerSaveState(ServiceType.SOUND)
+                    .batterySaverEnabled;
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
-        mIsPowerSaveMode = mPowerManager.getPowerSaveState(ServiceType.SOUND)
-                .batterySaverEnabled;
     }
 
     // Sends an error callback to all models with a valid registered callback.
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
new file mode 100644
index 0000000..b362df9
--- /dev/null
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -0,0 +1,160 @@
+/*
+ * 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.telephony;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.location.LocationManager;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Process;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.SparseBooleanArray;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Helper for performing location access checks.
+ * @hide
+ */
+public final class LocationAccessPolicy {
+    /**
+     * API to determine if the caller has permissions to get cell location.
+     *
+     * @param pkgName Package name of the application requesting access
+     * @param uid The uid of the package
+     * @param pid The pid of the package
+     * @return boolean true or false if permissions is granted
+     */
+    public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
+            int uid, int pid) throws SecurityException {
+        Trace.beginSection("TelephonyLocationCheck");
+        try {
+            // Always allow the phone process to access location. This avoid breaking legacy code
+            // that rely on public-facing APIs to access cell location, and it doesn't create a
+            // info leak risk because the cell location is stored in the phone process anyway.
+            if (uid == Process.PHONE_UID) {
+                return true;
+            }
+
+            // We always require the location permission and also require the
+            // location mode to be on for non-legacy apps. Legacy apps are
+            // required to be in the foreground to at least mitigate the case
+            // where a legacy app the user is not using tracks their location.
+            // Granting ACCESS_FINE_LOCATION to an app automatically grants it
+            // ACCESS_COARSE_LOCATION.
+
+            if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION, pid, uid) ==
+                    PackageManager.PERMISSION_DENIED) {
+                return false;
+            }
+            final int opCode = AppOpsManager.permissionToOpCode(
+                    Manifest.permission.ACCESS_COARSE_LOCATION);
+            if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
+                    .noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
+                return false;
+            }
+            if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))
+                    && !isLegacyForeground(context, pkgName, uid)) {
+                return false;
+            }
+            // If the user or profile is current, permission is granted.
+            // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
+            return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context);
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
+        int locationMode = Settings.Secure.getIntForUser(context.getContentResolver(),
+                Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userId);
+        return locationMode != Settings.Secure.LOCATION_MODE_OFF
+                && locationMode != Settings.Secure.LOCATION_MODE_SENSORS_ONLY;
+    }
+
+    private static boolean isLegacyForeground(@NonNull Context context, @NonNull String pkgName,
+            int uid) {
+        long token = Binder.clearCallingIdentity();
+        try {
+            return isLegacyVersion(context, pkgName) && isForegroundApp(context, uid);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private static boolean isLegacyVersion(@NonNull Context context, @NonNull String pkgName) {
+        try {
+            if (context.getPackageManager().getApplicationInfo(pkgName, 0)
+                    .targetSdkVersion <= Build.VERSION_CODES.O) {
+                return true;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            // In case of exception, assume known app (more strict checking)
+            // Note: This case will never happen since checkPackage is
+            // called to verify validity before checking app's version.
+        }
+        return false;
+    }
+
+    private static boolean isForegroundApp(@NonNull Context context, int uid) {
+        final ActivityManager am = context.getSystemService(ActivityManager.class);
+        return am.getUidImportance(uid) <= ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+    }
+
+    private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
+        return context.checkCallingOrSelfPermission(
+                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private static boolean isCurrentProfile(@NonNull Context context, int uid) {
+        long token = Binder.clearCallingIdentity();
+        try {
+            final int currentUser = ActivityManager.getCurrentUser();
+            final int callingUserId = UserHandle.getUserId(uid);
+            if (callingUserId == currentUser) {
+                return true;
+            } else {
+                List<UserInfo> userProfiles = context.getSystemService(
+                        UserManager.class).getProfiles(currentUser);
+                for (UserInfo user : userProfiles) {
+                    if (user.id == callingUserId) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index da3d87b..ce1b80c 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -30,7 +30,6 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.telephony.mbms.DownloadStateCallback;
 import android.telephony.mbms.FileInfo;
@@ -53,6 +52,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -107,11 +107,8 @@
     /**
      * {@link Uri} extra that Android will attach to the intent supplied via
      * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
-     * Indicates the location of the successfully downloaded file within the temp file root set
-     * via {@link #setTempFileRootDirectory(File)}.
-     * While you may use this file in-place, it is highly encouraged that you move
-     * this file to a different location after receiving the download completion intent, as this
-     * file resides within the temp file directory.
+     * Indicates the location of the successfully downloaded file within the directory that the
+     * app provided via the builder.
      *
      * Will always be set to a non-null value if
      * {@link #EXTRA_MBMS_DOWNLOAD_RESULT} is set to {@link #RESULT_SUCCESSFUL}.
@@ -220,6 +217,8 @@
      */
     public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4;
 
+    private static final String DESTINATION_SANITY_CHECK_FILE_NAME = "destinationSanityCheckFile";
+
     private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
 
     private final Context mContext;
@@ -236,23 +235,20 @@
     private final Map<DownloadStateCallback, InternalDownloadStateCallback>
             mInternalDownloadCallbacks = new HashMap<>();
 
-    private MbmsDownloadSession(Context context, MbmsDownloadSessionCallback callback,
-            int subscriptionId, Handler handler) {
+    private MbmsDownloadSession(Context context, Executor executor, int subscriptionId,
+            MbmsDownloadSessionCallback callback) {
         mContext = context;
         mSubscriptionId = subscriptionId;
-        if (handler == null) {
-            handler = new Handler(Looper.getMainLooper());
-        }
-        mInternalCallback = new InternalDownloadSessionCallback(callback, handler);
+        mInternalCallback = new InternalDownloadSessionCallback(callback, executor);
     }
 
     /**
      * Create a new {@link MbmsDownloadSession} using the system default data subscription ID.
-     * See {@link #create(Context, MbmsDownloadSessionCallback, int, Handler)}
+     * See {@link #create(Context, Executor, int, MbmsDownloadSessionCallback)}
      */
     public static MbmsDownloadSession create(@NonNull Context context,
-            @NonNull MbmsDownloadSessionCallback callback, @NonNull Handler handler) {
-        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+            @NonNull Executor executor, @NonNull MbmsDownloadSessionCallback callback) {
+        return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
     }
 
     /**
@@ -279,24 +275,24 @@
      * {@link MbmsDownloadSession} that you received before calling this method again.
      *
      * @param context The instance of {@link Context} to use
-     * @param callback A callback to get asynchronous error messages and file service updates.
+     * @param executor The executor on which you wish to execute callbacks.
      * @param subscriptionId The data subscription ID to use
-     * @param handler The {@link Handler} on which callbacks should be enqueued.
+     * @param callback A callback to get asynchronous error messages and file service updates.
      * @return A new instance of {@link MbmsDownloadSession}, or null if an error occurred during
      * setup.
      */
     public static @Nullable MbmsDownloadSession create(@NonNull Context context,
-            final @NonNull MbmsDownloadSessionCallback callback,
-            int subscriptionId, @NonNull Handler handler) {
+            @NonNull Executor executor, int subscriptionId,
+            final @NonNull MbmsDownloadSessionCallback callback) {
         if (!sIsInitialized.compareAndSet(false, true)) {
             throw new IllegalStateException("Cannot have two active instances");
         }
         MbmsDownloadSession session =
-                new MbmsDownloadSession(context, callback, subscriptionId, handler);
+                new MbmsDownloadSession(context, executor, subscriptionId, callback);
         final int result = session.bindAndInitialize();
         if (result != MbmsErrors.SUCCESS) {
             sIsInitialized.set(false);
-            handler.post(new Runnable() {
+            executor.execute(new Runnable() {
                 @Override
                 public void run() {
                     callback.onError(result, null);
@@ -500,6 +496,10 @@
      * {@link MbmsDownloadSession#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp
      * file root directory.
      *
+     * If the {@link DownloadRequest} has a destination that is not on the same filesystem as the
+     * temp file directory provided via {@link #getTempFileRootDirectory()}, an
+     * {@link IllegalArgumentException} will be thrown.
+     *
      * Asynchronous errors through the callback may include any error not specific to the
      * streaming use-case.
      * @param request The request that specifies what should be downloaded.
@@ -522,6 +522,8 @@
             setTempFileRootDirectory(tempRootDirectory);
         }
 
+        checkDownloadRequestDestination(request);
+
         try {
             int result = downloadService.download(request);
             if (result == MbmsErrors.SUCCESS) {
@@ -568,21 +570,21 @@
      * this method will throw an {@link IllegalArgumentException}.
      *
      * @param request The {@link DownloadRequest} that you want updates on.
+     * @param executor The {@link Executor} on which calls to {@code callback} should be executed.
      * @param callback The callback that should be called when the middleware has information to
      *                 share on the download.
-     * @param handler The {@link Handler} on which calls to {@code callback} should be enqueued on.
      * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
      * and some other error code otherwise.
      */
     public int registerStateCallback(@NonNull DownloadRequest request,
-            @NonNull DownloadStateCallback callback, @NonNull Handler handler) {
+            @NonNull Executor executor, @NonNull DownloadStateCallback callback) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
             throw new IllegalStateException("Middleware not yet bound");
         }
 
         InternalDownloadStateCallback internalCallback =
-                new InternalDownloadStateCallback(callback, handler);
+                new InternalDownloadStateCallback(callback, executor);
 
         try {
             int result = downloadService.registerStateCallback(request, internalCallback,
@@ -604,7 +606,7 @@
 
     /**
      * Un-register a callback previously registered via
-     * {@link #registerStateCallback(DownloadRequest, DownloadStateCallback, Handler)}. After
+     * {@link #registerStateCallback(DownloadRequest, Executor, DownloadStateCallback)}. After
      * this method is called, no further callbacks will be enqueued on the {@link Handler}
      * provided upon registration, even if this method throws an exception.
      *
@@ -692,7 +694,7 @@
      * The state will be delivered as a callback via
      * {@link DownloadStateCallback#onStateUpdated(DownloadRequest, FileInfo, int)}. If no such
      * callback has been registered via
-     * {@link #registerStateCallback(DownloadRequest, DownloadStateCallback, Handler)}, this
+     * {@link #registerStateCallback(DownloadRequest, Executor, DownloadStateCallback)}, this
      * method will be a no-op.
      *
      * If the middleware has no record of the
@@ -775,7 +777,7 @@
      * instance of {@link MbmsDownloadSessionCallback}, but callbacks that have already been
      * enqueued will still be delivered.
      *
-     * It is safe to call {@link #create(Context, MbmsDownloadSessionCallback, int, Handler)} to
+     * It is safe to call {@link #create(Context, Executor, int, MbmsDownloadSessionCallback)} to
      * obtain another instance of {@link MbmsDownloadSession} immediately after this method
      * returns.
      *
@@ -831,6 +833,36 @@
         }
     }
 
+    private void checkDownloadRequestDestination(DownloadRequest request) {
+        File downloadRequestDestination = new File(request.getDestinationUri().getPath());
+        if (!downloadRequestDestination.isDirectory()) {
+            throw new IllegalArgumentException("The destination path must be a directory");
+        }
+        // Check if the request destination is okay to use by attempting to rename an empty
+        // file to there.
+        File testFile = new File(MbmsTempFileProvider.getEmbmsTempFileDir(mContext),
+                DESTINATION_SANITY_CHECK_FILE_NAME);
+        File testFileDestination = new File(downloadRequestDestination,
+                DESTINATION_SANITY_CHECK_FILE_NAME);
+
+        try {
+            if (!testFile.exists()) {
+                testFile.createNewFile();
+            }
+            if (!testFile.renameTo(testFileDestination)) {
+                throw new IllegalArgumentException("Destination provided in the download request " +
+                        "is invalid -- files in the temp file directory cannot be directly moved " +
+                        "there.");
+            }
+        } catch (IOException e) {
+            throw new IllegalStateException("Got IOException while testing out the destination: "
+                    + e);
+        } finally {
+            testFile.delete();
+            testFileDestination.delete();
+        }
+    }
+
     private File getDownloadRequestTokenPath(DownloadRequest request) {
         File tempFileLocation = MbmsUtils.getEmbmsTempFileDirForService(mContext,
                 request.getFileServiceId());
diff --git a/telephony/java/android/telephony/MbmsStreamingSession.java b/telephony/java/android/telephony/MbmsStreamingSession.java
index fb2ff7b..42c760d4 100644
--- a/telephony/java/android/telephony/MbmsStreamingSession.java
+++ b/telephony/java/android/telephony/MbmsStreamingSession.java
@@ -24,9 +24,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.ServiceConnection;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.telephony.mbms.InternalStreamingSessionCallback;
 import android.telephony.mbms.InternalStreamingServiceCallback;
@@ -42,6 +40,7 @@
 
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -89,14 +88,11 @@
     private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
 
     /** @hide */
-    private MbmsStreamingSession(Context context, MbmsStreamingSessionCallback callback,
-                    int subscriptionId, Handler handler) {
+    private MbmsStreamingSession(Context context, Executor executor, int subscriptionId,
+            MbmsStreamingSessionCallback callback) {
         mContext = context;
         mSubscriptionId = subscriptionId;
-        if (handler == null) {
-            handler = new Handler(Looper.getMainLooper());
-        }
-        mInternalCallback = new InternalStreamingSessionCallback(callback, handler);
+        mInternalCallback = new InternalStreamingSessionCallback(callback, executor);
     }
 
     /**
@@ -117,25 +113,25 @@
      * {@link MbmsStreamingSession} that you received before calling this method again.
      *
      * @param context The {@link Context} to use.
+     * @param executor The executor on which you wish to execute callbacks.
+     * @param subscriptionId The subscription ID to use.
      * @param callback A callback object on which you wish to receive results of asynchronous
      *                 operations.
-     * @param subscriptionId The subscription ID to use.
-     * @param handler The handler you wish to receive callbacks on.
      * @return An instance of {@link MbmsStreamingSession}, or null if an error occurred.
      */
     public static @Nullable MbmsStreamingSession create(@NonNull Context context,
-            final @NonNull MbmsStreamingSessionCallback callback, int subscriptionId,
-            @NonNull Handler handler) {
+            @NonNull Executor executor, int subscriptionId,
+            final @NonNull MbmsStreamingSessionCallback callback) {
         if (!sIsInitialized.compareAndSet(false, true)) {
             throw new IllegalStateException("Cannot create two instances of MbmsStreamingSession");
         }
-        MbmsStreamingSession session = new MbmsStreamingSession(context, callback,
-                subscriptionId, handler);
+        MbmsStreamingSession session = new MbmsStreamingSession(context, executor,
+                subscriptionId, callback);
 
         final int result = session.bindAndInitialize();
         if (result != MbmsErrors.SUCCESS) {
             sIsInitialized.set(false);
-            handler.post(new Runnable() {
+            executor.execute(new Runnable() {
                 @Override
                 public void run() {
                     callback.onError(result, null);
@@ -148,22 +144,22 @@
 
     /**
      * Create a new {@link MbmsStreamingSession} using the system default data subscription ID.
-     * See {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)}.
+     * See {@link #create(Context, Executor, int, MbmsStreamingSessionCallback)}.
      */
     public static MbmsStreamingSession create(@NonNull Context context,
-            @NonNull MbmsStreamingSessionCallback callback, @NonNull Handler handler) {
-        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+            @NonNull Executor executor, @NonNull MbmsStreamingSessionCallback callback) {
+        return create(context, executor, SubscriptionManager.getDefaultSubscriptionId(), callback);
     }
 
     /**
      * Terminates this instance. Also terminates
      * any streaming services spawned from this instance as if
-     * {@link StreamingService#stopStreaming()} had been called on them. After this method returns,
+     * {@link StreamingService#close()} had been called on them. After this method returns,
      * no further callbacks originating from the middleware will be enqueued on the provided
      * instance of {@link MbmsStreamingSessionCallback}, but callbacks that have already been
      * enqueued will still be delivered.
      *
-     * It is safe to call {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)} to
+     * It is safe to call {@link #create(Context, Executor, int, MbmsStreamingSessionCallback)} to
      * obtain another instance of {@link MbmsStreamingSession} immediately after this method
      * returns.
      *
@@ -237,20 +233,20 @@
      * {@link MbmsErrors.StreamingErrors}.
      *
      * @param serviceInfo The information about the service to stream.
+     * @param executor The executor on which you wish to execute callbacks for this stream.
      * @param callback A callback that'll be called when something about the stream changes.
-     * @param handler A handler that calls to {@code callback} should be called on.
      * @return An instance of {@link StreamingService} through which the stream can be controlled.
      *         May be {@code null} if an error occurred.
      */
     public @Nullable StreamingService startStreaming(StreamingServiceInfo serviceInfo,
-            StreamingServiceCallback callback, @NonNull Handler handler) {
+            @NonNull Executor executor, StreamingServiceCallback callback) {
         IMbmsStreamingService streamingService = mService.get();
         if (streamingService == null) {
             throw new IllegalStateException("Middleware not yet bound");
         }
 
         InternalStreamingServiceCallback serviceCallback = new InternalStreamingServiceCallback(
-                callback, handler);
+                callback, executor);
 
         StreamingService serviceForApp = new StreamingService(
                 mSubscriptionId, streamingService, this, serviceInfo, serviceCallback);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 11a1984..4a61437 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -613,9 +613,9 @@
      *                 onSubscriptionsChanged overridden.
      */
     public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
-        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
+        String pkgName = mContext != null ? mContext.getOpPackageName() : "<unknown>";
         if (DBG) {
-            logd("register OnSubscriptionsChangedListener pkgForDebug=" + pkgForDebug
+            logd("register OnSubscriptionsChangedListener pkgName=" + pkgName
                     + " listener=" + listener);
         }
         try {
@@ -624,7 +624,7 @@
             ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
                     "telephony.registry"));
             if (tr != null) {
-                tr.addOnSubscriptionsChangedListener(pkgForDebug, listener.callback);
+                tr.addOnSubscriptionsChangedListener(pkgName, listener.callback);
             }
         } catch (RemoteException ex) {
             // Should not happen
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fefc03d..cdc1ba9 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4054,6 +4054,9 @@
      * To unregister a listener, pass the listener object and set the
      * events argument to
      * {@link PhoneStateListener#LISTEN_NONE LISTEN_NONE} (0).
+     * Note: if you call this method while in the middle of a binder transaction, you <b>must</b>
+     * call {@link android.os.Binder#clearCallingIdentity()} before calling this method. A
+     * {@link SecurityException} will be thrown otherwise.
      *
      * @param listener The {@link PhoneStateListener} object to register
      *                 (or unregister)
@@ -4846,8 +4849,6 @@
             return;
         }
 
-        Rlog.d(TAG, "setTelephonyProperty: success phoneId=" + phoneId +
-                " property=" + property + " value: " + value + " propVal=" + propVal);
         SystemProperties.set(property, propVal);
     }
 
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index f0d60b6..602c796 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -27,10 +27,13 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.Externalizable;
+import java.io.File;
 import java.io.IOException;
+import java.io.ObjectInput;
 import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
 import java.io.ObjectOutputStream;
-import java.io.Serializable;
 import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
@@ -54,34 +57,116 @@
     public static final int MAX_DESTINATION_URI_SIZE = 50000;
 
     /** @hide */
-    private static class OpaqueDataContainer implements Serializable {
-        private final String appIntent;
-        private final int version;
+    private static class SerializationDataContainer implements Externalizable {
+        private String fileServiceId;
+        private Uri source;
+        private Uri destination;
+        private int subscriptionId;
+        private String appIntent;
+        private int version;
 
-        public OpaqueDataContainer(String appIntent, int version) {
-            this.appIntent = appIntent;
-            this.version = version;
+        public SerializationDataContainer() {}
+
+        SerializationDataContainer(DownloadRequest request) {
+            fileServiceId = request.fileServiceId;
+            source = request.sourceUri;
+            destination = request.destinationUri;
+            subscriptionId = request.subscriptionId;
+            appIntent = request.serializedResultIntentForApp;
+            version = request.version;
+        }
+
+        @Override
+        public void writeExternal(ObjectOutput objectOutput) throws IOException {
+            objectOutput.write(version);
+            objectOutput.writeUTF(fileServiceId);
+            objectOutput.writeUTF(source.toString());
+            objectOutput.writeUTF(destination.toString());
+            objectOutput.write(subscriptionId);
+            objectOutput.writeUTF(appIntent);
+        }
+
+        @Override
+        public void readExternal(ObjectInput objectInput) throws IOException {
+            version = objectInput.read();
+            fileServiceId = objectInput.readUTF();
+            source = Uri.parse(objectInput.readUTF());
+            destination = Uri.parse(objectInput.readUTF());
+            subscriptionId = objectInput.read();
+            appIntent = objectInput.readUTF();
+            // Do version checks here -- future versions may have other fields.
         }
     }
 
     public static class Builder {
         private String fileServiceId;
         private Uri source;
+        private Uri destination;
         private int subscriptionId;
         private String appIntent;
         private int version = CURRENT_VERSION;
 
+        /**
+         * Constructs a {@link Builder} from a {@link DownloadRequest}
+         * @param other The {@link DownloadRequest} from which the data for the {@link Builder}
+         *              should come.
+         * @return An instance of {@link Builder} pre-populated with data from the provided
+         *         {@link DownloadRequest}.
+         */
+        public static Builder fromDownloadRequest(DownloadRequest other) {
+            Builder result = new Builder(other.sourceUri, other.destinationUri)
+                    .setServiceId(other.fileServiceId)
+                    .setSubscriptionId(other.subscriptionId);
+            result.appIntent = other.serializedResultIntentForApp;
+            // Version of the result is going to be the current version -- as this class gets
+            // updated, new fields will be set to default values in here.
+            return result;
+        }
+
+        /**
+         * This method constructs a new instance of {@link Builder} based on the serialized data
+         * passed in.
+         * @param data A byte array, the contents of which should have been originally obtained
+         *             from {@link DownloadRequest#toByteArray()}.
+         */
+        public static Builder fromSerializedRequest(byte[] data) {
+            Builder builder;
+            try {
+                ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(data));
+                SerializationDataContainer dataContainer =
+                        (SerializationDataContainer) stream.readObject();
+                builder = new Builder(dataContainer.source, dataContainer.destination);
+                builder.version = dataContainer.version;
+                builder.appIntent = dataContainer.appIntent;
+                builder.fileServiceId = dataContainer.fileServiceId;
+                builder.subscriptionId = dataContainer.subscriptionId;
+            } catch (IOException e) {
+                // Really should never happen
+                Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
+                throw new IllegalArgumentException(e);
+            } catch (ClassNotFoundException e) {
+                Log.e(LOG_TAG, "Got ClassNotFoundException trying to parse opaque data");
+                throw new IllegalArgumentException(e);
+            }
+            return builder;
+        }
 
         /**
          * Builds a new DownloadRequest.
          * @param sourceUri the source URI for the DownloadRequest to be built. This URI should
          *     never be null.
+         * @param destinationUri The final location for the file(s) that are to be downloaded. It
+         *     must be on the same filesystem as the temp file directory set via
+         *     {@link android.telephony.MbmsDownloadSession#setTempFileRootDirectory(File)}.
+         *     The provided path must be a directory that exists. An
+         *     {@link IllegalArgumentException} will be thrown otherwise.
          */
-        public Builder(@NonNull Uri sourceUri) {
-            if (sourceUri == null) {
-                throw new IllegalArgumentException("Source URI must be non-null.");
+        public Builder(@NonNull Uri sourceUri, @NonNull Uri destinationUri) {
+            if (sourceUri == null || destinationUri == null) {
+                throw new IllegalArgumentException("Source and destination URIs must be non-null.");
             }
             source = sourceUri;
+            destination = destinationUri;
         }
 
         /**
@@ -130,68 +215,34 @@
             return this;
         }
 
-        /**
-         * For use by the middleware to set the byte array of opaque data. The opaque data
-         * includes information about the download request that is used by the client app and the
-         * manager code, but is irrelevant to the middleware.
-         * @param data A byte array, the contents of which should have been originally obtained
-         *             from {@link DownloadRequest#getOpaqueData()}.
-         * @hide
-         */
-        @SystemApi
-        public Builder setOpaqueData(byte[] data) {
-            try {
-                ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(data));
-                OpaqueDataContainer dataContainer = (OpaqueDataContainer) stream.readObject();
-                version = dataContainer.version;
-                appIntent = dataContainer.appIntent;
-            } catch (IOException e) {
-                // Really should never happen
-                Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
-                throw new IllegalArgumentException(e);
-            } catch (ClassNotFoundException e) {
-                Log.e(LOG_TAG, "Got ClassNotFoundException trying to parse opaque data");
-                throw new IllegalArgumentException(e);
-            }
-            return this;
-        }
-
         public DownloadRequest build() {
-            return new DownloadRequest(fileServiceId, source, subscriptionId, appIntent, version);
+            return new DownloadRequest(fileServiceId, source, destination,
+                    subscriptionId, appIntent, version);
         }
     }
 
     private final String fileServiceId;
     private final Uri sourceUri;
+    private final Uri destinationUri;
     private final int subscriptionId;
     private final String serializedResultIntentForApp;
     private final int version;
 
     private DownloadRequest(String fileServiceId,
-            Uri source, int sub,
+            Uri source, Uri destination, int sub,
             String appIntent, int version) {
         this.fileServiceId = fileServiceId;
         sourceUri = source;
         subscriptionId = sub;
+        destinationUri = destination;
         serializedResultIntentForApp = appIntent;
         this.version = version;
     }
 
-    public static DownloadRequest copy(DownloadRequest other) {
-        return new DownloadRequest(other);
-    }
-
-    private DownloadRequest(DownloadRequest dr) {
-        fileServiceId = dr.fileServiceId;
-        sourceUri = dr.sourceUri;
-        subscriptionId = dr.subscriptionId;
-        serializedResultIntentForApp = dr.serializedResultIntentForApp;
-        version = dr.version;
-    }
-
     private DownloadRequest(Parcel in) {
         fileServiceId = in.readString();
         sourceUri = in.readParcelable(getClass().getClassLoader());
+        destinationUri = in.readParcelable(getClass().getClassLoader());
         subscriptionId = in.readInt();
         serializedResultIntentForApp = in.readString();
         version = in.readInt();
@@ -204,6 +255,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(fileServiceId);
         out.writeParcelable(sourceUri, flags);
+        out.writeParcelable(destinationUri, flags);
         out.writeInt(subscriptionId);
         out.writeString(serializedResultIntentForApp);
         out.writeInt(version);
@@ -224,6 +276,13 @@
     }
 
     /**
+     * @return The destination {@link Uri} of the downloaded file.
+     */
+    public Uri getDestinationUri() {
+        return destinationUri;
+    }
+
+    /**
      * @return The subscription ID on which to perform MBMS operations.
      */
     public int getSubscriptionId() {
@@ -244,19 +303,16 @@
     }
 
     /**
-     * For use by the middleware only. The byte array returned from this method should be
-     * persisted and sent back to the app upon download completion or failure by passing it into
-     * {@link Builder#setOpaqueData(byte[])}.
-     * @return A byte array of opaque data to persist.
-     * @hide
+     * This method returns a byte array that may be persisted to disk and restored to a
+     * {@link DownloadRequest}. The instance of {@link DownloadRequest} persisted by this method
+     * may be recovered via {@link Builder#fromSerializedRequest(byte[])}.
+     * @return A byte array of data to persist.
      */
-    @SystemApi
-    public byte[] getOpaqueData() {
+    public byte[] toByteArray() {
         try {
             ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             ObjectOutputStream stream = new ObjectOutputStream(byteArrayOutputStream);
-            OpaqueDataContainer container = new OpaqueDataContainer(
-                    serializedResultIntentForApp, version);
+            SerializationDataContainer container = new SerializationDataContainer(this);
             stream.writeObject(container);
             stream.flush();
             return byteArrayOutputStream.toByteArray();
@@ -299,15 +355,6 @@
     }
 
     /**
-     * @hide
-     */
-    public boolean isMultipartDownload() {
-        // TODO: figure out what qualifies a request as a multipart download request.
-        return getSourceUri().getLastPathSegment() != null &&
-                getSourceUri().getLastPathSegment().contains("*");
-    }
-
-    /**
      * Retrieves the hash string that should be used as the filename when storing a token for
      * this DownloadRequest.
      * @hide
@@ -320,8 +367,9 @@
             throw new RuntimeException("Could not get sha256 hash object");
         }
         if (version >= 1) {
-            // Hash the source URI and the app intent
+            // Hash the source, destination, and the app intent
             digest.update(sourceUri.toString().getBytes(StandardCharsets.UTF_8));
+            digest.update(destinationUri.toString().getBytes(StandardCharsets.UTF_8));
             if (serializedResultIntentForApp != null) {
                 digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
             }
@@ -344,12 +392,13 @@
                 version == request.version &&
                 Objects.equals(fileServiceId, request.fileServiceId) &&
                 Objects.equals(sourceUri, request.sourceUri) &&
+                Objects.equals(destinationUri, request.destinationUri) &&
                 Objects.equals(serializedResultIntentForApp, request.serializedResultIntentForApp);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(fileServiceId, sourceUri,
+        return Objects.hash(fileServiceId, sourceUri, destinationUri,
                 subscriptionId, serializedResultIntentForApp, version);
     }
 }
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
index a7a5958..c2a79d8 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
@@ -16,22 +16,23 @@
 
 package android.telephony.mbms;
 
-import android.os.Handler;
+import android.os.Binder;
 import android.os.RemoteException;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /** @hide */
 public class InternalDownloadSessionCallback extends IMbmsDownloadSessionCallback.Stub {
 
-    private final Handler mHandler;
+    private final Executor mExecutor;
     private final MbmsDownloadSessionCallback mAppCallback;
     private volatile boolean mIsStopped = false;
 
     public InternalDownloadSessionCallback(MbmsDownloadSessionCallback appCallback,
-            Handler handler) {
+            Executor executor) {
         mAppCallback = appCallback;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     @Override
@@ -40,10 +41,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onError(errorCode, message);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onError(errorCode, message);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -54,10 +60,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onFileServicesUpdated(services);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onFileServicesUpdated(services);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -68,18 +79,19 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onMiddlewareReady();
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onMiddlewareReady();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
 
-    public Handler getHandler() {
-        return mHandler;
-    }
-
     public void stop() {
         mIsStopped = true;
     }
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
index 8702952..f30ae27 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
@@ -16,20 +16,22 @@
 
 package android.telephony.mbms;
 
-import android.os.Handler;
+import android.os.Binder;
 import android.os.RemoteException;
 
+import java.util.concurrent.Executor;
+
 /**
  * @hide
  */
 public class InternalDownloadStateCallback extends IDownloadStateCallback.Stub {
-    private final Handler mHandler;
+    private final Executor mExecutor;
     private final DownloadStateCallback mAppCallback;
     private volatile boolean mIsStopped = false;
 
-    public InternalDownloadStateCallback(DownloadStateCallback appCallback, Handler handler) {
+    public InternalDownloadStateCallback(DownloadStateCallback appCallback, Executor executor) {
         mAppCallback = appCallback;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     @Override
@@ -40,11 +42,16 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
-                        fullDownloadSize, currentDecodedSize, fullDecodedSize);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+                            fullDownloadSize, currentDecodedSize, fullDecodedSize);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -56,10 +63,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onStateUpdated(request, fileInfo, state);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onStateUpdated(request, fileInfo, state);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
index eb6579ce..e9f39ff 100644
--- a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
@@ -16,18 +16,21 @@
 
 package android.telephony.mbms;
 
-import android.os.Handler;
+import android.os.Binder;
 import android.os.RemoteException;
 
+import java.util.concurrent.Executor;
+
 /** @hide */
 public class InternalStreamingServiceCallback extends IStreamingServiceCallback.Stub {
     private final StreamingServiceCallback mAppCallback;
-    private final Handler mHandler;
+    private final Executor mExecutor;
     private volatile boolean mIsStopped = false;
 
-    public InternalStreamingServiceCallback(StreamingServiceCallback appCallback, Handler handler) {
+    public InternalStreamingServiceCallback(StreamingServiceCallback appCallback,
+            Executor executor) {
         mAppCallback = appCallback;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     @Override
@@ -36,10 +39,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onError(errorCode, message);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onError(errorCode, message);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -50,10 +58,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onStreamStateUpdated(state, reason);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onStreamStateUpdated(state, reason);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -64,10 +77,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onMediaDescriptionUpdated();
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onMediaDescriptionUpdated();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -78,10 +96,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onBroadcastSignalStrengthUpdated(signalStrength);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -92,10 +115,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onStreamMethodUpdated(methodType);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onStreamMethodUpdated(methodType);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
index d782d12..d47f5ad 100644
--- a/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
@@ -16,21 +16,22 @@
 
 package android.telephony.mbms;
 
-import android.os.Handler;
+import android.os.Binder;
 import android.os.RemoteException;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /** @hide */
 public class InternalStreamingSessionCallback extends IMbmsStreamingSessionCallback.Stub {
-    private final Handler mHandler;
+    private final Executor mExecutor;
     private final MbmsStreamingSessionCallback mAppCallback;
     private volatile boolean mIsStopped = false;
 
     public InternalStreamingSessionCallback(MbmsStreamingSessionCallback appCallback,
-            Handler handler) {
+            Executor executor) {
         mAppCallback = appCallback;
-        mHandler = handler;
+        mExecutor = executor;
     }
 
     @Override
@@ -39,10 +40,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onError(errorCode, message);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onError(errorCode, message);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -54,10 +60,15 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onStreamingServicesUpdated(services);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onStreamingServicesUpdated(services);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
@@ -68,18 +79,19 @@
             return;
         }
 
-        mHandler.post(new Runnable() {
+        mExecutor.execute(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onMiddlewareReady();
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppCallback.onMiddlewareReady();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         });
     }
 
-    public Handler getHandler() {
-        return mHandler;
-    }
-
     public void stop() {
         mIsStopped = true;
     }
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index 9ef188c..b0c00c6 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -21,8 +21,10 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Bundle;
 import android.telephony.MbmsDownloadSession;
@@ -31,14 +33,11 @@
 
 import java.io.File;
 import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -62,6 +61,8 @@
     /** @hide */
     public static final String MBMS_FILE_PROVIDER_META_DATA_KEY = "mbms-file-provider-authority";
 
+    private static final String EMBMS_INTENT_PERMISSION = "android.permission.SEND_EMBMS_INTENTS";
+
     /**
      * Indicates that the requested operation completed without error.
      * @hide
@@ -137,6 +138,8 @@
     /** @hide */
     @Override
     public void onReceive(Context context, Intent intent) {
+        verifyPermissionIntegrity(context);
+
         if (!verifyIntentContents(context, intent)) {
             setResultCode(RESULT_MALFORMED_INTENT);
             return;
@@ -260,20 +263,18 @@
 
         FileInfo completedFileInfo =
                 (FileInfo) intent.getParcelableExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO);
-        Path stagingDirectory = FileSystems.getDefault().getPath(
-                MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath(),
-                TEMP_FILE_STAGING_LOCATION);
+        Path appSpecifiedDestination = FileSystems.getDefault().getPath(
+                request.getDestinationUri().getPath());
 
-        Uri stagedFileLocation;
+        Uri finalLocation;
         try {
-            stagedFileLocation = stageTempFile(finalTempFile, stagingDirectory);
+            finalLocation = moveToFinalLocation(finalTempFile, appSpecifiedDestination);
         } catch (IOException e) {
             Log.w(LOG_TAG, "Failed to move temp file to final destination");
             setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
             return;
         }
-        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI,
-                stagedFileLocation);
+        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI, finalLocation);
         intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO, completedFileInfo);
 
         context.sendBroadcast(intentForApp);
@@ -437,19 +438,22 @@
     }
 
     /*
-     * Moves a tempfile located at fromPath to a new location in the staging directory.
+     * Moves a tempfile located at fromPath to its final home where the app wants it
      */
-    private static Uri stageTempFile(Uri fromPath, Path stagingDirectory) throws IOException {
+    private static Uri moveToFinalLocation(Uri fromPath, Path appSpecifiedPath) throws IOException {
         if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) {
-            Log.w(LOG_TAG, "Moving source uri " + fromPath+ " does not have a file scheme");
+            Log.w(LOG_TAG, "Downloaded file location uri " + fromPath +
+                    " does not have a file scheme");
             return null;
         }
 
         Path fromFile = FileSystems.getDefault().getPath(fromPath.getPath());
-        if (!Files.isDirectory(stagingDirectory)) {
-            Files.createDirectory(stagingDirectory);
+        if (!Files.isDirectory(appSpecifiedPath)) {
+            Files.createDirectory(appSpecifiedPath);
         }
-        Path result = Files.move(fromFile, stagingDirectory.resolve(fromFile.getFileName()));
+        // TODO: do we want to support directory trees within the download directory?
+        Path result = Files.move(fromFile, appSpecifiedPath.resolve(fromFile.getFileName()),
+                StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
 
         return Uri.fromFile(result.toFile());
     }
@@ -513,39 +517,29 @@
         return mMiddlewarePackageNameCache;
     }
 
-    private static boolean manualMove(File src, File dst) {
-        InputStream in = null;
-        OutputStream out = null;
-        try {
-            if (!dst.exists()) {
-                dst.createNewFile();
-            }
-            in = new FileInputStream(src);
-            out = new FileOutputStream(dst);
-            byte[] buffer = new byte[2048];
-            int len;
-            do {
-                len = in.read(buffer);
-                out.write(buffer, 0, len);
-            } while (len > 0);
-        } catch (IOException e) {
-            Log.w(LOG_TAG, "Manual file move failed due to exception "  + e);
-            if (dst.exists()) {
-                dst.delete();
-            }
-            return false;
-        } finally {
-            try {
-                if (in != null) {
-                    in.close();
-                }
-                if (out != null) {
-                    out.close();
-                }
-            } catch (IOException e) {
-                Log.w(LOG_TAG, "Error closing streams: " + e);
-            }
+    private void verifyPermissionIntegrity(Context context) {
+        PackageManager pm = context.getPackageManager();
+        Intent queryIntent = new Intent(context, MbmsDownloadReceiver.class);
+        List<ResolveInfo> infos = pm.queryBroadcastReceivers(queryIntent, 0);
+        if (infos.size() != 1) {
+            throw new IllegalStateException("Non-unique download receiver in your app");
         }
-        return true;
+        ActivityInfo selfInfo = infos.get(0).activityInfo;
+        if (selfInfo == null) {
+            throw new IllegalStateException("Queried ResolveInfo does not contain a receiver");
+        }
+        if (MbmsUtils.getOverrideServiceName(context,
+                MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION) != null) {
+            // If an override was specified, just make sure that the permission isn't null.
+            if (selfInfo.permission == null) {
+                throw new IllegalStateException(
+                        "MbmsDownloadReceiver must require some permission");
+            }
+            return;
+        }
+        if (!Objects.equals(EMBMS_INTENT_PERMISSION, selfInfo.permission)) {
+            throw new IllegalStateException("MbmsDownloadReceiver must require the " +
+                    "SEND_EMBMS_INTENTS permission.");
+        }
     }
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsErrors.java b/telephony/java/android/telephony/mbms/MbmsErrors.java
index 75ca35e..b5fec44 100644
--- a/telephony/java/android/telephony/mbms/MbmsErrors.java
+++ b/telephony/java/android/telephony/mbms/MbmsErrors.java
@@ -108,8 +108,8 @@
 
         /**
          * Indicates that the app called
-         * {@link MbmsStreamingSession#startStreaming(
-         * StreamingServiceInfo, StreamingServiceCallback, android.os.Handler)}
+         * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
+         * java.util.concurrent.Executor, StreamingServiceCallback)}
          * more than once for the same {@link StreamingServiceInfo}.
          */
         public static final int ERROR_DUPLICATE_START_STREAM = 303;
diff --git a/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
index 5c130a0..6e03957 100644
--- a/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
@@ -22,11 +22,12 @@
 import android.telephony.MbmsStreamingSession;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * A callback class that is used to receive information from the middleware on MBMS streaming
  * services. An instance of this object should be passed into
- * {@link MbmsStreamingSession#create(Context, MbmsStreamingSessionCallback, int, Handler)}.
+ * {@link MbmsStreamingSession#create(Context, Executor, int, MbmsStreamingSessionCallback)}.
  */
 public class MbmsStreamingSessionCallback {
     /**
diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java
index b4ad1d7..ef317ee 100644
--- a/telephony/java/android/telephony/mbms/MbmsUtils.java
+++ b/telephony/java/android/telephony/mbms/MbmsUtils.java
@@ -50,7 +50,7 @@
         return new ComponentName(ci.packageName, ci.name);
     }
 
-    private static ComponentName getOverrideServiceName(Context context, String serviceAction) {
+    public static ComponentName getOverrideServiceName(Context context, String serviceAction) {
         String metaDataKey = null;
         switch (serviceAction) {
             case MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION:
diff --git a/telephony/java/android/telephony/mbms/StreamingService.java b/telephony/java/android/telephony/mbms/StreamingService.java
index ec9134a..b6239fe 100644
--- a/telephony/java/android/telephony/mbms/StreamingService.java
+++ b/telephony/java/android/telephony/mbms/StreamingService.java
@@ -29,11 +29,11 @@
 
 /**
  * Class used to represent a single MBMS stream. After a stream has been started with
- * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
- * StreamingServiceCallback, android.os.Handler)},
+ * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo, java.util.concurrent.Executor,
+ * StreamingServiceCallback)},
  * this class is used to hold information about the stream and control it.
  */
-public class StreamingService {
+public class StreamingService implements AutoCloseable {
     private static final String LOG_TAG = "MbmsStreamingService";
 
     /**
@@ -41,7 +41,7 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({STATE_STOPPED, STATE_STARTED, STATE_STALLED})
+    @IntDef(prefix = { "STATE_" }, value = {STATE_STOPPED, STATE_STARTED, STATE_STALLED})
     public @interface StreamingState {}
     public final static int STATE_STOPPED = 1;
     public final static int STATE_STARTED = 2;
@@ -53,7 +53,8 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({REASON_BY_USER_REQUEST, REASON_END_OF_SESSION, REASON_FREQUENCY_CONFLICT,
+    @IntDef(prefix = { "REASON_" },
+            value = {REASON_BY_USER_REQUEST, REASON_END_OF_SESSION, REASON_FREQUENCY_CONFLICT,
             REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE,
             REASON_LEFT_MBMS_BROADCAST_AREA, REASON_NONE})
     public @interface StreamingStateChangeReason {}
@@ -64,9 +65,9 @@
     public static final int REASON_NONE = 0;
 
     /**
-     * State changed due to a call to {@link #stopStreaming()} or
+     * State changed due to a call to {@link #close()} or
      * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
-     * StreamingServiceCallback, android.os.Handler)}
+     * java.util.concurrent.Executor, StreamingServiceCallback)}
      */
     public static final int REASON_BY_USER_REQUEST = 1;
 
@@ -161,7 +162,8 @@
      *
      * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
      */
-    public void stopStreaming() {
+    @Override
+    public void close() {
         if (mService == null) {
             throw new IllegalStateException("No streaming service attached");
         }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 02cc82c..9e2b519 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -952,6 +952,11 @@
     int getCarrierPrivilegeStatus(int subId);
 
     /**
+     * Similar to above, but check for the given uid.
+     */
+    int getCarrierPrivilegeStatusForUid(int subId, int uid);
+
+    /**
      * Similar to above, but check for the package whose name is pkgName.
      */
     int checkCarrierPrivilegesForPackage(String pkgName);
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
new file mode 100644
index 0000000..da8471f
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -0,0 +1,209 @@
+/*
+ * 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.internal.telephony;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.ITelephony;
+
+/** Utility class for Telephony permission enforcement. */
+public final class TelephonyPermissions {
+    private static final String LOG_TAG = "TelephonyPermissions";
+
+    private static final boolean DBG = false;
+
+    private TelephonyPermissions() {}
+
+    /**
+     * Check whether the caller (or self, if not processing an IPC) can read phone state.
+     *
+     * <p>This method behaves in one of the following ways:
+     * <ul>
+     *   <li>return true: if the caller has either the READ_PRIVILEGED_PHONE_STATE permission or the
+     *       READ_PHONE_STATE runtime permission.
+     *   <li>throw SecurityException: if the caller didn't declare any of these permissions, or, for
+     *       apps which support runtime permissions, if the caller does not currently have any of
+     *       these permissions.
+     *   <li>return false: if the caller lacks all of these permissions and doesn't support runtime
+     *       permissions. This implies that the user revoked the ability to read phone state
+     *       manually (via AppOps). In this case we can't throw as it would break app compatibility,
+     *       so we return false to indicate that the calling function should return dummy data.
+     * </ul>
+     */
+    public static boolean checkCallingOrSelfReadPhoneState(
+            Context context, String callingPackage, String message) {
+        return checkReadPhoneState(context, Binder.getCallingPid(), Binder.getCallingUid(),
+                callingPackage, message);
+    }
+
+    /**
+     * Check whether the app with the given pid/uid can read phone state.
+     *
+     * <p>This method behaves in one of the following ways:
+     * <ul>
+     *   <li>return true: if the caller has either the READ_PRIVILEGED_PHONE_STATE permission or the
+     *       READ_PHONE_STATE runtime permission.
+     *   <li>throw SecurityException: if the caller didn't declare any of these permissions, or, for
+     *       apps which support runtime permissions, if the caller does not currently have any of
+     *       these permissions.
+     *   <li>return false: if the caller lacks all of these permissions and doesn't support runtime
+     *       permissions. This implies that the user revoked the ability to read phone state
+     *       manually (via AppOps). In this case we can't throw as it would break app compatibility,
+     *       so we return false to indicate that the calling function should return dummy data.
+     * </ul>
+     */
+    public static boolean checkReadPhoneState(
+            Context context, int pid, int uid, String callingPackage, String message) {
+        try {
+            context.enforcePermission(
+                    android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message);
+
+            // SKIP checking for run-time permission since caller has PRIVILEGED permission
+            return true;
+        } catch (SecurityException privilegedPhoneStateException) {
+            context.enforcePermission(
+                    android.Manifest.permission.READ_PHONE_STATE, pid, uid, message);
+        }
+
+        // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
+        // revoked.
+        AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+        return appOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, uid, callingPackage) ==
+                AppOpsManager.MODE_ALLOWED;
+    }
+
+    /**
+     * Returns whether the caller can read phone numbers.
+     *
+     * <p>Besides apps with the ability to read phone state per {@link #checkReadPhoneState}, the
+     * default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS can also read phone numbers.
+     */
+    public static boolean checkCallingOrSelfReadPhoneNumber(
+            Context context, String callingPackage, String message) {
+        return checkReadPhoneNumber(
+                context, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, message);
+    }
+
+    @VisibleForTesting
+    public static boolean checkReadPhoneNumber(
+            Context context, int pid, int uid, String callingPackage, String message) {
+        // Default SMS app can always read it.
+        AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+        if (appOps.noteOp(AppOpsManager.OP_WRITE_SMS, uid, callingPackage) ==
+                AppOpsManager.MODE_ALLOWED) {
+            return true;
+        }
+
+        // NOTE(b/73308711): If an app has one of the following AppOps bits explicitly revoked, they
+        // will be denied access, even if they have another permission and AppOps bit if needed.
+
+        // First, check if we can read the phone state.
+        try {
+            return checkReadPhoneState(context, pid, uid, callingPackage, message);
+        } catch (SecurityException readPhoneStateSecurityException) {
+        }
+        // Can be read with READ_SMS too.
+        try {
+            context.enforcePermission(android.Manifest.permission.READ_SMS, pid, uid, message);
+            int opCode = AppOpsManager.permissionToOpCode(android.Manifest.permission.READ_SMS);
+            if (opCode != AppOpsManager.OP_NONE) {
+                return appOps.noteOp(opCode, uid, callingPackage) == AppOpsManager.MODE_ALLOWED;
+            } else {
+                return true;
+            }
+        } catch (SecurityException readSmsSecurityException) {
+        }
+        // Can be read with READ_PHONE_NUMBERS too.
+        try {
+            context.enforcePermission(android.Manifest.permission.READ_PHONE_NUMBERS, pid, uid,
+                    message);
+            int opCode = AppOpsManager.permissionToOpCode(
+                    android.Manifest.permission.READ_PHONE_NUMBERS);
+            if (opCode != AppOpsManager.OP_NONE) {
+                return appOps.noteOp(opCode, uid, callingPackage) == AppOpsManager.MODE_ALLOWED;
+            } else {
+                return true;
+            }
+        } catch (SecurityException readPhoneNumberSecurityException) {
+        }
+
+        throw new SecurityException(message + ": Neither user " + uid +
+                " nor current process has " + android.Manifest.permission.READ_PHONE_STATE +
+                ", " + android.Manifest.permission.READ_SMS + ", or " +
+                android.Manifest.permission.READ_PHONE_NUMBERS);
+    }
+
+    /**
+     * Ensure the caller (or self, if not processing an IPC) has MODIFY_PHONE_STATE (and is thus a
+     * privileged app) or carrier privileges.
+     *
+     * @throws SecurityException if the caller does not have the required permission/privileges
+     */
+    public static void enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+            Context context, int subId, String message) {
+        if (context.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) ==
+                PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
+
+        if (DBG) Rlog.d(LOG_TAG, "No modify permission, check carrier privilege next.");
+        enforceCallingOrSelfCarrierPrivilege(subId, message);
+    }
+
+    /**
+     * Make sure the caller (or self, if not processing an IPC) has carrier privileges.
+     *
+     * @throws SecurityException if the caller does not have the required privileges
+     */
+    public static void enforceCallingOrSelfCarrierPrivilege(int subId, String message) {
+        // NOTE: It's critical that we explicitly pass the calling UID here rather than call
+        // TelephonyManager#hasCarrierPrivileges directly, as the latter only works when called from
+        // the phone process. When called from another process, it will check whether that process
+        // has carrier privileges instead.
+        enforceCarrierPrivilege(subId, Binder.getCallingUid(), message);
+    }
+
+    private static void enforceCarrierPrivilege(int subId, int uid, String message) {
+        if (getCarrierPrivilegeStatus(subId, uid) !=
+                TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+            if (DBG) Rlog.e(LOG_TAG, "No Carrier Privilege.");
+            throw new SecurityException(message);
+        }
+    }
+
+    private static int getCarrierPrivilegeStatus(int subId, int uid) {
+        ITelephony telephony =
+                ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
+        try {
+            if (telephony != null) {
+                return telephony.getCarrierPrivilegeStatusForUid(subId, uid);
+            }
+        } catch (RemoteException e) {
+            // Fallback below.
+        }
+        Rlog.e(LOG_TAG, "Phone process is down, cannot check carrier privileges");
+        return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+    }
+}
diff --git a/test-base/Android.bp b/test-base/Android.bp
index 4912224..4d149f7 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -31,12 +31,8 @@
     // Needs to be consistent with the repackaged version of this make target.
     java_version: "1.8",
 
-    no_framework_libs: true,
+    sdk_version: "current",
     hostdex: true,
-    libs: [
-        "framework",
-    ],
-
 }
 
 // Build the legacy-test library
@@ -46,12 +42,9 @@
 // Also contains the com.android.internal.util.Predicate[s] classes.
 java_library {
     name: "legacy-test",
-    static_libs: ["android.test.base"],
 
-    no_framework_libs: true,
-    libs: [
-        "framework",
-    ],
+    sdk_version: "current",
+    static_libs: ["android.test.base"],
 }
 
 // Build the repackaged.android.test.base library
@@ -61,11 +54,8 @@
 java_library_static {
     name: "repackaged.android.test.base",
 
+    sdk_version: "current",
     static_libs: ["android.test.base"],
-    no_framework_libs: true,
-    libs: [
-        "framework",
-    ],
 
     jarjar_rules: "jarjar-rules.txt",
     // Pin java_version until jarjar is certified to support later versions. http://b/72703434
diff --git a/test-base/Android.mk b/test-base/Android.mk
index 8613854..ebb33de 100644
--- a/test-base/Android.mk
+++ b/test-base/Android.mk
@@ -26,10 +26,7 @@
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := \
-    core-oj \
-    core-libart \
-    framework \
+LOCAL_SDK_VERSION := current
 
 LOCAL_MODULE_CLASS := JAVA_LIBRARIES
 LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src
diff --git a/test-legacy/Android.mk b/test-legacy/Android.mk
index b8c5326..da47de0 100644
--- a/test-legacy/Android.mk
+++ b/test-legacy/Android.mk
@@ -21,16 +21,38 @@
 
 # Build the android.test.legacy library
 # =====================================
+# Built against the SDK so that it can be statically included in APKs
+# without breaking link type checks.
+#
+# This builds directly from the source rather than simply statically
+# including the android.test.base-minus-junit and
+# android.test.runner-minus-junit libraries because the latter library
+# cannot itself be built against the SDK. That is because it uses on
+# an internal method (setTestContext) on the AndroidTestCase class.
+# That class is provided by both the android.test.base-minus-junit and
+# the current SDK and as the latter is first on the classpath its
+# version is used. Unfortunately, it does not provide the internal
+# method and so compilation fails.
+#
+# Building from source avoids that because the compiler will use the
+# source version of AndroidTestCase instead of the one from the current
+# SDK.
+#
+# The use of the internal method does not prevent this from being
+# statically included because the class that provides the method is
+# also included in this library.
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := android.test.legacy
 
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, ../test-base/src/android) \
+    $(call all-java-files-under, ../test-base/src/com) \
+    $(call all-java-files-under, ../test-runner/src/android) \
+
 LOCAL_SDK_VERSION := current
 
-LOCAL_JAVA_LIBRARIES := junit
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    android.test.base-minus-junit \
-    android.test.runner-minus-junit \
+LOCAL_JAVA_LIBRARIES := junit android.test.mock.stubs
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
diff --git a/test-mock/Android.mk b/test-mock/Android.mk
index 5c586c7..de1b6c7 100644
--- a/test-mock/Android.mk
+++ b/test-mock/Android.mk
@@ -21,7 +21,7 @@
 # otherwise hidden methods could be visible.
 android_test_mock_source_files := \
     $(call all-java-files-under, src/android/test/mock) \
-    $(call all-java-files-under, ../core/java) \
+    $(call all-java-files-under, ../core/java/android)
 
 # For unbundled build we'll use the prebuilt jar from prebuilts/sdk.
 ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
@@ -30,7 +30,6 @@
 # ==========================================================
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(android_test_mock_source_files)
-
 LOCAL_JAVA_LIBRARIES := core-oj core-libart framework conscrypt okhttp bouncycastle
 LOCAL_MODULE_CLASS := JAVA_LIBRARIES
 LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/src/android/test/mock
@@ -42,6 +41,7 @@
 ANDROID_TEST_MOCK_REMOVED_API_FILE := $(LOCAL_PATH)/api/android-test-mock-removed.txt
 
 LOCAL_DROIDDOC_OPTIONS:= \
+    -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 \
     -stubpackages android.test.mock \
     -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android.test.mock.stubs_intermediates/src \
     -nodocs \
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 1ddc52c..1af7c3a 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -1209,4 +1209,11 @@
         throw new UnsupportedOperationException();
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public String getSystemTextClassifierPackageName() {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index fb7c2a7..c69279b 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -27,9 +27,8 @@
       javacflags: ["-Xep:DepAnn:ERROR"],
     },
 
-    no_framework_libs: true,
+    sdk_version: "current",
     libs: [
-        "framework",
         "android.test.base",
         "android.test.mock",
     ],
@@ -37,16 +36,15 @@
 
 // Build the android.test.runner-minus-junit library
 // =================================================
-// This is only intended for inclusion in the android.test.legacy and
-// legacy-android-test static libraries and must not be used elsewhere.
+// This is only intended for inclusion in the legacy-android-test static
+// library and must not be used elsewhere.
 java_library {
     name: "android.test.runner-minus-junit",
 
     srcs: ["src/android/**/*.java"],
 
-    no_framework_libs: true,
+    sdk_version: "current",
     libs: [
-        "framework",
         "android.test.base",
         "android.test.mock",
         "junit",
@@ -58,6 +56,7 @@
 java_library_static {
     name: "repackaged.android.test.runner",
 
+    sdk_version: "current",
     static_libs: ["android.test.runner"],
 
     jarjar_rules: "jarjar-rules.txt",
diff --git a/tests/ActivityManagerPerfTests/README.txt b/tests/ActivityManagerPerfTests/README.txt
index 1040ed1..2686290 100644
--- a/tests/ActivityManagerPerfTests/README.txt
+++ b/tests/ActivityManagerPerfTests/README.txt
@@ -5,13 +5,7 @@
 * Self-contained perf tests should go in frameworks/base/apct-tests/perftests
 
 Command to run tests
-* atest .../frameworks/base/tests/ActivityManagerPerfTests/tests/
-  * Command currently not working: b/71859981
-* m ActivityManagerPerfTests ActivityManagerPerfTestsTestApp && \
-  adb install "$OUT"/data/app/ActivityManagerPerfTests/ActivityManagerPerfTests.apk && \
-  adb install "$OUT"/data/app/ActivityManagerPerfTestsTestApp/ActivityManagerPerfTestsTestApp.apk && \
-  adb shell am instrument -w \
-  com.android.frameworks.perftests.amtests/android.support.test.runner.AndroidJUnitRunner
+* atest -v ActivityManagerPerfTests
 
 Overview
 * The numbers we are trying to measure are end-to-end numbers
diff --git a/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java
index f32464b..38c298c 100644
--- a/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunchWear/src/com/android/tests/applaunch/AppLaunch.java
@@ -103,6 +103,7 @@
     private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh";
     private static final String APP_LAUNCH_CMD = "am start -W -n";
     private static final String SUCCESS_MESSAGE = "Status: ok";
+    private static final String WARNING_MESSAGE = "Warning: Activity not started";
     private static final String COMPILE_SUCCESS = "Success";
     private static final String THIS_TIME = "ThisTime:";
     private static final String LAUNCH_ITERATION = "LAUNCH_ITERATION - %d";
@@ -231,12 +232,14 @@
                 dropCache();
                 String appPkgName = mNameToIntent.get(launch.getApp())
                         .getComponent().getPackageName();
-
+                Log.v(TAG, String.format("\nApp name: %s", launch.getApp()));
+                Log.v(TAG, String.format("Adding app package name: %s", appPkgName));
                 // App launch times for trial launch will not be used for final
                 // launch time calculations.
                 if (launch.getLaunchReason().equals(TRIAL_LAUNCH)) {
                     // In the "applaunch.txt" file, trail launches is referenced using
                     // "TRIAL_LAUNCH"
+                    Log.v(TAG, "Trial Launch");
                     if (SPEED_PROFILE_FILTER.equals(launch.getCompilerFilter())) {
                         assertTrue(String.format("Not able to compile the app : %s", appPkgName),
                               compileApp(VERIFY_FILTER, appPkgName));
@@ -246,8 +249,14 @@
                     }
                     // We only need to run a trial for the speed-profile filter, but we always
                     // run one for "applaunch.txt" consistency.
-                    AppLaunchResult launchResult =
-                        startApp(launch.getApp(), true, launch.getLaunchReason());
+                    AppLaunchResult launchResult = null;
+                    if (appPkgName.contains(WEARABLE_HOME_PACKAGE)) {
+                        Log.v(TAG, "Home package detected. Not killing app");
+                        launchResult = startApp(launch.getApp(), false, launch.getLaunchReason());
+                    } else {
+                        Log.v(TAG, "Will kill app before launch");
+                        launchResult = startApp(launch.getApp(), true, launch.getLaunchReason());
+                    }
                     if (launchResult.mLaunchTime < 0) {
                         addLaunchResult(launch, new AppLaunchResult());
                         // simply pass the app if launch isn't successful
@@ -268,6 +277,7 @@
 
                 // App launch times used for final calculation
                 else if (launch.getLaunchReason().contains(LAUNCH_ITERATION_PREFIX)) {
+                    Log.v(TAG, "Launch iteration prefix.");
                     AppLaunchResult launchResults = null;
                     if (hasFailureOnFirstLaunch(launch)) {
                         // skip if the app has failures while launched first
@@ -276,8 +286,10 @@
                     // In the "applaunch.txt" file app launches are referenced using
                     // "LAUNCH_ITERATION - ITERATION NUM"
                     if (appPkgName.contains(WEARABLE_HOME_PACKAGE)) {
+                        Log.v(TAG, "Home package detected. Not killing app");
                         launchResults = startApp(launch.getApp(), false, launch.getLaunchReason());
                     } else {
+                        Log.v(TAG, "Will kill app before launch");
                         launchResults = startApp(launch.getApp(), true, launch.getLaunchReason());
                     }
                     if (launchResults.mLaunchTime < 0) {
@@ -293,6 +305,7 @@
                 // App launch times for trace launch will not be used for final
                 // launch time calculations.
                 else if (launch.getLaunchReason().contains(TRACE_ITERATION_PREFIX)) {
+                    Log.v(TAG, "Trace iteration prefix");
                     AtraceLogger atraceLogger = AtraceLogger
                             .getAtraceLoggerInstance(getInstrumentation());
                     // Start the trace
@@ -300,7 +313,13 @@
                         atraceLogger.atraceStart(traceCategoriesSet, traceBufferSize,
                                 traceDumpInterval, rootTraceSubDir,
                                 String.format("%s-%s", launch.getApp(), launch.getLaunchReason()));
-                        startApp(launch.getApp(), true, launch.getLaunchReason());
+                        if (appPkgName.contains(WEARABLE_HOME_PACKAGE)) {
+                            Log.v(TAG, "Home package detected. Not killing app");
+                            startApp(launch.getApp(), false, launch.getLaunchReason());
+                        } else {
+                            Log.v(TAG, "Will kill app before launch");
+                            startApp(launch.getApp(), true, launch.getLaunchReason());
+                        }
                         sleep(POST_LAUNCH_IDLE_TIMEOUT);
                     } finally {
                         // Stop the trace
@@ -707,7 +726,12 @@
                 String packageName = mLaunchIntent.getComponent().getPackageName();
                 String componentName = mLaunchIntent.getComponent().flattenToShortString();
                 if (mForceStopBeforeLaunch) {
+                    Log.v(TAG, "Stopping app before launch");
                     mAm.forceStopPackage(packageName, UserHandle.USER_CURRENT);
+                } else {
+                    Log.v(TAG, "Not killing app. Going to Home Screen.");
+                    ParcelFileDescriptor goHome = getInstrumentation().getUiAutomation()
+                        .executeShellCommand("input keyevent 3");
                 }
                 String launchCmd = String.format("%s %s", APP_LAUNCH_CMD, componentName);
                 if (mSimplePerfAppOnly) {
@@ -767,6 +791,17 @@
                 TotalTime: 357
                 WaitTime: 377
                 Complete*/
+                /* WHEN NOT KILLING HOME :
+                Starting: Intent { cmp=com.google.android.wearable.app/
+                    com.google.android.clockwork.home.calendar.AgendaActivity }
+                Warning: Activity not started, its current task has been brought to the front
+                Status: ok
+                Activity: com.google.android.wearable.app/
+                    com.google.android.clockwork.home.calendar.AgendaActivity
+                ThisTime: 209
+                TotalTime: 209
+                WaitTime: 285
+                Complete*/
                 /* WITH SIMPLEPERF :
                 Performance counter statistics,
                 6595722690,cpu-cycles,4.511040,GHz,(100%),
@@ -776,28 +811,32 @@
                         inputStream));
                 String line = null;
                 int lineCount = 1;
+                int addLineForWarning = 0;
                 mBufferedWriter.newLine();
                 mBufferedWriter.write(headerInfo);
                 mBufferedWriter.newLine();
                 while ((line = bufferedReader.readLine()) != null) {
-                    if (lineCount == 2 && line.contains(SUCCESS_MESSAGE)) {
+                    if (lineCount == 2 && line.contains(WARNING_MESSAGE)) {
+                        addLineForWarning = 1;
+                    }
+                    if (lineCount == (2 + addLineForWarning) && line.contains(SUCCESS_MESSAGE)) {
                         launchSuccess = true;
                     }
                     // Parse TotalTime which is the launch time
-                    if (launchSuccess && lineCount == 5) {
+                    if (launchSuccess && lineCount == (5 + addLineForWarning)) {
                         String launchSplit[] = line.split(":");
                         launchTime = launchSplit[1].trim();
                     }
 
                     if (mSimplePerfAppOnly) {
                         // Parse simpleperf output.
-                        if (lineCount == 9) {
+                        if (lineCount == (9 + addLineForWarning)) {
                             if (!line.contains("cpu-cycles")) {
                                 Log.e(TAG, "Error in simpleperf output");
                             } else {
                                 cpuCycles = line.split(",")[0].trim();
                             }
-                        } else if (lineCount == 10) {
+                        } else if (lineCount == (10 + addLineForWarning)) {
                             if (!line.contains("major-faults")) {
                                 Log.e(TAG, "Error in simpleperf output");
                             } else {
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index dc2ed43..948422c 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -62,10 +62,14 @@
 
     shared_libs: [
         "libstats_proto_host",
+        "libprotobuf-cpp-full",
     ],
 
     proto: {
         type: "full",
+        include_dirs: [
+            "external/protobuf/src",
+        ],
     },
 }
 
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index 0e57f7f..ab106d7 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -41,12 +41,12 @@
 }
 
 AtomDecl::AtomDecl(const AtomDecl& that)
-    :code(that.code),
-     name(that.name),
-     message(that.message),
-     fields(that.fields)
-{
-}
+    : code(that.code),
+      name(that.name),
+      message(that.message),
+      fields(that.fields),
+      primaryFields(that.primaryFields),
+      exclusiveField(that.exclusiveField) {}
 
 AtomDecl::AtomDecl(int c, const string& n, const string& m)
     :code(c),
@@ -237,6 +237,31 @@
       signature->push_back(javaType);
     }
     atomDecl->fields.push_back(atField);
+
+    if (field->options().GetExtension(os::statsd::stateFieldOption).option() ==
+        os::statsd::StateField::PRIMARY) {
+        if (javaType == JAVA_TYPE_UNKNOWN ||
+            javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
+            javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
+            errorCount++;
+        }
+        atomDecl->primaryFields.push_back(it->first);
+    }
+
+    if (field->options().GetExtension(os::statsd::stateFieldOption).option() ==
+        os::statsd::StateField::EXCLUSIVE) {
+        if (javaType == JAVA_TYPE_UNKNOWN ||
+            javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
+            javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
+            errorCount++;
+        }
+
+        if (atomDecl->exclusiveField == 0) {
+            atomDecl->exclusiveField = it->first;
+        } else {
+            errorCount++;
+        }
+    }
   }
 
   return errorCount;
@@ -318,6 +343,9 @@
     AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
     vector<java_type_t> signature;
     errorCount += collate_atom(atom, &atomDecl, &signature);
+    if (atomDecl.primaryFields.size() != 0 && atomDecl.exclusiveField == 0) {
+        errorCount++;
+    }
     atoms->signatures.insert(signature);
     atoms->decls.insert(atomDecl);
 
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index 0455eca..edba3e2 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -81,6 +81,9 @@
     string message;
     vector<AtomField> fields;
 
+    vector<int> primaryFields;
+    int exclusiveField = 0;
+
     AtomDecl();
     AtomDecl(const AtomDecl& that);
     AtomDecl(int code, const string& name, const string& message);
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index da67e92..d58c223 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -320,6 +320,7 @@
     fprintf(out, "\n");
     fprintf(out, "#include <stdint.h>\n");
     fprintf(out, "#include <vector>\n");
+    fprintf(out, "#include <map>\n");
     fprintf(out, "#include <set>\n");
     fprintf(out, "\n");
 
@@ -412,6 +413,43 @@
 
     fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", maxPushedAtomId);
 
+    fprintf(out, "struct StateAtomFieldOptions {\n");
+    fprintf(out, "  std::vector<int> primaryFields;\n");
+    fprintf(out, "  int exclusiveField;\n");
+    fprintf(out, "\n");
+    fprintf(out,
+            "  static std::map<int, StateAtomFieldOptions> "
+            "getStateAtomFieldOptions() {\n");
+    fprintf(out, "    std::map<int, StateAtomFieldOptions> options;\n");
+    fprintf(out, "    StateAtomFieldOptions opt;\n");
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+         atom != atoms.decls.end(); atom++) {
+        if (atom->primaryFields.size() == 0 && atom->exclusiveField == 0) {
+            continue;
+        }
+        fprintf(out,
+                "\n    // Adding primary and exclusive fields for atom "
+                "(%d)%s\n",
+                atom->code, atom->name.c_str());
+        fprintf(out, "    opt.primaryFields.clear();\n");
+        for (const auto& field : atom->primaryFields) {
+            fprintf(out, "    opt.primaryFields.push_back(%d);\n", field);
+        }
+
+        fprintf(out, "    opt.exclusiveField = %d;\n", atom->exclusiveField);
+        fprintf(out, "    options[static_cast<int>(%s)] = opt;\n",
+                make_constant_name(atom->name).c_str());
+    }
+
+    fprintf(out, "    return options;\n");
+    fprintf(out, "  }\n");
+    fprintf(out, "};\n");
+
+    fprintf(out,
+            "const static std::map<int, StateAtomFieldOptions> "
+            "kStateAtomsFieldOptions = "
+            "StateAtomFieldOptions::getStateAtomFieldOptions();\n");
+
     // Print write methods
     fprintf(out, "//\n");
     fprintf(out, "// Write methods\n");
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index 66cbee8..264a865 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -17,6 +17,7 @@
 syntax = "proto2";
 
 import "frameworks/base/cmds/statsd/src/atoms.proto";
+import "frameworks/base/cmds/statsd/src/atom_field_options.proto";
 
 package android.stats_log_api_gen;
 
@@ -108,3 +109,68 @@
   oneof event { BadAttributionNodePositionAtom bad = 1; }
 }
 
+message BadStateAtoms {
+    oneof event {
+        BadStateAtom1 bad1 = 1;
+        BadStateAtom2 bad2 = 2;
+        BadStateAtom3 bad3 = 3;
+    }
+}
+
+message GoodStateAtoms {
+    oneof event {
+        GoodStateAtom1 good1 = 1;
+        GoodStateAtom2 good2 = 2;
+    }
+}
+
+// The atom has only primary field but no exclusive state field.
+message BadStateAtom1 {
+    optional int32 uid = 1
+            [(android.os.statsd.stateFieldOption).option = PRIMARY];
+}
+
+// Only primative types can be annotated.
+message BadStateAtom2 {
+    repeated android.os.statsd.AttributionNode attribution = 1
+            [(android.os.statsd.stateFieldOption).option = PRIMARY];
+    optional int32 state = 2
+            [(android.os.statsd.stateFieldOption).option = EXCLUSIVE];
+}
+
+// Having 2 exclusive state field in the atom means the atom is badly designed.
+// E.g., putting bluetooth state and wifi state in the same atom.
+message BadStateAtom3 {
+    optional int32 uid = 1
+            [(android.os.statsd.stateFieldOption).option = PRIMARY];
+    optional int32 state = 2
+            [(android.os.statsd.stateFieldOption).option = EXCLUSIVE];
+    optional int32 state2 = 3
+            [(android.os.statsd.stateFieldOption).option = EXCLUSIVE];
+}
+
+message GoodStateAtom1 {
+    optional int32 uid = 1
+            [(android.os.statsd.stateFieldOption).option = PRIMARY];
+    optional int32 state = 2
+            [(android.os.statsd.stateFieldOption).option = EXCLUSIVE];
+}
+
+// Atoms can have exclusive state field, but no primary field. That means
+// the state is globally exclusive (e.g., DisplayState).
+message GoodStateAtom2 {
+    optional int32 uid = 1;
+    optional int32 state = 2
+            [(android.os.statsd.stateFieldOption).option = EXCLUSIVE];
+}
+
+// We can have more than one primary fields. That means their combination is a
+// primary key.
+message GoodStateAtom3 {
+    optional int32 uid = 1
+            [(android.os.statsd.stateFieldOption).option = PRIMARY];
+    optional int32 tid = 2
+            [(android.os.statsd.stateFieldOption).option = PRIMARY];
+    optional int32 state = 3
+            [(android.os.statsd.stateFieldOption).option = EXCLUSIVE];
+}
\ No newline at end of file
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index 9e22cd9..1936d96 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -199,5 +199,18 @@
   EXPECT_EQ(1, errorCount);
 }
 
+TEST(CollationTest, FailOnBadStateAtomOptions) {
+    Atoms atoms;
+    int errorCount = collate_atoms(BadStateAtoms::descriptor(), &atoms);
+
+    EXPECT_EQ(3, errorCount);
+}
+
+TEST(CollationTest, PassOnGoodStateAtomOptions) {
+    Atoms atoms;
+    int errorCount = collate_atoms(GoodStateAtoms::descriptor(), &atoms);
+    EXPECT_EQ(0, errorCount);
+}
+
 }  // namespace stats_log_api_gen
 }  // namespace android
\ No newline at end of file