Merge "Move list of SystemUI services to xml"
diff --git a/Android.bp b/Android.bp
index 81cd65c..70b1fa0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -98,6 +98,7 @@
         "core/java/android/app/backup/IRestoreObserver.aidl",
         "core/java/android/app/backup/IRestoreSession.aidl",
         "core/java/android/app/backup/ISelectBackupTransportCallback.aidl",
+        "core/java/android/app/slice/ISliceManager.aidl",
         "core/java/android/app/timezone/ICallback.aidl",
         "core/java/android/app/timezone/IRulesManager.aidl",
         "core/java/android/app/usage/ICacheQuotaService.aidl",
@@ -134,6 +135,8 @@
         "core/java/android/content/pm/IPackageStatsObserver.aidl",
         "core/java/android/content/pm/IPinItemRequest.aidl",
         "core/java/android/content/pm/IShortcutService.aidl",
+        "core/java/android/content/pm/dex/IArtManager.aidl",
+        "core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl",
         "core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl",
         "core/java/android/database/IContentObserver.aidl",
         ":libcamera_client_aidl",
diff --git a/Android.mk b/Android.mk
index ce504a5..10d11f3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -46,6 +46,7 @@
 	frameworks/base/telephony/java/android/telephony/NeighboringCellInfo.aidl \
 	frameworks/base/telephony/java/android/telephony/ModemActivityInfo.aidl \
 	frameworks/base/telephony/java/android/telephony/UiccAccessRule.aidl \
+	frameworks/base/telephony/java/android/telephony/data/DataCallResponse.aidl \
 	frameworks/base/telephony/java/android/telephony/data/DataProfile.aidl \
 	frameworks/base/telephony/java/android/telephony/euicc/DownloadableSubscription.aidl \
 	frameworks/base/telephony/java/android/telephony/euicc/EuiccInfo.aidl \
@@ -95,6 +96,7 @@
 	frameworks/base/core/java/android/app/admin/NetworkEvent.aidl \
 	frameworks/base/core/java/android/app/admin/SystemUpdatePolicy.aidl \
 	frameworks/base/core/java/android/app/admin/PasswordMetrics.aidl \
+	frameworks/base/core/java/android/app/slice/ISliceManager.aidl \
 	frameworks/base/core/java/android/print/PrintDocumentInfo.aidl \
 	frameworks/base/core/java/android/print/PageRange.aidl \
 	frameworks/base/core/java/android/print/PrintAttributes.aidl \
@@ -654,6 +656,10 @@
 # Check comment when you are updating the API
 update-api: doc-comment-check-docs
 
+# Generate API diffs as part of docs builds
+docs: offline-sdk-referenceonly-diff
+docs: offline-system-sdk-referenceonly-diff
+
 # ====  static html in the sdk ==================================
 include $(CLEAR_VARS)
 
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index 57a61ec..92ee7cc 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -22,6 +22,11 @@
 import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
 
+import android.content.res.ColorStateList;
+import android.graphics.Typeface;
+import android.text.Layout;
+import android.text.style.TextAppearanceSpan;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,40 +38,35 @@
 @RunWith(AndroidJUnit4.class)
 public class StaticLayoutPerfTest {
 
-    public StaticLayoutPerfTest() {
-    }
+    public StaticLayoutPerfTest() {}
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    private static final String FIXED_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing "
-            + "elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad "
-            + "minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
-            + "commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse "
-            + "cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non "
-            + "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
-    private static final int FIXED_TEXT_LENGTH = FIXED_TEXT.length();
+    private static final int WORD_LENGTH = 9;  // Random word has 9 characters.
+    private static final int WORDS_IN_LINE = 8;  // Roughly, 8 words in a line.
+    private static final int PARA_LENGTH = 500;  // Number of characters in a paragraph.
 
-    private static TextPaint PAINT = new TextPaint();
-    private static final int TEXT_WIDTH = 20 * (int) PAINT.getTextSize();
+    private static final boolean NO_STYLE_TEXT = false;
+    private static final boolean STYLE_TEXT = true;
 
-    @Test
-    public void testCreate() {
-        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        while (state.keepRunning()) {
-            StaticLayout.Builder.obtain(FIXED_TEXT, 0, FIXED_TEXT_LENGTH, PAINT, TEXT_WIDTH)
-                    .build();
-        }
-    }
+    private final Random mRandom = new Random(31415926535L);
 
     private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
     private static final int ALPHABET_LENGTH = ALPHABET.length();
 
-    private static final int PARA_LENGTH = 500;
-    private final char[] mBuffer = new char[PARA_LENGTH];
-    private final Random mRandom = new Random(31415926535L);
+    private static final ColorStateList TEXT_COLOR = ColorStateList.valueOf(0x00000000);
+    private static final String[] FAMILIES = { "sans-serif", "serif", "monospace" };
+    private static final int[] STYLES = {
+            Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC
+    };
 
-    private CharSequence generateRandomParagraph(int wordLen) {
+    private final char[] mBuffer = new char[PARA_LENGTH];
+
+    private static TextPaint PAINT = new TextPaint();
+    private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
+
+    private CharSequence generateRandomParagraph(int wordLen, boolean applyRandomStyle) {
         for (int i = 0; i < PARA_LENGTH; i++) {
             if (i % (wordLen + 1) == wordLen) {
                 mBuffer[i] = ' ';
@@ -74,29 +74,112 @@
                 mBuffer[i] = ALPHABET.charAt(mRandom.nextInt(ALPHABET_LENGTH));
             }
         }
-        return CharBuffer.wrap(mBuffer);
+
+        CharSequence cs = CharBuffer.wrap(mBuffer);
+        if (!applyRandomStyle) {
+            return cs;
+        }
+
+        SpannableStringBuilder ssb = new SpannableStringBuilder(cs);
+        for (int i = 0; i < ssb.length(); i += WORD_LENGTH) {
+            final int spanStart = i;
+            final int spanEnd = (i + WORD_LENGTH) > ssb.length() ? ssb.length() : i + WORD_LENGTH;
+
+            final TextAppearanceSpan span = new TextAppearanceSpan(
+                  FAMILIES[mRandom.nextInt(FAMILIES.length)],
+                  STYLES[mRandom.nextInt(STYLES.length)],
+                  24 + mRandom.nextInt(32),  // text size. min 24 max 56
+                  TEXT_COLOR, TEXT_COLOR);
+
+            ssb.setSpan(span, spanStart, spanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        }
+        return ssb;
     }
 
-    // This tries to simulate the case where the cache hit rate is low, and most of the text is
-    // new text.
     @Test
-    public void testCreateRandom() {
+    public void testCreate_FixedText_NoStyle_Greedy_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
         while (state.keepRunning()) {
-            final CharSequence text = generateRandomParagraph(9);
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                     .build();
         }
     }
 
     @Test
-    public void testCreateRandom_breakBalanced() {
+    public void testCreate_RandomText_NoStyled_Greedy_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
-            final CharSequence text = generateRandomParagraph(9);
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .build();
+        }
+    }
+
+    @Test
+    public void testCreate_RandomText_NoStyled_Greedy_Hyphenation() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .build();
+        }
+    }
+
+    @Test
+    public void testCreate_RandomText_NoStyled_Balanced_NoHyphenation() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
                     .build();
         }
     }
+
+    @Test
+    public void testCreate_RandomText_NoStyled_Balanced_Hyphenation() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                    .build();
+        }
+    }
+
+    @Test
+    public void testCreate_RandomText_Styled_Greedy_NoHyphenation() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            state.resumeTiming();
+
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .build();
+        }
+    }
 }
diff --git a/api/current.txt b/api/current.txt
index 67ac75f..f56f1a1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1470,6 +1470,8 @@
     field public static final int vendor = 16843751; // 0x10103e7
     field public static final int version = 16844057; // 0x1010519
     field public static final int versionCode = 16843291; // 0x101021b
+    field public static final int versionCodeMajor = 16844150; // 0x1010576
+    field public static final int versionMajor = 16844151; // 0x1010577
     field public static final int versionName = 16843292; // 0x101021c
     field public static final int verticalCorrection = 16843322; // 0x101023a
     field public static final int verticalDivider = 16843054; // 0x101012e
@@ -1503,6 +1505,7 @@
     field public static final deprecated int weekSeparatorLineColor = 16843590; // 0x1010346
     field public static final int weightSum = 16843048; // 0x1010128
     field public static final int widgetCategory = 16843716; // 0x10103c4
+    field public static final int widgetFeatures = 16844153; // 0x1010579
     field public static final int widgetLayout = 16843243; // 0x10101eb
     field public static final int width = 16843097; // 0x1010159
     field public static final int windowActionBar = 16843469; // 0x10102cd
@@ -4992,7 +4995,7 @@
   public class KeyguardManager {
     method public android.content.Intent createConfirmDeviceCredentialIntent(java.lang.CharSequence, java.lang.CharSequence);
     method public deprecated void exitKeyguardSecurely(android.app.KeyguardManager.OnKeyguardExitResult);
-    method public boolean inKeyguardRestrictedInputMode();
+    method public deprecated boolean inKeyguardRestrictedInputMode();
     method public boolean isDeviceLocked();
     method public boolean isDeviceSecure();
     method public boolean isKeyguardLocked();
@@ -6320,6 +6323,7 @@
     method public android.os.UserHandle createAndManageUser(android.content.ComponentName, java.lang.String, android.content.ComponentName, android.os.PersistableBundle, int);
     method public void enableSystemApp(android.content.ComponentName, java.lang.String);
     method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
+    method public android.security.AttestedKeyPair generateKeyPair(android.content.ComponentName, java.lang.String, android.security.keystore.KeyGenParameterSpec);
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
     method public java.util.Set<java.lang.String> getAffiliationIds(android.content.ComponentName);
@@ -6368,6 +6372,7 @@
     method public java.util.List<java.lang.String> getPermittedInputMethods(android.content.ComponentName);
     method public long getRequiredStrongAuthTimeout(android.content.ComponentName);
     method public boolean getScreenCaptureDisabled(android.content.ComponentName);
+    method public java.util.List<android.os.UserHandle> getSecondaryUsers(android.content.ComponentName);
     method public java.lang.CharSequence getShortSupportMessage(android.content.ComponentName);
     method public boolean getStorageEncryption(android.content.ComponentName);
     method public int getStorageEncryptionStatus();
@@ -6388,6 +6393,7 @@
     method public deprecated boolean isCallerApplicationRestrictionsManagingPackage();
     method public boolean isDeviceOwnerApp(java.lang.String);
     method public boolean isLockTaskPermitted(java.lang.String);
+    method public boolean isLogoutButtonEnabled();
     method public boolean isManagedProfile(android.content.ComponentName);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
     method public boolean isNetworkLoggingEnabled(android.content.ComponentName);
@@ -6399,6 +6405,7 @@
     method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String);
     method public void lockNow();
     method public void lockNow(int);
+    method public boolean logoutUser(android.content.ComponentName);
     method public void reboot(android.content.ComponentName);
     method public void removeActiveAdmin(android.content.ComponentName);
     method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
@@ -6430,6 +6437,7 @@
     method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
     method public void setLockTaskFeatures(android.content.ComponentName, int);
     method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
+    method public void setLogoutButtonEnabled(android.content.ComponentName, boolean);
     method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence);
     method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
     method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
@@ -6472,6 +6480,7 @@
     method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
     method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
     method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
+    method public boolean stopUser(android.content.ComponentName, android.os.UserHandle);
     method public boolean switchUser(android.content.ComponentName, android.os.UserHandle);
     method public void uninstallAllUserCaCerts(android.content.ComponentName);
     method public void uninstallCaCert(android.content.ComponentName, byte[]);
@@ -6767,6 +6776,7 @@
     method public void onFullBackup(android.app.backup.FullBackupDataOutput) throws java.io.IOException;
     method public void onQuotaExceeded(long, long);
     method public abstract void onRestore(android.app.backup.BackupDataInput, int, android.os.ParcelFileDescriptor) throws java.io.IOException;
+    method public void onRestore(android.app.backup.BackupDataInput, long, android.os.ParcelFileDescriptor) throws java.io.IOException;
     method public void onRestoreFile(android.os.ParcelFileDescriptor, long, java.io.File, int, long, long) throws java.io.IOException;
     method public void onRestoreFinished();
     field public static final int TYPE_DIRECTORY = 2; // 0x2
@@ -7343,6 +7353,8 @@
     field public static final int WIDGET_CATEGORY_HOME_SCREEN = 1; // 0x1
     field public static final int WIDGET_CATEGORY_KEYGUARD = 2; // 0x2
     field public static final int WIDGET_CATEGORY_SEARCHBOX = 4; // 0x4
+    field public static final int WIDGET_FEATURE_HIDE_FROM_PICKER = 2; // 0x2
+    field public static final int WIDGET_FEATURE_RECONFIGURABLE = 1; // 0x1
     field public int autoAdvanceViewId;
     field public android.content.ComponentName configure;
     field public int icon;
@@ -7358,6 +7370,7 @@
     field public int resizeMode;
     field public int updatePeriodMillis;
     field public int widgetCategory;
+    field public int widgetFeatures;
   }
 
 }
@@ -9961,6 +9974,7 @@
   }
 
   public class QuickViewConstants {
+    field public static final java.lang.String FEATURE_DELETE = "android:delete";
     field public static final java.lang.String FEATURE_DOWNLOAD = "android:download";
     field public static final java.lang.String FEATURE_EDIT = "android:edit";
     field public static final java.lang.String FEATURE_PRINT = "android:print";
@@ -10072,6 +10086,7 @@
 
   public abstract interface ServiceConnection {
     method public default void onBindingDied(android.content.ComponentName);
+    method public default void onNullBinding(android.content.ComponentName);
     method public abstract void onServiceConnected(android.content.ComponentName, android.os.IBinder);
     method public abstract void onServiceDisconnected(android.content.ComponentName);
   }
@@ -10590,6 +10605,8 @@
   public class PackageInfo implements android.os.Parcelable {
     ctor public PackageInfo();
     method public int describeContents();
+    method public long getLongVersionCode();
+    method public void setLongVersionCode(long);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.PackageInfo> CREATOR;
     field public static final int INSTALL_LOCATION_AUTO = 0; // 0x0
@@ -10619,7 +10636,7 @@
     field public android.content.pm.Signature[] signatures;
     field public java.lang.String[] splitNames;
     field public int[] splitRevisionCodes;
-    field public int versionCode;
+    field public deprecated int versionCode;
     field public java.lang.String versionName;
   }
 
@@ -11134,9 +11151,10 @@
     method public int describeContents();
     method public android.content.pm.VersionedPackage getDeclaringPackage();
     method public java.util.List<android.content.pm.VersionedPackage> getDependentPackages();
+    method public long getLongVersion();
     method public java.lang.String getName();
     method public int getType();
-    method public int getVersion();
+    method public deprecated int getVersion();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR;
     field public static final int TYPE_BUILTIN = 0; // 0x0
@@ -11230,9 +11248,11 @@
 
   public final class VersionedPackage implements android.os.Parcelable {
     ctor public VersionedPackage(java.lang.String, int);
+    ctor public VersionedPackage(java.lang.String, long);
     method public int describeContents();
+    method public long getLongVersionCode();
     method public java.lang.String getPackageName();
-    method public int getVersionCode();
+    method public deprecated int getVersionCode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.VersionedPackage> CREATOR;
   }
@@ -11242,6 +11262,8 @@
 package android.content.pm.crossprofile {
 
   public class CrossProfileApps {
+    method public android.graphics.drawable.Drawable getProfileSwitchingIcon(android.os.UserHandle);
+    method public java.lang.CharSequence getProfileSwitchingLabel(android.os.UserHandle);
     method public java.util.List<android.os.UserHandle> getTargetUserProfiles();
     method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
   }
@@ -15494,6 +15516,8 @@
     field public static final int CONTROL_AF_MODE_EDOF = 5; // 0x5
     field public static final int CONTROL_AF_MODE_MACRO = 2; // 0x2
     field public static final int CONTROL_AF_MODE_OFF = 0; // 0x0
+    field public static final int CONTROL_AF_SCENE_CHANGE_DETECTED = 1; // 0x1
+    field public static final int CONTROL_AF_SCENE_CHANGE_NOT_DETECTED = 0; // 0x0
     field public static final int CONTROL_AF_STATE_ACTIVE_SCAN = 3; // 0x3
     field public static final int CONTROL_AF_STATE_FOCUSED_LOCKED = 4; // 0x4
     field public static final int CONTROL_AF_STATE_INACTIVE = 0; // 0x0
@@ -15768,6 +15792,7 @@
     field public static final android.hardware.camera2.CaptureResult.Key<android.util.Range<java.lang.Integer>> CONTROL_AE_TARGET_FPS_RANGE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AF_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.MeteringRectangle[]> CONTROL_AF_REGIONS;
+    field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AF_SCENE_CHANGE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AF_STATE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_AF_TRIGGER;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> CONTROL_AWB_LOCK;
@@ -25975,6 +26000,23 @@
     enum_constant public static final android.net.LocalSocketAddress.Namespace RESERVED;
   }
 
+  public final class MacAddress implements android.os.Parcelable {
+    method public int addressType();
+    method public int describeContents();
+    method public static android.net.MacAddress fromBytes(byte[]);
+    method public static android.net.MacAddress fromString(java.lang.String);
+    method public boolean isLocallyAssigned();
+    method public byte[] toByteArray();
+    method public java.lang.String toSafeString();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.net.MacAddress BROADCAST_ADDRESS;
+    field public static final android.os.Parcelable.Creator<android.net.MacAddress> CREATOR;
+    field public static final int TYPE_BROADCAST = 3; // 0x3
+    field public static final int TYPE_MULTICAST = 2; // 0x2
+    field public static final int TYPE_UNICAST = 1; // 0x1
+    field public static final int TYPE_UNKNOWN = 0; // 0x0
+  }
+
   public class MailTo {
     method public java.lang.String getBody();
     method public java.lang.String getCc();
@@ -37109,6 +37151,11 @@
 
 package android.security {
 
+  public final class AttestedKeyPair {
+    method public java.util.List<java.security.cert.Certificate> getAttestationRecord();
+    method public java.security.KeyPair getKeyPair();
+  }
+
   public final class KeyChain {
     ctor public KeyChain();
     method public static void choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, java.lang.String[], java.security.Principal[], java.lang.String, int, java.lang.String);
@@ -39468,6 +39515,7 @@
     method public final int getState();
     method public final android.telecom.StatusHints getStatusHints();
     method public final android.telecom.Connection.VideoProvider getVideoProvider();
+    method public void handleRttUpgradeResponse(android.telecom.Connection.RttTextStream);
     method public final boolean isRingbackRequested();
     method public void onAbort();
     method public void onAnswer(int);
@@ -39484,8 +39532,10 @@
     method public void onReject(java.lang.String);
     method public void onSeparate();
     method public void onShowIncomingCallUi();
+    method public void onStartRtt(android.telecom.Connection.RttTextStream);
     method public void onStateChanged(int);
     method public void onStopDtmfTone();
+    method public void onStopRtt();
     method public void onUnhold();
     method public static java.lang.String propertiesToString(int);
     method public final void putExtras(android.os.Bundle);
@@ -39493,6 +39543,10 @@
     method public final void removeExtras(java.lang.String...);
     method public void requestBluetoothAudio(java.lang.String);
     method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
+    method public final void sendRemoteRttRequest();
+    method public final void sendRttInitiationFailure(int);
+    method public final void sendRttInitiationSuccess();
+    method public final void sendRttSessionRemotelyTerminated();
     method public final void setActive();
     method public final void setAddress(android.net.Uri, int);
     method public final void setAudioModeIsVoip(boolean);
@@ -39546,6 +39600,7 @@
     field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
+    field public static final int PROPERTY_IS_RTT = 256; // 0x100
     field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_DIALING = 3; // 0x3
@@ -39565,6 +39620,12 @@
     field public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4; // 0x4
   }
 
+  public static final class Connection.RttTextStream {
+    method public java.lang.String read() throws java.io.IOException;
+    method public java.lang.String readImmediately() throws java.io.IOException;
+    method public void write(java.lang.String) throws java.io.IOException;
+  }
+
   public static abstract class Connection.VideoProvider {
     ctor public Connection.VideoProvider();
     method public void changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities);
@@ -39605,7 +39666,9 @@
     method public android.telecom.PhoneAccountHandle getAccountHandle();
     method public android.net.Uri getAddress();
     method public android.os.Bundle getExtras();
+    method public android.telecom.Connection.RttTextStream getRttTextStream();
     method public int getVideoState();
+    method public boolean isRequestingRtt();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telecom.ConnectionRequest> CREATOR;
   }
@@ -39615,12 +39678,15 @@
     method public final void addConference(android.telecom.Conference);
     method public final void addExistingConnection(android.telecom.PhoneAccountHandle, android.telecom.Connection);
     method public final void conferenceRemoteConnections(android.telecom.RemoteConnection, android.telecom.RemoteConnection);
+    method public final void connectionServiceFocusReleased();
     method public final android.telecom.RemoteConnection createRemoteIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public final android.telecom.RemoteConnection createRemoteOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public final java.util.Collection<android.telecom.Conference> getAllConferences();
     method public final java.util.Collection<android.telecom.Connection> getAllConnections();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConference(android.telecom.Connection, android.telecom.Connection);
+    method public void onConnectionServiceFocusGained();
+    method public void onConnectionServiceFocusLost();
     method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
@@ -47924,7 +47990,6 @@
     method public void interrupt();
     method public static boolean isAccessibilityButtonSupported();
     method public boolean isEnabled();
-    method public boolean isObservedEventType(int);
     method public boolean isTouchExplorationEnabled();
     method public void removeAccessibilityRequestPreparer(android.view.accessibility.AccessibilityRequestPreparer);
     method public boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
@@ -49081,6 +49146,7 @@
     method public android.content.Intent getSecondaryIntent(int);
     method public java.lang.CharSequence getSecondaryLabel(int);
     method public android.view.View.OnClickListener getSecondaryOnClickListener(int);
+    method public java.lang.String getSignature();
     method public java.lang.String getText();
   }
 
@@ -49095,6 +49161,7 @@
     method public android.view.textclassifier.TextClassification.Builder setLabel(java.lang.String);
     method public android.view.textclassifier.TextClassification.Builder setOnClickListener(android.view.View.OnClickListener);
     method public android.view.textclassifier.TextClassification.Builder setPrimaryAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable, android.view.View.OnClickListener);
+    method public android.view.textclassifier.TextClassification.Builder setSignature(java.lang.String);
     method public android.view.textclassifier.TextClassification.Builder setText(java.lang.String);
   }
 
@@ -49159,12 +49226,14 @@
     method public int getEntityCount();
     method public int getSelectionEndIndex();
     method public int getSelectionStartIndex();
+    method public java.lang.String getSignature();
   }
 
   public static final class TextSelection.Builder {
     ctor public TextSelection.Builder(int, int);
     method public android.view.textclassifier.TextSelection build();
     method public android.view.textclassifier.TextSelection.Builder setEntityType(java.lang.String, float);
+    method public android.view.textclassifier.TextSelection.Builder setSignature(java.lang.String);
   }
 
   public static final class TextSelection.Options {
@@ -54885,7 +54954,6 @@
     method public java.lang.Object[] getSigners();
     method public java.lang.String getSimpleName();
     method public java.lang.Class<? super T> getSuperclass();
-    method public java.lang.String getTypeName();
     method public synchronized java.lang.reflect.TypeVariable<java.lang.Class<T>>[] getTypeParameters();
     method public boolean isAnnotation();
     method public boolean isAnonymousClass();
@@ -56546,6 +56614,11 @@
     ctor public MalformedParameterizedTypeException();
   }
 
+  public class MalformedParametersException extends java.lang.RuntimeException {
+    ctor public MalformedParametersException();
+    ctor public MalformedParametersException(java.lang.String);
+  }
+
   public abstract interface Member {
     method public abstract java.lang.Class<?> getDeclaringClass();
     method public abstract int getModifiers();
@@ -56643,6 +56716,7 @@
   }
 
   public abstract interface Type {
+    method public default java.lang.String getTypeName();
   }
 
   public abstract interface TypeVariable<D extends java.lang.reflect.GenericDeclaration> implements java.lang.reflect.Type {
diff --git a/api/system-current.txt b/api/system-current.txt
index 16404e4..33fa246 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -126,6 +126,7 @@
     field public static final java.lang.String READ_PRINT_SERVICES = "android.permission.READ_PRINT_SERVICES";
     field public static final java.lang.String READ_PRINT_SERVICE_RECOMMENDATIONS = "android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS";
     field public static final java.lang.String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
+    field public static final java.lang.String READ_RUNTIME_PROFILES = "android.permission.READ_RUNTIME_PROFILES";
     field public static final java.lang.String READ_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES";
     field public static final java.lang.String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
     field public static final java.lang.String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
@@ -183,6 +184,7 @@
   }
 
   public static final class R.attr {
+    field public static final int isVrOnly = 16844152; // 0x1010578
     field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
     field public static final int requiredSystemPropertyValue = 16844134; // 0x1010566
     field public static final int searchKeyphrase = 16843871; // 0x101045f
@@ -409,8 +411,9 @@
     field public static final java.lang.String EXTRA_LOG_CANCEL_ALL = "android.app.backup.extra.LOG_CANCEL_ALL";
     field public static final java.lang.String EXTRA_LOG_EVENT_CATEGORY = "android.app.backup.extra.LOG_EVENT_CATEGORY";
     field public static final java.lang.String EXTRA_LOG_EVENT_ID = "android.app.backup.extra.LOG_EVENT_ID";
+    field public static final java.lang.String EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION = "android.app.backup.extra.LOG_EVENT_PACKAGE_FULL_VERSION";
     field public static final java.lang.String EXTRA_LOG_EVENT_PACKAGE_NAME = "android.app.backup.extra.LOG_EVENT_PACKAGE_NAME";
-    field public static final java.lang.String EXTRA_LOG_EVENT_PACKAGE_VERSION = "android.app.backup.extra.LOG_EVENT_PACKAGE_VERSION";
+    field public static final deprecated java.lang.String EXTRA_LOG_EVENT_PACKAGE_VERSION = "android.app.backup.extra.LOG_EVENT_PACKAGE_VERSION";
     field public static final java.lang.String EXTRA_LOG_EXCEPTION_FULL_BACKUP = "android.app.backup.extra.LOG_EXCEPTION_FULL_BACKUP";
     field public static final java.lang.String EXTRA_LOG_ILLEGAL_KEY = "android.app.backup.extra.LOG_ILLEGAL_KEY";
     field public static final java.lang.String EXTRA_LOG_MANIFEST_PACKAGE_NAME = "android.app.backup.extra.LOG_MANIFEST_PACKAGE_NAME";
@@ -780,13 +783,16 @@
 
   public final class InstantAppResolveInfo implements android.os.Parcelable {
     ctor public InstantAppResolveInfo(android.content.pm.InstantAppResolveInfo.InstantAppDigest, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>, int);
+    ctor public InstantAppResolveInfo(android.content.pm.InstantAppResolveInfo.InstantAppDigest, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>, long, android.os.Bundle);
     ctor public InstantAppResolveInfo(java.lang.String, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>);
     method public int describeContents();
     method public byte[] getDigestBytes();
     method public int getDigestPrefix();
+    method public android.os.Bundle getExtras();
     method public java.util.List<android.content.pm.InstantAppIntentFilter> getIntentFilters();
+    method public long getLongVersionCode();
     method public java.lang.String getPackageName();
-    method public int getVersionCode();
+    method public deprecated int getVersionCode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.pm.InstantAppResolveInfo> CREATOR;
   }
@@ -843,6 +849,7 @@
   public abstract class PackageManager {
     method public abstract void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
     method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
+    method public android.content.pm.dex.ArtManager getArtManager();
     method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
     method public abstract android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String);
@@ -950,6 +957,24 @@
 
 }
 
+package android.content.pm.dex {
+
+  public class ArtManager {
+    method public boolean isRuntimeProfilingEnabled();
+    method public void snapshotRuntimeProfile(java.lang.String, java.lang.String, android.content.pm.dex.ArtManager.SnapshotRuntimeProfileCallback, android.os.Handler);
+    field public static final int SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND = 1; // 0x1
+    field public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2; // 0x2
+    field public static final int SNAPSHOT_FAILED_PACKAGE_NOT_FOUND = 0; // 0x0
+  }
+
+  public static abstract class ArtManager.SnapshotRuntimeProfileCallback {
+    ctor public ArtManager.SnapshotRuntimeProfileCallback();
+    method public abstract void onError(int);
+    method public abstract void onSuccess(android.os.ParcelFileDescriptor);
+  }
+
+}
+
 package android.content.pm.permission {
 
   public final class RuntimePermissionPresentationInfo implements android.os.Parcelable {
@@ -4106,6 +4131,7 @@
     method public java.lang.String getCdmaMdn(int);
     method public java.lang.String getCdmaMin();
     method public java.lang.String getCdmaMin(int);
+    method public java.lang.String getCdmaPrlVersion();
     method public int getCurrentPhoneType();
     method public int getCurrentPhoneType(int);
     method public deprecated boolean getDataEnabled();
@@ -4151,6 +4177,25 @@
 
 package android.telephony.data {
 
+  public final class DataCallResponse implements android.os.Parcelable {
+    ctor public DataCallResponse(int, int, int, int, java.lang.String, java.lang.String, java.util.List<android.telephony.data.InterfaceAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.net.InetAddress>, java.util.List<java.lang.String>, int);
+    ctor public DataCallResponse(android.os.Parcel);
+    method public int describeContents();
+    method public int getActive();
+    method public java.util.List<android.telephony.data.InterfaceAddress> getAddresses();
+    method public int getCallId();
+    method public java.util.List<java.net.InetAddress> getDnses();
+    method public java.util.List<java.net.InetAddress> getGateways();
+    method public java.lang.String getIfname();
+    method public int getMtu();
+    method public java.util.List<java.lang.String> getPcscfs();
+    method public int getStatus();
+    method public int getSuggestedRetryTime();
+    method public java.lang.String getType();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.data.DataCallResponse> CREATOR;
+  }
+
   public final class DataProfile implements android.os.Parcelable {
     ctor public DataProfile(int, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, int, int, int, int, boolean, int, java.lang.String, int, int, java.lang.String, java.lang.String, boolean);
     ctor public DataProfile(android.os.Parcel);
@@ -4180,6 +4225,17 @@
     field public static final int TYPE_COMMON = 0; // 0x0
   }
 
+  public final class InterfaceAddress implements android.os.Parcelable {
+    ctor public InterfaceAddress(java.net.InetAddress, int);
+    ctor public InterfaceAddress(java.lang.String, int) throws java.net.UnknownHostException;
+    ctor public InterfaceAddress(android.os.Parcel);
+    method public int describeContents();
+    method public java.net.InetAddress getAddress();
+    method public int getNetworkPrefixLength();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.data.InterfaceAddress> CREATOR;
+  }
+
 }
 
 package android.telephony.ims {
diff --git a/api/test-current.txt b/api/test-current.txt
index ef898a4..3c3521f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -425,7 +425,7 @@
 
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
     field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
-    field public static final java.lang.String AUTOFILL_FEATURE_FIELD_DETECTION = "autofill_field_detection";
+    field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
     field public static final java.lang.String AUTOFILL_SERVICE = "autofill_service";
     field public static final java.lang.String DISABLED_PRINT_SERVICES = "disabled_print_services";
     field public static final deprecated java.lang.String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
@@ -457,8 +457,31 @@
     method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
   }
 
+  public final class EditDistanceScorer extends android.service.autofill.InternalScorer implements android.os.Parcelable android.service.autofill.Scorer {
+    method public int describeContents();
+    method public static android.service.autofill.EditDistanceScorer getInstance();
+    method public float getScore(android.view.autofill.AutofillValue, java.lang.String);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.EditDistanceScorer> CREATOR;
+  }
+
+  public final class FieldClassification implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.util.List<android.service.autofill.FieldClassification.Match> getMatches();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.FieldClassification> CREATOR;
+  }
+
+  public static final class FieldClassification.Match implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.lang.String getRemoteId();
+    method public float getScore();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.FieldClassification.Match> CREATOR;
+  }
+
   public static final class FillEventHistory.Event {
-    method public java.util.Map<java.lang.String, java.lang.Integer> getFieldsClassification();
+    method public java.util.Map<android.view.autofill.AutofillId, android.service.autofill.FieldClassification> getFieldsClassification();
   }
 
   public static final class FillResponse.Builder {
@@ -473,6 +496,11 @@
     ctor public InternalSanitizer();
   }
 
+  public abstract class InternalScorer implements android.os.Parcelable android.service.autofill.Scorer {
+    ctor public InternalScorer();
+    method public abstract float getScore(android.view.autofill.AutofillValue, java.lang.String);
+  }
+
   public abstract class InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
     ctor public InternalTransformation();
   }
@@ -490,6 +518,9 @@
     method public boolean isValid(android.service.autofill.ValueFinder);
   }
 
+  public abstract interface Scorer {
+  }
+
   public final class TextValueSanitizer extends android.service.autofill.InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
     method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
   }
@@ -505,7 +536,7 @@
   }
 
   public static final class UserData.Builder {
-    ctor public UserData.Builder(java.lang.String, java.lang.String);
+    ctor public UserData.Builder(android.service.autofill.Scorer, java.lang.String, java.lang.String);
     method public android.service.autofill.UserData.Builder add(java.lang.String, java.lang.String);
     method public android.service.autofill.UserData build();
   }
@@ -600,32 +631,6 @@
 
 }
 
-package android.telecom {
-
-  public abstract class Connection extends android.telecom.Conferenceable {
-    method public void handleRttUpgradeResponse(android.telecom.Connection.RttTextStream);
-    method public void onStartRtt(android.telecom.Connection.RttTextStream);
-    method public void onStopRtt();
-    method public final void sendRemoteRttRequest();
-    method public final void sendRttInitiationFailure(int);
-    method public final void sendRttInitiationSuccess();
-    method public final void sendRttSessionRemotelyTerminated();
-    field public static final int PROPERTY_IS_RTT = 256; // 0x100
-  }
-
-  public static final class Connection.RttTextStream {
-    method public java.lang.String read() throws java.io.IOException;
-    method public java.lang.String readImmediately() throws java.io.IOException;
-    method public void write(java.lang.String) throws java.io.IOException;
-  }
-
-  public final class ConnectionRequest implements android.os.Parcelable {
-    method public android.telecom.Connection.RttTextStream getRttTextStream();
-    method public boolean isRequestingRtt();
-  }
-
-}
-
 package android.telephony {
 
   public class MbmsDownloadSession implements java.lang.AutoCloseable {
@@ -967,15 +972,6 @@
 
 package android.view.accessibility {
 
-  public final class AccessibilityManager {
-    method public void addAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener, android.os.Handler);
-    method public void removeAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
-  }
-
-  public static abstract interface AccessibilityManager.AccessibilityServicesStateChangeListener {
-    method public abstract void onAccessibilityServicesStateChanged(android.view.accessibility.AccessibilityManager);
-  }
-
   public class AccessibilityNodeInfo implements android.os.Parcelable {
     method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
   }
@@ -994,6 +990,7 @@
 
   public final class AutofillManager {
     method public android.service.autofill.UserData getUserData();
+    method public boolean isFieldClassificationEnabled();
     method public void setUserData(android.service.autofill.UserData);
   }
 
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 87cde03..337aeaa 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -161,6 +161,7 @@
     tests/LogEntryMatcher_test.cpp \
     tests/LogReader_test.cpp \
     tests/MetricsManager_test.cpp \
+    tests/StatsLogProcessor_test.cpp \
     tests/UidMap_test.cpp \
     tests/condition/CombinationConditionTracker_test.cpp \
     tests/condition/SimpleConditionTracker_test.cpp \
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index bc63f59..2df0c90 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -86,7 +86,7 @@
     // pass the event to metrics managers.
     for (auto& pair : mMetricsManagers) {
         pair.second->onLogEvent(msg);
-        flushIfNecessary(msg.GetTimestampNs(), pair.first, pair.second);
+        flushIfNecessary(msg.GetTimestampNs(), pair.first, *(pair.second));
     }
 
     // Hard-coded logic to update the isolated uid's in the uid-map.
@@ -215,23 +215,32 @@
     mLastBroadcastTimes.erase(key);
 }
 
-void StatsLogProcessor::flushIfNecessary(uint64_t timestampNs,
-                                         const ConfigKey& key,
-                                         const unique_ptr<MetricsManager>& metricsManager) {
+void StatsLogProcessor::flushIfNecessary(uint64_t timestampNs, const ConfigKey& key,
+                                         MetricsManager& metricsManager) {
     std::lock_guard<std::mutex> lock(mBroadcastTimesMutex);
 
-    size_t totalBytes = metricsManager->byteSize() + mUidMap->getBytesUsed();
-    // TODO: Find a way to test that the dropping and broadcasts are sent when memory is exceeded.
-    if (totalBytes > kMaxSerializedBytes) {  // Too late. We need to start clearing data.
+    auto lastCheckTime = mLastByteSizeTimes.find(key);
+    if (lastCheckTime != mLastByteSizeTimes.end()) {
+        if (timestampNs - lastCheckTime->second < StatsdStats::kMinByteSizeCheckPeriodNs) {
+            return;
+        }
+    }
+
+    // We suspect that the byteSize() computation is expensive, so we set a rate limit.
+    size_t totalBytes = metricsManager.byteSize();
+    mLastByteSizeTimes[key] = timestampNs;
+    if (totalBytes >
+        StatsdStats::kMaxMetricsBytesPerConfig) {  // Too late. We need to start clearing data.
         // We ignore the return value so we force each metric producer to clear its contents.
-        metricsManager->onDumpReport();
+        metricsManager.onDumpReport();
         StatsdStats::getInstance().noteDataDropped(key);
         VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
-    } else if (totalBytes >
-               .9 * kMaxSerializedBytes) {  // Send broadcast so that receivers can pull data.
-        auto lastFlushNs = mLastBroadcastTimes.find(key);
-        if (lastFlushNs != mLastBroadcastTimes.end()) {
-            if (timestampNs - lastFlushNs->second < kMinBroadcastPeriod) {
+    } else if (totalBytes > .9 * StatsdStats::kMaxMetricsBytesPerConfig) {
+        // Send broadcast so that receivers can pull data.
+        auto lastBroadcastTime = mLastBroadcastTimes.find(key);
+        if (lastBroadcastTime != mLastBroadcastTimes.end()) {
+            if (timestampNs - lastBroadcastTime->second < StatsdStats::kMinBroadcastPeriodNs) {
+                VLOG("StatsD would've sent a broadcast but the rate limit stopped us.");
                 return;
             }
         }
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index e9ac015..a4df23a 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -60,29 +60,25 @@
 
     std::unordered_map<ConfigKey, long> mLastBroadcastTimes;
 
+    // Tracks when we last checked the bytes consumed for each config key.
+    std::unordered_map<ConfigKey, long> mLastByteSizeTimes;
+
     sp<UidMap> mUidMap;  // Reference to the UidMap to lookup app name and version for each uid.
 
     sp<AnomalyMonitor> mAnomalyMonitor;
 
-    /* Max *serialized* size of the logs kept in memory before flushing through binder call.
-       Proto lite does not implement the SpaceUsed() function which gives the in memory byte size.
-       So we cap memory usage by limiting the serialized size. Note that protobuf's in memory size
-       is higher than its serialized size.
-     */
-    static const size_t kMaxSerializedBytes = 16 * 1024;
-
     /* Check if we should send a broadcast if approaching memory limits and if we're over, we
      * actually delete the data. */
-    void flushIfNecessary(uint64_t timestampNs,
-                          const ConfigKey& key,
-                          const unique_ptr<MetricsManager>& metricsManager);
+    void flushIfNecessary(uint64_t timestampNs, const ConfigKey& key,
+                          MetricsManager& metricsManager);
 
     // Function used to send a broadcast so that receiver for the config key can call getData
     // to retrieve the stored data.
     std::function<void(const ConfigKey& key)> mSendBroadcast;
 
-    /* Minimum period between two broadcasts in nanoseconds. Currently set to 60 seconds. */
-    static const unsigned long long kMinBroadcastPeriod = 60 * NS_PER_SEC;
+    FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
+    FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast);
+    FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 0e9cd3b..dc12efb 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -72,7 +72,7 @@
 
 // ======================================================================
 StatsService::StatsService(const sp<Looper>& handlerLooper)
-    : mAnomalyMonitor(new AnomalyMonitor(2))  // TODO: Put this comment somewhere better
+    : mAnomalyMonitor(new AnomalyMonitor(MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS))
 {
     mUidMap = new UidMap();
     mConfigManager = new ConfigManager();
@@ -551,7 +551,7 @@
     return NO_ERROR;
 }
 
-Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version,
+Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
                                       const vector<String16>& app) {
     VLOG("StatsService::informAllUidData was called");
 
@@ -566,7 +566,7 @@
     return Status::ok();
 }
 
-Status StatsService::informOnePackage(const String16& app, int32_t uid, int32_t version) {
+Status StatsService::informOnePackage(const String16& app, int32_t uid, int64_t version) {
     VLOG("StatsService::informOnePackage was called");
 
     if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 03bc6d9..e434f65 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -46,6 +46,10 @@
     StatsService(const sp<Looper>& handlerLooper);
     virtual ~StatsService();
 
+    /** The anomaly alarm registered with AlarmManager won't be updated by less than this. */
+    // TODO: Consider making this configurable. And choose a good number.
+    const uint32_t MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS = 5;
+
     virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
     virtual status_t dump(int fd, const Vector<String16>& args);
     virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
@@ -54,9 +58,9 @@
     virtual Status statsCompanionReady();
     virtual Status informAnomalyAlarmFired();
     virtual Status informPollAlarmFired();
-    virtual Status informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version,
+    virtual Status informAllUidData(const vector<int32_t>& uid, const vector<int64_t>& version,
                                     const vector<String16>& app);
-    virtual Status informOnePackage(const String16& app, int32_t uid, int32_t version);
+    virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version);
     virtual Status informOnePackageRemoved(const String16& app, int32_t uid);
     virtual Status writeDataToDisk();
 
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
index 2b2bcfc..4912648 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyMonitor.cpp
@@ -18,6 +18,7 @@
 #include "Log.h"
 
 #include "anomaly/AnomalyMonitor.h"
+#include "guardrail/StatsdStats.h"
 
 namespace android {
 namespace os {
@@ -76,10 +77,7 @@
     if (!wasPresent) return;
     if (mPq.empty()) {
         if (DEBUG) ALOGD("Queue is empty. Cancel any alarm.");
-        mRegisteredAlarmTimeSec = 0;
-        if (mStatsCompanionService != nullptr) {
-            mStatsCompanionService->cancelAnomalyAlarm();
-        }
+        cancelRegisteredAlarmTime_l();
         return;
     }
     uint32_t soonestAlarmTimeSec = mPq.top()->timestampSec;
@@ -106,10 +104,7 @@
     if (!oldAlarms.empty()) {
         if (mPq.empty()) {
             if (DEBUG) ALOGD("Queue is empty. Cancel any alarm.");
-            mRegisteredAlarmTimeSec = 0;
-            if (mStatsCompanionService != nullptr) {
-                mStatsCompanionService->cancelAnomalyAlarm();
-            }
+            cancelRegisteredAlarmTime_l();
         } else {
             // Always update the registered alarm in this case (unlike remove()).
             updateRegisteredAlarmTime_l(mPq.top()->timestampSec);
@@ -123,6 +118,16 @@
     mRegisteredAlarmTimeSec = timestampSec;
     if (mStatsCompanionService != nullptr) {
         mStatsCompanionService->setAnomalyAlarm(secToMs(mRegisteredAlarmTimeSec));
+        StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
+    }
+}
+
+void AnomalyMonitor::cancelRegisteredAlarmTime_l() {
+    if (DEBUG) ALOGD("Cancelling reg alarm.");
+    mRegisteredAlarmTimeSec = 0;
+    if (mStatsCompanionService != nullptr) {
+        mStatsCompanionService->cancelAnomalyAlarm();
+        StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
     }
 }
 
diff --git a/cmds/statsd/src/anomaly/AnomalyMonitor.h b/cmds/statsd/src/anomaly/AnomalyMonitor.h
index e19c469..7acc7904 100644
--- a/cmds/statsd/src/anomaly/AnomalyMonitor.h
+++ b/cmds/statsd/src/anomaly/AnomalyMonitor.h
@@ -138,6 +138,12 @@
      */
     void updateRegisteredAlarmTime_l(uint32_t timestampSec);
 
+    /**
+     * Cancels the alarm registered with StatsCompanionService.
+     * Also correspondingly sets mRegisteredAlarmTimeSec to 0.
+     */
+    void cancelRegisteredAlarmTime_l();
+
     /** Converts uint32 timestamp in seconds to a Java long in msec. */
     int64_t secToMs(uint32_t timeSec);
 };
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index 7bacb44..e8b4083 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -18,6 +18,7 @@
 #include "Log.h"
 
 #include "AnomalyTracker.h"
+#include "guardrail/StatsdStats.h"
 
 #include <android/os/IIncidentManager.h>
 #include <android/os/IncidentReportArgs.h>
@@ -31,8 +32,9 @@
 // TODO: Separate DurationAnomalyTracker as a separate subclass and let each MetricProducer
 //       decide and let which one it wants.
 // TODO: Get rid of bucketNumbers, and return to the original circular array method.
-AnomalyTracker::AnomalyTracker(const Alert& alert)
+AnomalyTracker::AnomalyTracker(const Alert& alert, const ConfigKey& configKey)
     : mAlert(alert),
+      mConfigKey(configKey),
       mNumOfPastBuckets(mAlert.number_of_buckets() - 1) {
     VLOG("AnomalyTracker() called");
     if (mAlert.number_of_buckets() <= 0) {
@@ -220,6 +222,8 @@
     } else {
         ALOGW("An anomaly has occurred! (But informing incidentd not requested.)");
     }
+
+    StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.name());
 }
 
 void AnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index 49e8323..874add2 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -18,6 +18,7 @@
 
 #include <gtest/gtest_prod.h>
 #include "AnomalyMonitor.h"
+#include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // Alert
 #include "stats_util.h"  // HashableDimensionKey and DimToValMap
 
@@ -35,7 +36,7 @@
 // Does NOT allow negative values.
 class AnomalyTracker : public virtual RefBase {
 public:
-    AnomalyTracker(const Alert& alert);
+    AnomalyTracker(const Alert& alert, const ConfigKey& configKey);
 
     virtual ~AnomalyTracker();
 
@@ -107,9 +108,13 @@
 
 protected:
     void flushPastBuckets(const int64_t& currBucketNum);
+
     // statsd_config.proto Alert message that defines this tracker.
     const Alert mAlert;
 
+    // A reference to the Alert's config key.
+    const ConfigKey& mConfigKey;
+
     // Number of past buckets. One less than the total number of buckets needed
     // for the anomaly detection (since the current bucket is not in the past).
     int mNumOfPastBuckets;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 968d08f..c81fc1d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -103,7 +103,11 @@
  * resulted in a particular bit of work being done.
  */
 message WorkSource {
-    // TODO
+    // The uid for a given element in the attribution chain.
+    repeated int32 uid = 1;
+    // The (optional) string tag for an element in the attribution chain. If the
+    // element has no tag, it is encoded as an empty string.
+    repeated string tag = 2;
 }
 
 /*
@@ -413,8 +417,10 @@
     optional string tag = 3;
 
     enum State {
-        OFF = 0;
-        ON = 1;
+        RELEASE = 0;
+        ACQUIRE = 1;
+        CHANGE_RELEASE = 2;
+        CHANGE_ACQUIRE = 3;
     }
     optional State state = 4;
 }
@@ -1066,4 +1072,4 @@
     optional uint64 controller_rx_time_ms = 9;
     // product of current(mA), voltage(V) and time(ms)
     optional uint64 energy_used = 10;
-}
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/atoms_copy.proto b/cmds/statsd/src/atoms_copy.proto
index 58e225a..18b2144 100644
--- a/cmds/statsd/src/atoms_copy.proto
+++ b/cmds/statsd/src/atoms_copy.proto
@@ -99,7 +99,11 @@
  * resulted in a particular bit of work being done.
  */
 message WorkSource {
-    // TODO
+    // The uid for a given element in the attribution chain.
+    repeated int32 uid = 1;
+    // The (optional) string tag for an element in the attribution chain. If the
+    // element has no tag, it is encoded as an empty string.
+    repeated string tag = 2;
 }
 
 /*
@@ -404,8 +408,10 @@
     optional string tag = 3;
 
     enum State {
-        OFF = 0;
-        ON = 1;
+        RELEASE = 0;
+        ACQUIRE = 1;
+        CHANGE_RELEASE = 2;
+        CHANGE_ACQUIRE = 3;
     }
     optional State state = 4;
 }
@@ -907,4 +913,4 @@
     optional uint64 uid = 1;
     optional uint64 freq_idx = 2;
     optional uint64 time_ms = 3;
-}
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index dd84cf4..02aca1a 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -39,11 +39,11 @@
     VLOG("~CombinationConditionTracker() %s", mName.c_str());
 }
 
-bool CombinationConditionTracker::init(const vector<Condition>& allConditionConfig,
+bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConfig,
                                        const vector<sp<ConditionTracker>>& allConditionTrackers,
                                        const unordered_map<string, int>& conditionNameIndexMap,
                                        vector<bool>& stack) {
-    VLOG("Combiniation condition init() %s", mName.c_str());
+    VLOG("Combination predicate init() %s", mName.c_str());
     if (mInitialized) {
         return true;
     }
@@ -51,22 +51,22 @@
     // mark this node as visited in the recursion stack.
     stack[mIndex] = true;
 
-    Condition_Combination combinationCondition = allConditionConfig[mIndex].combination();
+    Predicate_Combination combinationCondition = allConditionConfig[mIndex].combination();
 
     if (!combinationCondition.has_operation()) {
         return false;
     }
     mLogicalOperation = combinationCondition.operation();
 
-    if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.condition_size() != 1) {
+    if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.predicate_size() != 1) {
         return false;
     }
 
-    for (string child : combinationCondition.condition()) {
+    for (string child : combinationCondition.predicate()) {
         auto it = conditionNameIndexMap.find(child);
 
         if (it == conditionNameIndexMap.end()) {
-            ALOGW("Condition %s not found in the config", child.c_str());
+            ALOGW("Predicate %s not found in the config", child.c_str());
             return false;
         }
 
@@ -154,7 +154,7 @@
             }
         }
         nonSlicedConditionCache[mIndex] = ConditionState::kUnknown;
-        ALOGD("CombinationCondition %s sliced may changed? %d", mName.c_str(),
+        ALOGD("CombinationPredicate %s sliced may changed? %d", mName.c_str(),
               conditionChangedCache[mIndex] == true);
     }
 }
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index 00dc6b7..9336914 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -30,7 +30,7 @@
 
     ~CombinationConditionTracker();
 
-    bool init(const std::vector<Condition>& allConditionConfig,
+    bool init(const std::vector<Predicate>& allConditionConfig,
               const std::vector<sp<ConditionTracker>>& allConditionTrackers,
               const std::unordered_map<std::string, int>& conditionNameIndexMap,
               std::vector<bool>& stack) override;
@@ -47,7 +47,7 @@
 
 private:
     LogicalOperation mLogicalOperation;
-    // Store index of the children Conditions.
+    // Store index of the children Predicates.
     // We don't store string name of the Children, because we want to get rid of the hash map to
     // map the name to object. We don't want to store smart pointers to children, because it
     // increases the risk of circular dependency and memory leak.
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index b85d8c1..6f66ad6 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -45,12 +45,12 @@
     // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also
     // be done in the constructor, but we do it separately because (1) easy to return a bool to
     // indicate whether the initialization is successful. (2) makes unit test easier.
-    // allConditionConfig: the list of all Condition config from statsd_config.
+    // allConditionConfig: the list of all Predicate config from statsd_config.
     // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
     //                       need to call init() on children conditions)
     // conditionNameIndexMap: the mapping from condition name to its index.
     // stack: a bit map to keep track which nodes have been visited on the stack in the recursion.
-    virtual bool init(const std::vector<Condition>& allConditionConfig,
+    virtual bool init(const std::vector<Predicate>& allConditionConfig,
                       const std::vector<sp<ConditionTracker>>& allConditionTrackers,
                       const std::unordered_map<std::string, int>& conditionNameIndexMap,
                       std::vector<bool>& stack) = 0;
@@ -118,4 +118,3 @@
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
-
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index 1a01afa..30a3684 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -24,7 +24,7 @@
 namespace os {
 namespace statsd {
 
-// Held by MetricProducer, to query a condition state with input defined in EventConditionLink.
+// Held by MetricProducer, to query a condition state with input defined in MetricConditionLink.
 class ConditionWizard : public virtual android::RefBase {
 public:
     ConditionWizard(){};  // for testing
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 50cd130..18b93ee 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -34,16 +34,16 @@
 
 SimpleConditionTracker::SimpleConditionTracker(
         const ConfigKey& key, const string& name, const int index,
-        const SimpleCondition& simpleCondition,
+        const SimplePredicate& simplePredicate,
         const unordered_map<string, int>& trackerNameIndexMap)
     : ConditionTracker(name, index), mConfigKey(key) {
     VLOG("creating SimpleConditionTracker %s", mName.c_str());
-    mCountNesting = simpleCondition.count_nesting();
+    mCountNesting = simplePredicate.count_nesting();
 
-    if (simpleCondition.has_start()) {
-        auto pair = trackerNameIndexMap.find(simpleCondition.start());
+    if (simplePredicate.has_start()) {
+        auto pair = trackerNameIndexMap.find(simplePredicate.start());
         if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Start matcher %s not found in the config", simpleCondition.start().c_str());
+            ALOGW("Start matcher %s not found in the config", simplePredicate.start().c_str());
             return;
         }
         mStartLogMatcherIndex = pair->second;
@@ -52,10 +52,10 @@
         mStartLogMatcherIndex = -1;
     }
 
-    if (simpleCondition.has_stop()) {
-        auto pair = trackerNameIndexMap.find(simpleCondition.stop());
+    if (simplePredicate.has_stop()) {
+        auto pair = trackerNameIndexMap.find(simplePredicate.stop());
         if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Stop matcher %s not found in the config", simpleCondition.stop().c_str());
+            ALOGW("Stop matcher %s not found in the config", simplePredicate.stop().c_str());
             return;
         }
         mStopLogMatcherIndex = pair->second;
@@ -64,10 +64,10 @@
         mStopLogMatcherIndex = -1;
     }
 
-    if (simpleCondition.has_stop_all()) {
-        auto pair = trackerNameIndexMap.find(simpleCondition.stop_all());
+    if (simplePredicate.has_stop_all()) {
+        auto pair = trackerNameIndexMap.find(simplePredicate.stop_all());
         if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Stop all matcher %s not found in the config", simpleCondition.stop().c_str());
+            ALOGW("Stop all matcher %s not found in the config", simplePredicate.stop().c_str());
             return;
         }
         mStopAllLogMatcherIndex = pair->second;
@@ -76,14 +76,14 @@
         mStopAllLogMatcherIndex = -1;
     }
 
-    mOutputDimension.insert(mOutputDimension.begin(), simpleCondition.dimension().begin(),
-                            simpleCondition.dimension().end());
+    mOutputDimension.insert(mOutputDimension.begin(), simplePredicate.dimension().begin(),
+                            simplePredicate.dimension().end());
 
     if (mOutputDimension.size() > 0) {
         mSliced = true;
     }
 
-    if (simpleCondition.initial_value() == SimpleCondition_InitialValue_FALSE) {
+    if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) {
         mInitialValue = ConditionState::kFalse;
     } else {
         mInitialValue = ConditionState::kUnknown;
@@ -98,7 +98,7 @@
     VLOG("~SimpleConditionTracker()");
 }
 
-bool SimpleConditionTracker::init(const vector<Condition>& allConditionConfig,
+bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig,
                                   const vector<sp<ConditionTracker>>& allConditionTrackers,
                                   const unordered_map<string, int>& conditionNameIndexMap,
                                   vector<bool>& stack) {
@@ -139,7 +139,7 @@
         StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mName, newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
-            ALOGE("Condition %s dropping data for dimension key %s", mName.c_str(), newKey.c_str());
+            ALOGE("Predicate %s dropping data for dimension key %s", mName.c_str(), newKey.c_str());
             return true;
         }
     }
@@ -221,7 +221,7 @@
     conditionChangedCache[mIndex] = changed;
     conditionCache[mIndex] = newCondition;
 
-    VLOG("SimpleCondition %s nonSlicedChange? %d", mName.c_str(),
+    VLOG("SimplePredicate %s nonSlicedChange? %d", mName.c_str(),
          conditionChangedCache[mIndex] == true);
 }
 
@@ -292,13 +292,13 @@
             (pair == conditionParameters.end()) ? DEFAULT_DIMENSION_KEY : pair->second;
 
     if (pair == conditionParameters.end() && mOutputDimension.size() > 0) {
-        ALOGE("Condition %s output has dimension, but it's not specified in the query!",
+        ALOGE("Predicate %s output has dimension, but it's not specified in the query!",
               mName.c_str());
         conditionCache[mIndex] = mInitialValue;
         return;
     }
 
-    VLOG("simpleCondition %s query key: %s", mName.c_str(), key.c_str());
+    VLOG("simplePredicate %s query key: %s", mName.c_str(), key.c_str());
 
     auto startedCountIt = mSlicedConditionState.find(key);
     if (startedCountIt == mSlicedConditionState.end()) {
@@ -308,7 +308,7 @@
                 startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
     }
 
-    VLOG("Condition %s return %d", mName.c_str(), conditionCache[mIndex]);
+    VLOG("Predicate %s return %d", mName.c_str(), conditionCache[mIndex]);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index d21afd1..644d84c 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -30,12 +30,12 @@
 class SimpleConditionTracker : public virtual ConditionTracker {
 public:
     SimpleConditionTracker(const ConfigKey& key, const std::string& name, const int index,
-                           const SimpleCondition& simpleCondition,
+                           const SimplePredicate& simplePredicate,
                            const std::unordered_map<std::string, int>& trackerNameIndexMap);
 
     ~SimpleConditionTracker();
 
-    bool init(const std::vector<Condition>& allConditionConfig,
+    bool init(const std::vector<Predicate>& allConditionConfig,
               const std::vector<sp<ConditionTracker>>& allConditionTrackers,
               const std::unordered_map<std::string, int>& conditionNameIndexMap,
               std::vector<bool>& stack) override;
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index 669a4b7..ff0e3bc 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -94,17 +94,17 @@
 }
 
 HashableDimensionKey getDimensionKeyForCondition(const LogEvent& event,
-                                                 const EventConditionLink& link) {
+                                                 const MetricConditionLink& link) {
     vector<KeyMatcher> eventKey;
-    eventKey.reserve(link.key_in_main().size());
+    eventKey.reserve(link.key_in_what().size());
 
-    for (const auto& key : link.key_in_main()) {
+    for (const auto& key : link.key_in_what()) {
         eventKey.push_back(key);
     }
 
     vector<KeyValuePair> dimensionKey = getDimensionKey(event, eventKey);
 
-    for (int i = 0; i < link.key_in_main_size(); i++) {
+    for (int i = 0; i < link.key_in_what_size(); i++) {
         auto& kv = dimensionKey[i];
         kv.set_key(link.key_in_condition(i).key());
     }
diff --git a/cmds/statsd/src/condition/condition_util.h b/cmds/statsd/src/condition/condition_util.h
index 4167bf9..934c207 100644
--- a/cmds/statsd/src/condition/condition_util.h
+++ b/cmds/statsd/src/condition/condition_util.h
@@ -37,7 +37,7 @@
                                             const std::vector<ConditionState>& conditionCache);
 
 HashableDimensionKey getDimensionKeyForCondition(const LogEvent& event,
-                                                 const EventConditionLink& link);
+                                                 const MetricConditionLink& link);
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 4b82e68..540199d 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -214,7 +214,8 @@
     int UID_PROCESS_STATE_UID_KEY = 1;
 
     int KERNEL_WAKELOCK_TAG_ID = 1004;
-    int KERNEL_WAKELOCK_NAME_KEY = 4;
+    int KERNEL_WAKELOCK_COUNT_KEY = 2;
+    int KERNEL_WAKELOCK_NAME_KEY = 1;
 
     int DEVICE_TEMPERATURE_TAG_ID = 33;
     int DEVICE_TEMPERATURE_KEY = 1;
@@ -272,9 +273,9 @@
     keyMatcher = metric->add_dimension();
     keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
     metric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
-    EventConditionLink* link = metric->add_links();
+    MetricConditionLink* link = metric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
-    link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
+    link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID);
     link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
 
     // Duration of an app holding any wl, while screen on and app in background, slice by uid
@@ -288,7 +289,7 @@
     durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
-    link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
+    link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID);
     link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
 
     // max Duration of an app holding any wl, while screen on and app in background, slice by uid
@@ -302,7 +303,7 @@
     durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
-    link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
+    link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID);
     link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
 
     // Duration of an app holding any wl, while screen on and app in background
@@ -314,7 +315,7 @@
     durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
-    link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
+    link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID);
     link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
 
     // Duration of screen on time.
@@ -338,7 +339,7 @@
     ValueMetric* valueMetric = config.add_value_metric();
     valueMetric->set_name("METRIC_6");
     valueMetric->set_what("KERNEL_WAKELOCK");
-    valueMetric->set_value_field(1);
+    valueMetric->set_value_field(KERNEL_WAKELOCK_COUNT_KEY);
     valueMetric->set_condition("SCREEN_IS_ON");
     keyMatcher = valueMetric->add_dimension();
     keyMatcher->set_key(KERNEL_WAKELOCK_NAME_KEY);
@@ -422,57 +423,57 @@
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_tag(KERNEL_WAKELOCK_TAG_ID);
 
-    // Conditions.............
-    Condition* condition = config.add_condition();
-    condition->set_name("SCREEN_IS_ON");
-    SimpleCondition* simpleCondition = condition->mutable_simple_condition();
-    simpleCondition->set_start("SCREEN_TURNED_ON");
-    simpleCondition->set_stop("SCREEN_TURNED_OFF");
-    simpleCondition->set_count_nesting(false);
+    // Predicates.............
+    Predicate* predicate = config.add_predicate();
+    predicate->set_name("SCREEN_IS_ON");
+    SimplePredicate* simplePredicate = predicate->mutable_simple_predicate();
+    simplePredicate->set_start("SCREEN_TURNED_ON");
+    simplePredicate->set_stop("SCREEN_TURNED_OFF");
+    simplePredicate->set_count_nesting(false);
 
-    condition = config.add_condition();
-    condition->set_name("SCREEN_IS_OFF");
-    simpleCondition = condition->mutable_simple_condition();
-    simpleCondition->set_start("SCREEN_TURNED_OFF");
-    simpleCondition->set_stop("SCREEN_TURNED_ON");
-    simpleCondition->set_count_nesting(false);
+    predicate = config.add_predicate();
+    predicate->set_name("SCREEN_IS_OFF");
+    simplePredicate = predicate->mutable_simple_predicate();
+    simplePredicate->set_start("SCREEN_TURNED_OFF");
+    simplePredicate->set_stop("SCREEN_TURNED_ON");
+    simplePredicate->set_count_nesting(false);
 
-    condition = config.add_condition();
-    condition->set_name("APP_IS_BACKGROUND");
-    simpleCondition = condition->mutable_simple_condition();
-    simpleCondition->set_start("APP_GOES_BACKGROUND");
-    simpleCondition->set_stop("APP_GOES_FOREGROUND");
-    KeyMatcher* condition_dimension1 = simpleCondition->add_dimension();
-    condition_dimension1->set_key(APP_USAGE_UID_KEY_ID);
-    simpleCondition->set_count_nesting(false);
+    predicate = config.add_predicate();
+    predicate->set_name("APP_IS_BACKGROUND");
+    simplePredicate = predicate->mutable_simple_predicate();
+    simplePredicate->set_start("APP_GOES_BACKGROUND");
+    simplePredicate->set_stop("APP_GOES_FOREGROUND");
+    KeyMatcher* predicate_dimension1 = simplePredicate->add_dimension();
+    predicate_dimension1->set_key(APP_USAGE_UID_KEY_ID);
+    simplePredicate->set_count_nesting(false);
 
-    condition = config.add_condition();
-    condition->set_name("APP_IS_BACKGROUND_AND_SCREEN_ON");
-    Condition_Combination* combination_condition = condition->mutable_combination();
-    combination_condition->set_operation(LogicalOperation::AND);
-    combination_condition->add_condition("APP_IS_BACKGROUND");
-    combination_condition->add_condition("SCREEN_IS_ON");
+    predicate = config.add_predicate();
+    predicate->set_name("APP_IS_BACKGROUND_AND_SCREEN_ON");
+    Predicate_Combination* combination_predicate = predicate->mutable_combination();
+    combination_predicate->set_operation(LogicalOperation::AND);
+    combination_predicate->add_predicate("APP_IS_BACKGROUND");
+    combination_predicate->add_predicate("SCREEN_IS_ON");
 
-    condition = config.add_condition();
-    condition->set_name("WL_HELD_PER_APP_PER_NAME");
-    simpleCondition = condition->mutable_simple_condition();
-    simpleCondition->set_start("APP_GET_WL");
-    simpleCondition->set_stop("APP_RELEASE_WL");
-    KeyMatcher* condition_dimension = simpleCondition->add_dimension();
-    condition_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
-    condition_dimension = simpleCondition->add_dimension();
-    condition_dimension->set_key(WAKE_LOCK_NAME_KEY);
-    simpleCondition->set_count_nesting(true);
+    predicate = config.add_predicate();
+    predicate->set_name("WL_HELD_PER_APP_PER_NAME");
+    simplePredicate = predicate->mutable_simple_predicate();
+    simplePredicate->set_start("APP_GET_WL");
+    simplePredicate->set_stop("APP_RELEASE_WL");
+    KeyMatcher* predicate_dimension = simplePredicate->add_dimension();
+    predicate_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
+    predicate_dimension = simplePredicate->add_dimension();
+    predicate_dimension->set_key(WAKE_LOCK_NAME_KEY);
+    simplePredicate->set_count_nesting(true);
 
-    condition = config.add_condition();
-    condition->set_name("WL_HELD_PER_APP");
-    simpleCondition = condition->mutable_simple_condition();
-    simpleCondition->set_start("APP_GET_WL");
-    simpleCondition->set_stop("APP_RELEASE_WL");
-    simpleCondition->set_initial_value(SimpleCondition_InitialValue_FALSE);
-    condition_dimension = simpleCondition->add_dimension();
-    condition_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
-    simpleCondition->set_count_nesting(true);
+    predicate = config.add_predicate();
+    predicate->set_name("WL_HELD_PER_APP");
+    simplePredicate = predicate->mutable_simple_predicate();
+    simplePredicate->set_start("APP_GET_WL");
+    simplePredicate->set_stop("APP_RELEASE_WL");
+    simplePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
+    predicate_dimension = simplePredicate->add_dimension();
+    predicate_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
+    simplePredicate->set_count_nesting(true);
 
     return config;
 }
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index e1c02d7..ffe1be9 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -49,15 +49,15 @@
             return false;
         }
         data->clear();
-        long timestamp = time(nullptr);
+        int timestamp = time(nullptr);
         for (const StatsLogEventWrapper& it : returned_value) {
             log_msg tmp;
             tmp.entry_v1.len = it.bytes.size();
             // Manually set the header size to 28 bytes to match the pushed log events.
             tmp.entry.hdr_size = kLogMsgHeaderSize;
+            tmp.entry_v1.sec = timestamp;
             // And set the received bytes starting after the 28 bytes reserved for header.
             std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + kLogMsgHeaderSize);
-            tmp.entry_v1.sec = timestamp;
             data->push_back(make_shared<LogEvent>(tmp));
         }
         ALOGD("StatsCompanionServicePuller::pull succeeded for %d", tagId);
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 2957457..b02b9da 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -41,11 +41,9 @@
 const int FIELD_ID_BEGIN_TIME = 1;
 const int FIELD_ID_END_TIME = 2;
 const int FIELD_ID_CONFIG_STATS = 3;
-const int FIELD_ID_MATCHER_STATS = 4;
-const int FIELD_ID_CONDITION_STATS = 5;
-const int FIELD_ID_METRIC_STATS = 6;
 const int FIELD_ID_ATOM_STATS = 7;
 const int FIELD_ID_UIDMAP_STATS = 8;
+const int FIELD_ID_ANOMALY_ALARM_STATS = 9;
 
 const int FIELD_ID_MATCHER_STATS_NAME = 1;
 const int FIELD_ID_MATCHER_STATS_COUNT = 2;
@@ -59,6 +57,8 @@
 const int FIELD_ID_ATOM_STATS_TAG = 1;
 const int FIELD_ID_ATOM_STATS_COUNT = 2;
 
+const int FIELD_ID_ANOMALY_ALARMS_REGISTERED = 1;
+
 // TODO: add stats for pulled atoms.
 StatsdStats::StatsdStats() {
     mPushedAtomStats.resize(android::util::kMaxPushedAtomId + 1);
@@ -101,11 +101,12 @@
     if (it != mConfigStats.end()) {
         int32_t nowTimeSec = time(nullptr);
         it->second.set_deletion_time_sec(nowTimeSec);
-        // Add condition stats, metrics stats, matcher stats
-        addSubStatsToConfig(key, it->second);
+        // Add condition stats, metrics stats, matcher stats, alert stats
+        addSubStatsToConfigLocked(key, it->second);
         // Remove them after they are added to the config stats.
         mMatcherStats.erase(key);
         mMetricsStats.erase(key);
+        mAlertStats.erase(key);
         mConditionStats.erase(key);
         mIceBox.push_back(it->second);
         mConfigStats.erase(it);
@@ -219,6 +220,17 @@
     matcherStats[name]++;
 }
 
+void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const string& name) {
+    lock_guard<std::mutex> lock(mLock);
+    auto& alertStats = mAlertStats[key];
+    alertStats[name]++;
+}
+
+void StatsdStats::noteRegisteredAnomalyAlarmChanged() {
+    lock_guard<std::mutex> lock(mLock);
+    mAnomalyAlarmRegisteredStats++;
+}
+
 void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) {
     lock_guard<std::mutex> lock(mLock);
 
@@ -246,6 +258,8 @@
     mConditionStats.clear();
     mMetricsStats.clear();
     std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0);
+    mAlertStats.clear();
+    mAnomalyAlarmRegisteredStats = 0;
     mMatcherStats.clear();
     for (auto& config : mConfigStats) {
         config.second.clear_broadcast_sent_time_sec();
@@ -254,10 +268,11 @@
         config.second.clear_matcher_stats();
         config.second.clear_condition_stats();
         config.second.clear_metric_stats();
+        config.second.clear_alert_stats();
     }
 }
 
-void StatsdStats::addSubStatsToConfig(const ConfigKey& key,
+void StatsdStats::addSubStatsToConfigLocked(const ConfigKey& key,
                                       StatsdStatsReport_ConfigStats& configStats) {
     // Add matcher stats
     if (mMatcherStats.find(key) != mMatcherStats.end()) {
@@ -289,6 +304,16 @@
             VLOG("metrics %s max output tuple size %d", stats.first.c_str(), stats.second);
         }
     }
+    // Add anomaly detection alert stats
+    if (mAlertStats.find(key) != mAlertStats.end()) {
+        const auto& alertStats = mAlertStats[key];
+        for (const auto& stats : alertStats) {
+            auto output = configStats.add_alert_stats();
+            output->set_name(stats.first);
+            output->set_declared_times(stats.second);
+            VLOG("alert %s declared %d times", stats.first.c_str(), stats.second);
+        }
+    }
 }
 
 void StatsdStats::dumpStats(std::vector<uint8_t>* output, bool reset) {
@@ -358,7 +383,7 @@
             }
         }
 
-        addSubStatsToConfig(pair.first, configStats);
+        addSubStatsToConfigLocked(pair.first, configStats);
 
         const int numBytes = configStats.ByteSize();
         vector<char> buffer(numBytes);
@@ -370,6 +395,7 @@
         configStats.clear_matcher_stats();
         configStats.clear_condition_stats();
         configStats.clear_metric_stats();
+        configStats.clear_alert_stats();
     }
 
     VLOG("********Atom stats***********");
@@ -386,6 +412,15 @@
         }
     }
 
+    if (mAnomalyAlarmRegisteredStats > 0) {
+        VLOG("********AnomalyAlarmStats stats***********");
+        long long token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ANOMALY_ALARM_STATS);
+        proto.write(FIELD_TYPE_INT32 | FIELD_ID_ANOMALY_ALARMS_REGISTERED,
+                    mAnomalyAlarmRegisteredStats);
+        proto.end(token);
+        VLOG("Anomaly alarm registrations: %d", mAnomalyAlarmRegisteredStats);
+    }
+
     const int numBytes = mUidMapStats.ByteSize();
     vector<char> buffer(numBytes);
     mUidMapStats.SerializeToArray(&buffer[0], numBytes);
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index d6f6566..4cf168e 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -19,6 +19,7 @@
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 
 #include <gtest/gtest_prod.h>
+#include <log/log_time.h>
 #include <mutex>
 #include <string>
 #include <vector>
@@ -45,10 +46,20 @@
 
     const static int kMaxTimestampCount = 20;
 
+    // Max memory allowed for storing metrics per configuration. When this limit is approached,
+    // statsd will send a broadcast so that the client can fetch the data and clear this memory.
+    static const size_t kMaxMetricsBytesPerConfig = 128 * 1024;
+
     // Cap the UID map's memory usage to this. This should be fairly high since the UID information
     // is critical for understanding the metrics.
     const static size_t kMaxBytesUsedUidMap = 50 * 1024;
 
+    /* Minimum period between two broadcasts in nanoseconds. */
+    static const unsigned long long kMinBroadcastPeriodNs = 60 * NS_PER_SEC;
+
+    /* Min period between two checks of byte size per config key in nanoseconds. */
+    static const unsigned long long kMinByteSizeCheckPeriodNs = 10 * NS_PER_SEC;
+
     /**
      * Report a new config has been received and report the static stats about the config.
      *
@@ -112,11 +123,24 @@
     void noteMatcherMatched(const ConfigKey& key, const std::string& name);
 
     /**
+     * Report that an anomaly detection alert has been declared.
+     *
+     * [key]: The config key that this alert belongs to.
+     * [name]: The name of the alert.
+     */
+    void noteAnomalyDeclared(const ConfigKey& key, const std::string& name);
+
+    /**
      * Report an atom event has been logged.
      */
     void noteAtomLogged(int atomId, int32_t timeSec);
 
     /**
+     * Report that statsd modified the anomaly alarm registered with StatsCompanionService.
+     */
+    void noteRegisteredAnomalyAlarmChanged();
+
+    /**
      * Records the number of snapshot and delta entries that are being dropped from the uid map.
      */
     void noteUidMapDropped(int snapshots, int deltas);
@@ -174,6 +198,14 @@
     // This is a vector, not a map because it will be accessed A LOT -- for each stats log.
     std::vector<int> mPushedAtomStats;
 
+    // Stores the number of times statsd modified the anomaly alarm registered with
+    // StatsCompanionService.
+    int mAnomalyAlarmRegisteredStats = 0;
+
+    // Stores the number of times an anomaly detection alert has been declared
+    // (per config, per alert name).
+    std::map<const ConfigKey, std::map<const std::string, int>> mAlertStats;
+
     // Stores how many times a matcher have been matched.
     std::map<const ConfigKey, std::map<const std::string, int>> mMatcherStats;
 
@@ -181,7 +213,8 @@
 
     void resetInternalLocked();
 
-    void addSubStatsToConfig(const ConfigKey& key, StatsdStatsReport_ConfigStats& configStats);
+    void addSubStatsToConfigLocked(const ConfigKey& key,
+                                   StatsdStatsReport_ConfigStats& configStats);
 
     void noteDataDropped(const ConfigKey& key, int32_t timeSec);
 
@@ -195,6 +228,7 @@
     FRIEND_TEST(StatsdStatsTest, TestSubStats);
     FRIEND_TEST(StatsdStatsTest, TestAtomLog);
     FRIEND_TEST(StatsdStatsTest, TestTimestampThreshold);
+    FRIEND_TEST(StatsdStatsTest, TestAnomalyMonitor);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 534d54e..9e88e5d0 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -117,6 +117,9 @@
                     allMatched = false;
                     break;
                 }
+            } else {
+                allMatched = false;
+                break;
             }
         } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqInt ||
                    matcherCase == KeyValueMatcher::ValueMatcherCase::kLtInt ||
@@ -153,6 +156,9 @@
                         break;
                     }
                 }
+            } else {
+                allMatched = false;
+                break;
             }
         } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqBool) {
             // Boolean fields
@@ -163,6 +169,9 @@
                     allMatched = false;
                     break;
                 }
+            } else {
+                allMatched = false;
+                break;
             }
         } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtFloat ||
                    matcherCase == KeyValueMatcher::ValueMatcherCase::kGtFloat) {
@@ -181,6 +190,9 @@
                         break;
                     }
                 }
+            } else {
+                allMatched = false;
+                break;
             }
         } else {
             // If value matcher is not present, assume that we match.
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index ce38f58..800a2b9 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -51,7 +51,8 @@
     void finish() override;
 
     // TODO: Implement this later.
-    virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+    virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version)
+            override{};
     // TODO: Implement this later.
     virtual void notifyAppRemoved(const string& apk, const int uid) override{};
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 83fe84a..cedea30 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -111,7 +111,7 @@
         return nullptr;
     }
     // TODO: return a DurationAnomalyTracker (which should sublclass AnomalyTracker)
-    return new AnomalyTracker(alert);
+    return new AnomalyTracker(alert, mConfigKey);
 }
 
 void DurationMetricProducer::startNewProtoOutputStreamLocked(long long startTime) {
@@ -122,16 +122,16 @@
 }
 
 unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
-        const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) const {
+        const HashableDimensionKey& eventKey) const {
     switch (mMetric.aggregation_type()) {
         case DurationMetric_AggregationType_SUM:
             return make_unique<OringDurationTracker>(
                     mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested,
-                    mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket);
+                    mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers);
         case DurationMetric_AggregationType_MAX_SPARSE:
             return make_unique<MaxDurationTracker>(
                     mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested,
-                    mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket);
+                    mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers);
     }
 }
 
@@ -179,13 +179,6 @@
             continue;
         }
 
-        // If there is no duration bucket info for this key, don't include it in the report.
-        // For example, duration started, but condition is never turned to true.
-        // TODO: Only add the key to the map when we add duration buckets info for it.
-        if (pair.second.size() == 0) {
-            continue;
-        }
-
         long long wrapperToken =
                 mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
@@ -228,7 +221,7 @@
                   (long long)mCurrentBucketStartTimeNs);
     std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked();
     startNewProtoOutputStreamLocked(endTime);
-    // TODO: Properly clear the old buckets.
+    mPastBuckets.clear();
     return buffer;
 }
 
@@ -238,7 +231,7 @@
     }
     VLOG("flushing...........");
     for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end();) {
-        if (it->second->flushIfNeeded(eventTime)) {
+        if (it->second->flushIfNeeded(eventTime, &mPastBuckets)) {
             VLOG("erase bucket for key %s", it->first.c_str());
             it = mCurrentSlicedDuration.erase(it);
         } else {
@@ -290,7 +283,7 @@
         if (hitGuardRailLocked(eventKey)) {
             return;
         }
-        mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey, mPastBuckets[eventKey]);
+        mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey);
     }
 
     auto it = mCurrentSlicedDuration.find(eventKey);
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 2653df8..4bf9d1c 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -50,7 +50,8 @@
     void finish() override;
 
     // TODO: Implement this later.
-    virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+    virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version)
+            override{};
     // TODO: Implement this later.
     virtual void notifyAppRemoved(const string& apk, const int uid) override{};
 
@@ -106,7 +107,7 @@
 
     // Helper function to create a duration tracker given the metric aggregation type.
     std::unique_ptr<DurationTracker> createDurationTracker(
-            const HashableDimensionKey& eventKey, std::vector<DurationBucket>& bucket) const;
+            const HashableDimensionKey& eventKey) const;
 
     // Util function to check whether the specified dimension hits the guardrail.
     bool hitGuardRailLocked(const HashableDimensionKey& newKey);
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index afb48c4..da3b3ca 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -43,7 +43,8 @@
     void finish() override;
 
     // TODO: Implement this later.
-    virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+    virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version)
+            override{};
     // TODO: Implement this later.
     virtual void notifyAppRemoved(const string& apk, const int uid) override{};
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 50cc8d4..1f6bd58b 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -203,7 +203,7 @@
         return;
     }
     for (const auto& data : allData) {
-        onMatchedLogEvent(0, *data, false /*scheduledPull*/);
+        onMatchedLogEventLocked(0, *data, false /*scheduledPull*/);
     }
     flushIfNeededLocked(eventTime);
 }
@@ -227,7 +227,7 @@
     std::lock_guard<std::mutex> lock(mMutex);
 
     for (const auto& data : allData) {
-        onMatchedLogEvent(0, *data, true /*scheduledPull*/);
+        onMatchedLogEventLocked(0, *data, true /*scheduledPull*/);
     }
 }
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index c839c09..36705b1 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -59,7 +59,8 @@
     void finish() override;
 
     // TODO: Implement this later.
-    virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+    virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version)
+            override{};
     // TODO: Implement this later.
     virtual void notifyAppRemoved(const string& apk, const int uid) override{};
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 819df77..adeb3cd 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -94,7 +94,7 @@
     }
 
     virtual sp<AnomalyTracker> createAnomalyTracker(const Alert &alert) {
-        return new AnomalyTracker(alert);
+        return new AnomalyTracker(alert, mConfigKey);
     }
 
     void addAnomalyTracker(sp<AnomalyTracker> tracker) {
@@ -137,7 +137,7 @@
     // that StatsLogReport wants.
     std::unordered_map<HashableDimensionKey, std::vector<KeyValuePair>> mDimensionKeyMap;
 
-    std::vector<EventConditionLink> mConditionLinks;
+    std::vector<MetricConditionLink> mConditionLinks;
 
     std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
 
@@ -149,7 +149,7 @@
      * [eventKey]: the extracted dimension key for the final output. if the metric doesn't have
      *             dimensions, it will be DEFAULT_DIMENSION_KEY
      * [conditionKey]: the keys of conditions which should be used to query the condition for this
-     *                 target event (from EventConditionLink). This is passed to individual metrics
+     *                 target event (from MetricConditionLink). This is passed to individual metrics
      *                 because DurationMetric needs it to be cached.
      * [condition]: whether condition is met. If condition is sliced, this is the result coming from
      *              query with ConditionWizard; If condition is not sliced, this is the
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 4e6a0ce..86c4733 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -36,7 +36,7 @@
 public:
     MetricsManager(const ConfigKey& configKey, const StatsdConfig& config);
 
-    ~MetricsManager();
+    virtual ~MetricsManager();
 
     // Return whether the configuration is valid.
     bool isConfigValid() const;
@@ -52,11 +52,11 @@
     void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor);
 
     // Config source owner can call onDumpReport() to get all the metrics collected.
-    std::vector<std::unique_ptr<std::vector<uint8_t>>> onDumpReport();
+    virtual std::vector<std::unique_ptr<std::vector<uint8_t>>> onDumpReport();
 
     // Computes the total byte size of all metrics managed by a single config source.
     // Does not change the state.
-    size_t byteSize();
+    virtual size_t byteSize();
 
 private:
     const ConfigKey mConfigKey;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 60b725d..a2efd3f 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -49,7 +49,8 @@
     void onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& data) override;
 
     // TODO: Implement this later.
-    virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override{};
+    virtual void notifyAppUpgrade(const string& apk, const int uid, const int64_t version)
+            override{};
     // TODO: Implement this later.
     virtual void notifyAppRemoved(const string& apk, const int uid) override{};
 
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 834f7f5..3c714b3 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -63,8 +63,7 @@
     DurationTracker(const ConfigKey& key, const string& name, const HashableDimensionKey& eventKey,
                     sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                     uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
-                    const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
-                    std::vector<DurationBucket>& bucket)
+                    const std::vector<sp<AnomalyTracker>>& anomalyTrackers)
         : mConfigKey(key),
           mName(name),
           mEventKey(eventKey),
@@ -73,7 +72,6 @@
           mBucketSizeNs(bucketSizeNs),
           mNested(nesting),
           mCurrentBucketStartTimeNs(currentBucketStartNs),
-          mBucket(bucket),
           mDuration(0),
           mCurrentBucketNum(0),
           mAnomalyTrackers(anomalyTrackers){};
@@ -91,7 +89,9 @@
 
     // Flush stale buckets if needed, and return true if the tracker has no on-going duration
     // events, so that the owner can safely remove the tracker.
-    virtual bool flushIfNeeded(uint64_t timestampNs) = 0;
+    virtual bool flushIfNeeded(
+            uint64_t timestampNs,
+            std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) = 0;
 
     // Predict the anomaly timestamp given the current status.
     virtual int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
@@ -159,8 +159,6 @@
 
     uint64_t mCurrentBucketStartTimeNs;
 
-    std::vector<DurationBucket>& mBucket;  // where to write output
-
     int64_t mDuration;  // current recorded duration result
 
     uint64_t mCurrentBucketNum;
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 4b346dd..08c9135 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -28,10 +28,9 @@
                                        const HashableDimensionKey& eventKey,
                                        sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                                        uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
-                                       const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
-                                       std::vector<DurationBucket>& bucket)
+                                       const std::vector<sp<AnomalyTracker>>& anomalyTrackers)
     : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
-                      bucketSizeNs, anomalyTrackers, bucket) {
+                      bucketSizeNs, anomalyTrackers) {
 }
 
 bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
@@ -145,7 +144,8 @@
     }
 }
 
-bool MaxDurationTracker::flushIfNeeded(uint64_t eventTime) {
+bool MaxDurationTracker::flushIfNeeded(
+        uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) {
     if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
         return false;
     }
@@ -202,7 +202,7 @@
 
     if (mDuration != 0) {
         info.mDuration = mDuration;
-        mBucket.push_back(info);
+        (*output)[mEventKey].push_back(info);
         addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
         VLOG("  final duration for last bucket: %lld", (long long)mDuration);
     }
@@ -215,7 +215,7 @@
             info.mBucketEndNs = endTime + mBucketSizeNs * i;
             info.mBucketNum = mCurrentBucketNum + i;
             info.mDuration = mBucketSizeNs;
-            mBucket.push_back(info);
+            (*output)[mEventKey].push_back(info);
             addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
             VLOG("  filling gap bucket with duration %lld", (long long)mBucketSizeNs);
         }
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index e0d1466..10eddb8 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -32,15 +32,16 @@
                        const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
                        int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
                        uint64_t bucketSizeNs,
-                       const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
-                       std::vector<DurationBucket>& bucket);
+                       const std::vector<sp<AnomalyTracker>>& anomalyTrackers);
     void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
                    const ConditionKey& conditionKey) override;
     void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
                   const bool stopAll) override;
     void noteStopAll(const uint64_t eventTime) override;
 
-    bool flushIfNeeded(uint64_t timestampNs) override;
+    bool flushIfNeeded(
+            uint64_t timestampNs,
+            std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override;
 
     void onSlicedConditionMayChange(const uint64_t timestamp) override;
     void onConditionChanged(bool condition, const uint64_t timestamp) override;
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index abdfbc0..8122744 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -29,10 +29,9 @@
                                            sp<ConditionWizard> wizard, int conditionIndex,
                                            bool nesting, uint64_t currentBucketStartNs,
                                            uint64_t bucketSizeNs,
-                                           const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
-                                           std::vector<DurationBucket>& bucket)
+                                           const std::vector<sp<AnomalyTracker>>& anomalyTrackers)
     : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
-                      bucketSizeNs, anomalyTrackers, bucket),
+                      bucketSizeNs, anomalyTrackers),
       mStarted(),
       mPaused() {
     mLastStartTime = 0;
@@ -128,7 +127,8 @@
     mConditionKeyMap.clear();
 }
 
-bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) {
+bool OringDurationTracker::flushIfNeeded(
+        uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) {
     if (eventTime < mCurrentBucketStartTimeNs + mBucketSizeNs) {
         return false;
     }
@@ -145,7 +145,7 @@
     }
     if (mDuration > 0) {
         current_info.mDuration = mDuration;
-        mBucket.push_back(current_info);
+        (*output)[mEventKey].push_back(current_info);
         addPastBucketToAnomalyTrackers(current_info.mDuration, current_info.mBucketNum);
         VLOG("  duration: %lld", (long long)current_info.mDuration);
     }
@@ -157,9 +157,9 @@
             info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs;
             info.mBucketNum = mCurrentBucketNum + i;
             info.mDuration = mBucketSizeNs;
-                mBucket.push_back(info);
-                addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
-                VLOG("  add filling bucket with duration %lld", (long long)info.mDuration);
+            (*output)[mEventKey].push_back(info);
+            addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum);
+            VLOG("  add filling bucket with duration %lld", (long long)info.mDuration);
         }
     }
     mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs;
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index a8404a9..b7d3cba 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -31,8 +31,7 @@
                          const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
                          int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
                          uint64_t bucketSizeNs,
-                         const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
-                         std::vector<DurationBucket>& bucket);
+                         const std::vector<sp<AnomalyTracker>>& anomalyTrackers);
 
     void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
                    const ConditionKey& conditionKey) override;
@@ -43,7 +42,9 @@
     void onSlicedConditionMayChange(const uint64_t timestamp) override;
     void onConditionChanged(bool condition, const uint64_t timestamp) override;
 
-    bool flushIfNeeded(uint64_t timestampNs) override;
+    bool flushIfNeeded(
+            uint64_t timestampNs,
+            std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override;
 
     int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
                                       const uint64_t currentTimestamp) const override;
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 00048f5..943becb 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -64,20 +64,20 @@
 bool handleMetricWithConditions(
         const string condition, const int metricIndex,
         const unordered_map<string, int>& conditionTrackerMap,
-        const ::google::protobuf::RepeatedPtrField<::android::os::statsd::EventConditionLink>&
+        const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>&
                 links,
         vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
         unordered_map<int, std::vector<int>>& conditionToMetricMap) {
     auto condition_it = conditionTrackerMap.find(condition);
     if (condition_it == conditionTrackerMap.end()) {
-        ALOGW("cannot find Condition \"%s\" in the config", condition.c_str());
+        ALOGW("cannot find Predicate \"%s\" in the config", condition.c_str());
         return false;
     }
 
     for (const auto& link : links) {
         auto it = conditionTrackerMap.find(link.condition());
         if (it == conditionTrackerMap.end()) {
-            ALOGW("cannot find Condition \"%s\" in the config", link.condition().c_str());
+            ALOGW("cannot find Predicate \"%s\" in the config", link.condition().c_str());
             return false;
         }
         allConditionTrackers[condition_it->second]->setSliced(true);
@@ -142,31 +142,31 @@
                     unordered_map<string, int>& conditionTrackerMap,
                     vector<sp<ConditionTracker>>& allConditionTrackers,
                     unordered_map<int, std::vector<int>>& trackerToConditionMap) {
-    vector<Condition> conditionConfigs;
-    const int conditionTrackerCount = config.condition_size();
+    vector<Predicate> conditionConfigs;
+    const int conditionTrackerCount = config.predicate_size();
     conditionConfigs.reserve(conditionTrackerCount);
     allConditionTrackers.reserve(conditionTrackerCount);
 
     for (int i = 0; i < conditionTrackerCount; i++) {
-        const Condition& condition = config.condition(i);
+        const Predicate& condition = config.predicate(i);
         int index = allConditionTrackers.size();
         switch (condition.contents_case()) {
-            case Condition::ContentsCase::kSimpleCondition: {
+            case Predicate::ContentsCase::kSimplePredicate: {
                 allConditionTrackers.push_back(new SimpleConditionTracker(
-                        key, condition.name(), index, condition.simple_condition(), logTrackerMap));
+                        key, condition.name(), index, condition.simple_predicate(), logTrackerMap));
                 break;
             }
-            case Condition::ContentsCase::kCombination: {
+            case Predicate::ContentsCase::kCombination: {
                 allConditionTrackers.push_back(
                         new CombinationConditionTracker(condition.name(), index));
                 break;
             }
             default:
-                ALOGE("Condition \"%s\" malformed", condition.name().c_str());
+                ALOGE("Predicate \"%s\" malformed", condition.name().c_str());
                 return false;
         }
         if (conditionTrackerMap.find(condition.name()) != conditionTrackerMap.end()) {
-            ALOGE("Duplicate Condition found!");
+            ALOGE("Duplicate Predicate found!");
             return false;
         }
         conditionTrackerMap[condition.name()] = index;
@@ -232,7 +232,7 @@
             }
         } else {
             if (metric.links_size() > 0) {
-                ALOGW("metrics has a EventConditionLink but doesn't have a condition");
+                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
                 return false;
             }
         }
@@ -254,43 +254,43 @@
             return false;
         }
 
-        const Condition& durationWhat = config.condition(what_it->second);
+        const Predicate& durationWhat = config.predicate(what_it->second);
 
-        if (durationWhat.contents_case() != Condition::ContentsCase::kSimpleCondition) {
+        if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
             ALOGE("DurationMetric's \"what\" must be a simple condition");
             return false;
         }
 
-        const auto& simpleCondition = durationWhat.simple_condition();
+        const auto& simplePredicate = durationWhat.simple_predicate();
 
-        bool nesting = simpleCondition.count_nesting();
+        bool nesting = simplePredicate.count_nesting();
 
         int trackerIndices[3] = {-1, -1, -1};
-        if (!simpleCondition.has_start() ||
-            !handleMetricWithLogTrackers(simpleCondition.start(), metricIndex,
+        if (!simplePredicate.has_start() ||
+            !handleMetricWithLogTrackers(simplePredicate.start(), metricIndex,
                                          metric.dimension_size() > 0, allAtomMatchers,
                                          logTrackerMap, trackerToMetricMap, trackerIndices[0])) {
             ALOGE("Duration metrics must specify a valid the start event matcher");
             return false;
         }
 
-        if (simpleCondition.has_stop() &&
-            !handleMetricWithLogTrackers(simpleCondition.stop(), metricIndex,
+        if (simplePredicate.has_stop() &&
+            !handleMetricWithLogTrackers(simplePredicate.stop(), metricIndex,
                                          metric.dimension_size() > 0, allAtomMatchers,
                                          logTrackerMap, trackerToMetricMap, trackerIndices[1])) {
             return false;
         }
 
-        if (simpleCondition.has_stop_all() &&
-            !handleMetricWithLogTrackers(simpleCondition.stop_all(), metricIndex,
+        if (simplePredicate.has_stop_all() &&
+            !handleMetricWithLogTrackers(simplePredicate.stop_all(), metricIndex,
                                          metric.dimension_size() > 0, allAtomMatchers,
                                          logTrackerMap, trackerToMetricMap, trackerIndices[2])) {
             return false;
         }
 
         vector<KeyMatcher> internalDimension;
-        internalDimension.insert(internalDimension.begin(), simpleCondition.dimension().begin(),
-                                 simpleCondition.dimension().end());
+        internalDimension.insert(internalDimension.begin(), simplePredicate.dimension().begin(),
+                                 simplePredicate.dimension().end());
 
         int conditionIndex = -1;
 
@@ -303,7 +303,7 @@
             }
         } else {
             if (metric.links_size() > 0) {
-                ALOGW("metrics has a EventConditionLink but doesn't have a condition");
+                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
                 return false;
             }
         }
@@ -340,7 +340,7 @@
             }
         } else {
             if (metric.links_size() > 0) {
-                ALOGW("metrics has a EventConditionLink but doesn't have a condition");
+                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
                 return false;
             }
         }
@@ -390,7 +390,7 @@
             }
         } else {
             if (metric.links_size() > 0) {
-                ALOGW("metrics has a EventConditionLink but doesn't have a condition");
+                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
                 return false;
             }
         }
@@ -439,7 +439,7 @@
             }
         } else {
             if (metric.links_size() > 0) {
-                ALOGW("metrics has a EventConditionLink but doesn't have a condition");
+                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
                 return false;
             }
         }
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 751d8df..8e9c8e3 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -62,7 +62,7 @@
                     std::unordered_map<std::string, int>& conditionTrackerMap,
                     std::vector<sp<ConditionTracker>>& allConditionTrackers,
                     std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
-                    std::unordered_map<int, std::vector<EventConditionLink>>& eventConditionLinks);
+                    std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks);
 
 // Initialize MetricProducers.
 // input:
@@ -79,7 +79,7 @@
         const ConfigKey& key, const StatsdConfig& config,
         const std::unordered_map<std::string, int>& logTrackerMap,
         const std::unordered_map<std::string, int>& conditionTrackerMap,
-        const std::unordered_map<int, std::vector<EventConditionLink>>& eventConditionLinks,
+        const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
         const vector<sp<LogMatchingTracker>>& allAtomMatchers,
         vector<sp<ConditionTracker>>& allConditionTrackers,
         std::vector<sp<MetricProducer>>& allMetricProducers,
diff --git a/cmds/statsd/src/packages/PackageInfoListener.h b/cmds/statsd/src/packages/PackageInfoListener.h
index 5aa3db5..bc8b0de 100644
--- a/cmds/statsd/src/packages/PackageInfoListener.h
+++ b/cmds/statsd/src/packages/PackageInfoListener.h
@@ -28,7 +28,7 @@
 public:
     // Uid map will notify this listener that the app with apk name and uid has been upgraded to
     // the specified version.
-    virtual void notifyAppUpgrade(const std::string& apk, const int uid, const int version) = 0;
+    virtual void notifyAppUpgrade(const std::string& apk, const int uid, const int64_t version) = 0;
 
     // Notify interested listeners that the given apk and uid combination no longer exits.
     virtual void notifyAppRemoved(const std::string& apk, const int uid) = 0;
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index db592e2..6e7a613 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -23,6 +23,8 @@
 #include <binder/IServiceManager.h>
 #include <utils/Errors.h>
 
+#include <inttypes.h>
+
 using namespace android;
 
 namespace android {
@@ -46,7 +48,7 @@
     return false;
 }
 
-int UidMap::getAppVersion(int uid, const string& packageName) const {
+int64_t UidMap::getAppVersion(int uid, const string& packageName) const {
     lock_guard<mutex> lock(mMutex);
 
     auto range = mMap.equal_range(uid);
@@ -58,13 +60,13 @@
     return 0;
 }
 
-void UidMap::updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode,
+void UidMap::updateMap(const vector<int32_t>& uid, const vector<int64_t>& versionCode,
                        const vector<String16>& packageName) {
     updateMap(time(nullptr) * NS_PER_SEC, uid, versionCode, packageName);
 }
 
 void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
-                       const vector<int32_t>& versionCode, const vector<String16>& packageName) {
+                       const vector<int64_t>& versionCode, const vector<String16>& packageName) {
     lock_guard<mutex> lock(mMutex);  // Exclusively lock for updates.
 
     mMap.clear();
@@ -87,12 +89,12 @@
     ensureBytesUsedBelowLimit();
 }
 
-void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t& versionCode) {
+void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int64_t& versionCode) {
     updateApp(time(nullptr) * NS_PER_SEC, app_16, uid, versionCode);
 }
 
 void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid,
-                       const int32_t& versionCode) {
+                       const int64_t& versionCode) {
     lock_guard<mutex> lock(mMutex);
 
     string app = string(String8(app_16).string());
@@ -116,7 +118,7 @@
     auto range = mMap.equal_range(int(uid));
     for (auto it = range.first; it != range.second; ++it) {
         if (it->second.packageName == app) {
-            it->second.versionCode = int(versionCode);
+            it->second.versionCode = versionCode;
             return;
         }
         VLOG("updateApp failed to find the app %s with uid %i to update", app.c_str(), uid);
@@ -124,7 +126,7 @@
     }
 
     // Otherwise, we need to add an app at this uid.
-    mMap.insert(make_pair(uid, AppData(app, int(versionCode))));
+    mMap.insert(make_pair(uid, AppData(app, versionCode)));
 }
 
 void UidMap::ensureBytesUsedBelowLimit() {
@@ -298,8 +300,8 @@
     lock_guard<mutex> lock(mMutex);
 
     for (auto it : mMap) {
-        fprintf(out, "%s, v%d (%i)\n", it.second.packageName.c_str(), it.second.versionCode,
-                it.first);
+        fprintf(out, "%s, v%" PRId64 " (%i)\n", it.second.packageName.c_str(),
+                it.second.versionCode, it.first);
     }
 }
 
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index d2971c9..9e1ad69 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -41,9 +41,9 @@
 
 struct AppData {
     const string packageName;
-    int versionCode;
+    int64_t versionCode;
 
-    AppData(const string& a, const int v) : packageName(a), versionCode(v){};
+    AppData(const string& a, const int64_t v) : packageName(a), versionCode(v){};
 };
 
 // UidMap keeps track of what the corresponding app name (APK name) and version code for every uid
@@ -57,16 +57,16 @@
      * All three inputs must be the same size, and the jth element in each array refers to the same
      * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j].
      */
-    void updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode,
+    void updateMap(const vector<int32_t>& uid, const vector<int64_t>& versionCode,
                    const vector<String16>& packageName);
 
-    void updateApp(const String16& packageName, const int32_t& uid, const int32_t& versionCode);
+    void updateApp(const String16& packageName, const int32_t& uid, const int64_t& versionCode);
     void removeApp(const String16& packageName, const int32_t& uid);
 
     // Returns true if the given uid contains the specified app (eg. com.google.android.gms).
     bool hasApp(int uid, const string& packageName) const;
 
-    int getAppVersion(int uid, const string& packageName) const;
+    int64_t getAppVersion(int uid, const string& packageName) const;
 
     // Helper for debugging contents of this uid map. Can be triggered with:
     // adb shell cmd stats print-uid-map
@@ -104,10 +104,10 @@
 
 private:
     void updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
-                   const vector<int32_t>& versionCode, const vector<String16>& packageName);
+                   const vector<int64_t>& versionCode, const vector<String16>& packageName);
 
     void updateApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid,
-                   const int32_t& versionCode);
+                   const int64_t& versionCode);
     void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid);
 
     UidMapping getOutput(const int64_t& timestamp, const ConfigKey& key);
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index cc8a26d..f8b91fe 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -102,7 +102,7 @@
     message PackageInfo {
       optional string name = 1;
 
-      optional int32 version = 2;
+      optional int64 version = 2;
 
       optional int32 uid = 3;
     }
@@ -119,7 +119,7 @@
     optional string app = 3;
     optional int32 uid = 4;
 
-    optional int32 version = 5;
+    optional int64 version = 5;
   }
   repeated Change changes = 2;
 }
@@ -193,6 +193,11 @@
         optional int32 max_tuple_counts = 2;
     }
 
+    message AlertStats {
+        optional string name = 1;
+        optional int32 declared_times = 2;
+    }
+
     message ConfigStats {
         optional int32 uid = 1;
         optional string name = 2;
@@ -210,6 +215,7 @@
         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;
@@ -229,4 +235,9 @@
         optional int32 dropped_changes = 5;
     }
     optional UidMapStats uidmap_stats = 8;
+
+    message AnomalyAlarmStats {
+        optional int32 alarms_registered = 1;
+    }
+    optional AnomalyAlarmStats anomaly_alarm_stats = 9;
 }
\ No newline at end of file
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 6837be0..c9654af 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -75,7 +75,7 @@
     }
 }
 
-message SimpleCondition {
+message SimplePredicate {
     optional string start = 1;
 
     optional string stop = 2;
@@ -93,17 +93,17 @@
     repeated KeyMatcher dimension = 6;
 }
 
-message Condition {
+message Predicate {
     optional string name = 1;
 
     message Combination {
         optional LogicalOperation operation = 1;
 
-        repeated string condition = 2;
+        repeated string predicate = 2;
     }
 
     oneof contents {
-        SimpleCondition simple_condition = 2;
+        SimplePredicate simple_predicate = 2;
         Combination combination = 3;
     }
 }
@@ -112,10 +112,10 @@
     optional int64 bucket_size_millis = 1;
 }
 
-message EventConditionLink {
+message MetricConditionLink {
     optional string condition = 1;
 
-    repeated KeyMatcher key_in_main = 2;
+    repeated KeyMatcher key_in_what = 2;
 
     repeated KeyMatcher key_in_condition = 3;
 }
@@ -127,7 +127,7 @@
 
     optional string condition = 3;
 
-    repeated EventConditionLink links = 4;
+    repeated MetricConditionLink links = 4;
 }
 
 message CountMetric {
@@ -141,7 +141,7 @@
 
     optional Bucket bucket = 5;
 
-    repeated EventConditionLink links = 6;
+    repeated MetricConditionLink links = 6;
 }
 
 message DurationMetric {
@@ -151,7 +151,7 @@
 
     optional string condition = 3;
 
-    repeated EventConditionLink links = 4;
+    repeated MetricConditionLink links = 4;
 
     enum AggregationType {
         SUM = 1;
@@ -178,7 +178,7 @@
 
     optional Bucket bucket = 6;
 
-    repeated EventConditionLink links = 7;
+    repeated MetricConditionLink links = 7;
 }
 
 message ValueMetric {
@@ -194,7 +194,7 @@
 
     optional Bucket bucket = 6;
 
-    repeated EventConditionLink links = 7;
+    repeated MetricConditionLink links = 7;
 
     enum AggregationType { SUM = 1; }
     optional AggregationType aggregation_type = 8 [default = SUM];
@@ -232,7 +232,7 @@
 
     repeated AtomMatcher atom_matcher = 7;
 
-    repeated Condition condition = 8;
+    repeated Predicate predicate = 8;
 
     repeated Alert alert = 9;
 }
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index e4750e9..c6a5310 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -34,7 +34,7 @@
 using std::set;
 using std::unordered_map;
 using std::vector;
-using android::os::statsd::Condition;
+using android::os::statsd::Predicate;
 
 #ifdef __ANDROID__
 
@@ -165,7 +165,7 @@
     return config;
 }
 
-StatsdConfig buildMissingCondition() {
+StatsdConfig buildMissingPredicate() {
     StatsdConfig config;
     config.set_name("12345");
 
@@ -223,7 +223,7 @@
     return config;
 }
 
-StatsdConfig buildCircleConditions() {
+StatsdConfig buildCirclePredicates() {
     StatsdConfig config;
     config.set_name("12345");
 
@@ -247,19 +247,19 @@
     simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
 
-    auto condition = config.add_condition();
+    auto condition = config.add_predicate();
     condition->set_name("SCREEN_IS_ON");
-    SimpleCondition* simpleCondition = condition->mutable_simple_condition();
-    simpleCondition->set_start("SCREEN_IS_ON");
-    simpleCondition->set_stop("SCREEN_IS_OFF");
+    SimplePredicate* simplePredicate = condition->mutable_simple_predicate();
+    simplePredicate->set_start("SCREEN_IS_ON");
+    simplePredicate->set_stop("SCREEN_IS_OFF");
 
-    condition = config.add_condition();
+    condition = config.add_predicate();
     condition->set_name("SCREEN_IS_EITHER_ON_OFF");
 
-    Condition_Combination* combination = condition->mutable_combination();
+    Predicate_Combination* combination = condition->mutable_combination();
     combination->set_operation(LogicalOperation::OR);
-    combination->add_condition("SCREEN_IS_ON");
-    combination->add_condition("SCREEN_IS_EITHER_ON_OFF");
+    combination->add_predicate("SCREEN_IS_ON");
+    combination->add_predicate("SCREEN_IS_EITHER_ON_OFF");
 
     return config;
 }
@@ -329,8 +329,8 @@
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
-TEST(MetricsManagerTest, TestMissingCondition) {
-    StatsdConfig config = buildMissingCondition();
+TEST(MetricsManagerTest, TestMissingPredicate) {
+    StatsdConfig config = buildMissingPredicate();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
@@ -344,8 +344,8 @@
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
-TEST(MetricsManagerTest, TestCircleConditionDependency) {
-    StatsdConfig config = buildCircleConditions();
+TEST(MetricsManagerTest, TestCirclePredicateDependency) {
+    StatsdConfig config = buildCirclePredicates();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
new file mode 100644
index 0000000..ff04d95
--- /dev/null
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -0,0 +1,117 @@
+// 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.
+
+#include "StatsLogProcessor.h"
+#include "config/ConfigKey.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
+#include "packages/UidMap.h"
+#include "statslog.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <stdio.h>
+
+using namespace android;
+using namespace testing;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+/**
+ * Mock MetricsManager (ByteSize() is called).
+ */
+class MockMetricsManager : public MetricsManager {
+public:
+    MockMetricsManager() : MetricsManager(ConfigKey(1, "key"), StatsdConfig()) {
+    }
+
+    MOCK_METHOD0(byteSize, size_t());
+    MOCK_METHOD0(onDumpReport, std::vector<std::unique_ptr<std::vector<uint8_t>>>());
+};
+
+TEST(StatsLogProcessorTest, TestRateLimitByteSize) {
+    sp<UidMap> m = new UidMap();
+    sp<AnomalyMonitor> anomalyMonitor;
+    // Construct the processor with a dummy sendBroadcast function that does nothing.
+    StatsLogProcessor p(m, anomalyMonitor, [](const ConfigKey& key) {});
+
+    MockMetricsManager mockMetricsManager;
+
+    ConfigKey key(100, "key");
+    // Expect only the first flush to trigger a check for byte size since the last two are
+    // rate-limited.
+    EXPECT_CALL(mockMetricsManager, byteSize()).Times(1);
+    p.flushIfNecessary(99, key, mockMetricsManager);
+    p.flushIfNecessary(100, key, mockMetricsManager);
+    p.flushIfNecessary(101, key, mockMetricsManager);
+}
+
+TEST(StatsLogProcessorTest, TestRateLimitBroadcast) {
+    sp<UidMap> m = new UidMap();
+    sp<AnomalyMonitor> anomalyMonitor;
+    int broadcastCount = 0;
+    StatsLogProcessor p(m, anomalyMonitor,
+                        [&broadcastCount](const ConfigKey& key) { broadcastCount++; });
+
+    MockMetricsManager mockMetricsManager;
+
+    ConfigKey key(100, "key");
+    EXPECT_CALL(mockMetricsManager, byteSize())
+            .Times(2)
+            .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * .95)));
+
+    // Expect only one broadcast despite always returning a size that should trigger broadcast.
+    p.flushIfNecessary(1, key, mockMetricsManager);
+    EXPECT_EQ(1, broadcastCount);
+
+    // This next call to flush should not trigger a broadcast.
+    p.mLastByteSizeTimes.clear();  // Force another check for byte size.
+    p.flushIfNecessary(2, key, mockMetricsManager);
+    EXPECT_EQ(1, broadcastCount);
+}
+
+TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) {
+    sp<UidMap> m = new UidMap();
+    sp<AnomalyMonitor> anomalyMonitor;
+    int broadcastCount = 0;
+    StatsLogProcessor p(m, anomalyMonitor,
+                        [&broadcastCount](const ConfigKey& key) { broadcastCount++; });
+
+    MockMetricsManager mockMetricsManager;
+
+    ConfigKey key(100, "key");
+    EXPECT_CALL(mockMetricsManager, byteSize())
+            .Times(1)
+            .WillRepeatedly(Return(int(StatsdStats::kMaxMetricsBytesPerConfig * 1.2)));
+
+    EXPECT_CALL(mockMetricsManager, onDumpReport()).Times(1);
+
+    // Expect to call the onDumpReport and skip the broadcast.
+    p.flushIfNecessary(1, key, mockMetricsManager);
+    EXPECT_EQ(0, broadcastCount);
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index aa194e6..5b2cedd 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -61,7 +61,7 @@
 TEST(UidMapTest, TestMatching) {
     UidMap m;
     vector<int32_t> uids;
-    vector<int32_t> versions;
+    vector<int64_t> versions;
     vector<String16> apps;
 
     uids.push_back(1000);
@@ -79,7 +79,7 @@
 TEST(UidMapTest, TestAddAndRemove) {
     UidMap m;
     vector<int32_t> uids;
-    vector<int32_t> versions;
+    vector<int64_t> versions;
     vector<String16> apps;
 
     uids.push_back(1000);
@@ -107,7 +107,7 @@
     m.OnConfigUpdated(config1);
 
     vector<int32_t> uids;
-    vector<int32_t> versions;
+    vector<int64_t> versions;
     vector<String16> apps;
     uids.push_back(1000);
     uids.push_back(1000);
@@ -161,7 +161,7 @@
 
     size_t startBytes = m.mBytesUsed;
     vector<int32_t> uids;
-    vector<int32_t> versions;
+    vector<int64_t> versions;
     vector<String16> apps;
     uids.push_back(1000);
     apps.push_back(String16(kApp1.c_str()));
@@ -191,7 +191,7 @@
 
     size_t startBytes = m.mBytesUsed;
     vector<int32_t> uids;
-    vector<int32_t> versions;
+    vector<int64_t> versions;
     vector<String16> apps;
     for (int i = 0; i < 100; i++) {
         uids.push_back(1);
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index 65c2a05..f385763 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -30,6 +30,8 @@
 namespace os {
 namespace statsd {
 
+const ConfigKey kConfigKey(0, "test");
+
 void AddValueToBucket(const std::vector<std::pair<string, long>>& key_value_pair_list,
                       std::shared_ptr<DimToValMap> bucket) {
     for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) {
@@ -51,7 +53,7 @@
     alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC);
     alert.set_trigger_if_sum_gt(2);
 
-    AnomalyTracker anomalyTracker(alert);
+    AnomalyTracker anomalyTracker(alert, kConfigKey);
 
     std::shared_ptr<DimToValMap> bucket0 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}});
     int64_t eventTimestamp0 = 10;
@@ -168,7 +170,7 @@
     alert.set_refractory_period_secs(2 * bucketSizeNs / NS_PER_SEC);
     alert.set_trigger_if_sum_gt(2);
 
-    AnomalyTracker anomalyTracker(alert);
+    AnomalyTracker anomalyTracker(alert, kConfigKey);
 
     std::shared_ptr<DimToValMap> bucket9 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}});
     std::shared_ptr<DimToValMap> bucket16 = MockBucket({{"b", 4}});
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 8b0b6a4..01ba82d 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -30,21 +30,21 @@
 
 const ConfigKey kConfigKey(0, "test");
 
-SimpleCondition getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
+SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
                                          bool outputSlicedUid) {
-    SimpleCondition simpleCondition;
-    simpleCondition.set_start("WAKE_LOCK_ACQUIRE");
-    simpleCondition.set_stop("WAKE_LOCK_RELEASE");
-    simpleCondition.set_stop_all("RELEASE_ALL");
+    SimplePredicate simplePredicate;
+    simplePredicate.set_start("WAKE_LOCK_ACQUIRE");
+    simplePredicate.set_stop("WAKE_LOCK_RELEASE");
+    simplePredicate.set_stop_all("RELEASE_ALL");
     if (outputSlicedUid) {
-        KeyMatcher* keyMatcher = simpleCondition.add_dimension();
+        KeyMatcher* keyMatcher = simplePredicate.add_dimension();
         keyMatcher->set_key(1);
     }
 
-    simpleCondition.set_count_nesting(countNesting);
-    simpleCondition.set_initial_value(defaultFalse ? SimpleCondition_InitialValue_FALSE
-                                                       : SimpleCondition_InitialValue_UNKNOWN);
-    return simpleCondition;
+    simplePredicate.set_count_nesting(countNesting);
+    simplePredicate.set_initial_value(defaultFalse ? SimplePredicate_InitialValue_FALSE
+                                                       : SimplePredicate_InitialValue_UNKNOWN);
+    return simplePredicate;
 }
 
 void makeWakeLockEvent(LogEvent* event, int uid, const string& wl, int acquire) {
@@ -68,18 +68,18 @@
 }
 
 TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) {
-    SimpleCondition simpleCondition;
-    simpleCondition.set_start("SCREEN_TURNED_ON");
-    simpleCondition.set_stop("SCREEN_TURNED_OFF");
-    simpleCondition.set_count_nesting(false);
-    simpleCondition.set_initial_value(SimpleCondition_InitialValue_UNKNOWN);
+    SimplePredicate simplePredicate;
+    simplePredicate.set_start("SCREEN_TURNED_ON");
+    simplePredicate.set_stop("SCREEN_TURNED_OFF");
+    simplePredicate.set_count_nesting(false);
+    simplePredicate.set_initial_value(SimplePredicate_InitialValue_UNKNOWN);
 
     unordered_map<string, int> trackerNameIndexMap;
     trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
     trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
 
     SimpleConditionTracker conditionTracker(kConfigKey, "SCREEN_IS_ON", 0 /*tracker index*/,
-                                            simpleCondition, trackerNameIndexMap);
+                                            simplePredicate, trackerNameIndexMap);
 
     LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
 
@@ -87,11 +87,11 @@
     matcherState.push_back(MatchingState::kNotMatched);
     matcherState.push_back(MatchingState::kNotMatched);
 
-    vector<sp<ConditionTracker>> allConditions;
+    vector<sp<ConditionTracker>> allPredicates;
     vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
     vector<bool> changedCache(1, false);
 
-    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
                                        changedCache);
     // not matched start or stop. condition doesn't change
     EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]);
@@ -104,7 +104,7 @@
     conditionCache[0] = ConditionState::kNotEvaluated;
     changedCache[0] = false;
 
-    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
                                        changedCache);
     // now condition should change to true.
     EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
@@ -117,7 +117,7 @@
     conditionCache[0] = ConditionState::kNotEvaluated;
     changedCache[0] = false;
 
-    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
                                        changedCache);
     EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
     EXPECT_FALSE(changedCache[0]);
@@ -129,7 +129,7 @@
     conditionCache[0] = ConditionState::kNotEvaluated;
     changedCache[0] = false;
 
-    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
                                        changedCache);
 
     // condition changes to false.
@@ -143,7 +143,7 @@
     conditionCache[0] = ConditionState::kNotEvaluated;
     changedCache[0] = false;
 
-    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
                                        changedCache);
     // condition should still be false. not changed.
     EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
@@ -151,17 +151,17 @@
 }
 
 TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) {
-    SimpleCondition simpleCondition;
-    simpleCondition.set_start("SCREEN_TURNED_ON");
-    simpleCondition.set_stop("SCREEN_TURNED_OFF");
-    simpleCondition.set_count_nesting(true);
+    SimplePredicate simplePredicate;
+    simplePredicate.set_start("SCREEN_TURNED_ON");
+    simplePredicate.set_stop("SCREEN_TURNED_OFF");
+    simplePredicate.set_count_nesting(true);
 
     unordered_map<string, int> trackerNameIndexMap;
     trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
     trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
 
     SimpleConditionTracker conditionTracker(kConfigKey, "SCREEN_IS_ON",
-                                            0 /*condition tracker index*/, simpleCondition,
+                                            0 /*condition tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
 
     LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
@@ -170,11 +170,11 @@
     vector<MatchingState> matcherState;
     matcherState.push_back(MatchingState::kMatched);
     matcherState.push_back(MatchingState::kNotMatched);
-    vector<sp<ConditionTracker>> allConditions;
+    vector<sp<ConditionTracker>> allPredicates;
     vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
     vector<bool> changedCache(1, false);
 
-    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
                                        changedCache);
 
     EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
@@ -187,7 +187,7 @@
     conditionCache[0] = ConditionState::kNotEvaluated;
     changedCache[0] = false;
 
-    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
                                        changedCache);
 
     EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
@@ -200,7 +200,7 @@
     conditionCache[0] = ConditionState::kNotEvaluated;
     changedCache[0] = false;
 
-    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
                                        changedCache);
     // result should still be true
     EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
@@ -213,7 +213,7 @@
     conditionCache[0] = ConditionState::kNotEvaluated;
     changedCache[0] = false;
 
-    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
                                        changedCache);
     // result should still be true
     EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
@@ -221,7 +221,7 @@
 }
 
 TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
-    SimpleCondition simpleCondition = getWakeLockHeldCondition(
+    SimplePredicate simplePredicate = getWakeLockHeldCondition(
             true /*nesting*/, true /*default to false*/, true /*output slice by uid*/);
     string conditionName = "WL_HELD_BY_UID2";
 
@@ -231,7 +231,7 @@
     trackerNameIndexMap["RELEASE_ALL"] = 2;
 
     SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
-                                            0 /*condition tracker index*/, simpleCondition,
+                                            0 /*condition tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
     int uid = 111;
 
@@ -243,11 +243,11 @@
     matcherState.push_back(MatchingState::kMatched);
     matcherState.push_back(MatchingState::kNotMatched);
     matcherState.push_back(MatchingState::kNotMatched);
-    vector<sp<ConditionTracker>> allConditions;
+    vector<sp<ConditionTracker>> allPredicates;
     vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
     vector<bool> changedCache(1, false);
 
-    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
                                        changedCache);
 
     EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
@@ -257,7 +257,7 @@
     const auto queryKey = getWakeLockQueryKey(1, uid, conditionName);
     conditionCache[0] = ConditionState::kNotEvaluated;
 
-    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
     EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
     // another wake lock acquired by this uid
@@ -268,7 +268,7 @@
     matcherState.push_back(MatchingState::kNotMatched);
     conditionCache[0] = ConditionState::kNotEvaluated;
     changedCache[0] = false;
-    conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
                                        changedCache);
     EXPECT_FALSE(changedCache[0]);
 
@@ -280,7 +280,7 @@
     matcherState.push_back(MatchingState::kMatched);
     conditionCache[0] = ConditionState::kNotEvaluated;
     changedCache[0] = false;
-    conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
                                        changedCache);
     // nothing changes, because wake lock 2 is still held for this uid
     EXPECT_FALSE(changedCache[0]);
@@ -292,19 +292,19 @@
     matcherState.push_back(MatchingState::kMatched);
     conditionCache[0] = ConditionState::kNotEvaluated;
     changedCache[0] = false;
-    conditionTracker.evaluateCondition(event4, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
                                        changedCache);
     EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
     EXPECT_TRUE(changedCache[0]);
 
     // query again
     conditionCache[0] = ConditionState::kNotEvaluated;
-    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
     EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
 }
 
 TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
-    SimpleCondition simpleCondition = getWakeLockHeldCondition(
+    SimplePredicate simplePredicate = getWakeLockHeldCondition(
             true /*nesting*/, true /*default to false*/, false /*slice output by uid*/);
     string conditionName = "WL_HELD";
 
@@ -314,7 +314,7 @@
     trackerNameIndexMap["RELEASE_ALL"] = 2;
 
     SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
-                                            0 /*condition tracker index*/, simpleCondition,
+                                            0 /*condition tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
     int uid1 = 111;
     string uid1_wl1 = "wl1_1";
@@ -329,11 +329,11 @@
     matcherState.push_back(MatchingState::kMatched);
     matcherState.push_back(MatchingState::kNotMatched);
     matcherState.push_back(MatchingState::kNotMatched);
-    vector<sp<ConditionTracker>> allConditions;
+    vector<sp<ConditionTracker>> allPredicates;
     vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
     vector<bool> changedCache(1, false);
 
-    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
                                        changedCache);
 
     EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
@@ -343,7 +343,7 @@
     map<string, HashableDimensionKey> queryKey;
     conditionCache[0] = ConditionState::kNotEvaluated;
 
-    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
     EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
     // another wake lock acquired by this uid
@@ -354,7 +354,7 @@
     matcherState.push_back(MatchingState::kNotMatched);
     conditionCache[0] = ConditionState::kNotEvaluated;
     changedCache[0] = false;
-    conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
                                        changedCache);
     EXPECT_FALSE(changedCache[0]);
 
@@ -366,7 +366,7 @@
     matcherState.push_back(MatchingState::kMatched);
     conditionCache[0] = ConditionState::kNotEvaluated;
     changedCache[0] = false;
-    conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
                                        changedCache);
     // nothing changes, because uid2 is still holding wl.
     EXPECT_FALSE(changedCache[0]);
@@ -378,19 +378,19 @@
     matcherState.push_back(MatchingState::kMatched);
     conditionCache[0] = ConditionState::kNotEvaluated;
     changedCache[0] = false;
-    conditionTracker.evaluateCondition(event4, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
                                        changedCache);
     EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
     EXPECT_TRUE(changedCache[0]);
 
     // query again
     conditionCache[0] = ConditionState::kNotEvaluated;
-    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
     EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
 }
 
 TEST(SimpleConditionTrackerTest, TestStopAll) {
-    SimpleCondition simpleCondition = getWakeLockHeldCondition(
+    SimplePredicate simplePredicate = getWakeLockHeldCondition(
             true /*nesting*/, true /*default to false*/, true /*output slice by uid*/);
     string conditionName = "WL_HELD_BY_UID3";
 
@@ -400,7 +400,7 @@
     trackerNameIndexMap["RELEASE_ALL"] = 2;
 
     SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
-                                            0 /*condition tracker index*/, simpleCondition,
+                                            0 /*condition tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
     int uid1 = 111;
     int uid2 = 222;
@@ -413,11 +413,11 @@
     matcherState.push_back(MatchingState::kMatched);
     matcherState.push_back(MatchingState::kNotMatched);
     matcherState.push_back(MatchingState::kNotMatched);
-    vector<sp<ConditionTracker>> allConditions;
+    vector<sp<ConditionTracker>> allPredicates;
     vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
     vector<bool> changedCache(1, false);
 
-    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
                                        changedCache);
 
     EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
@@ -427,7 +427,7 @@
     const auto queryKey = getWakeLockQueryKey(1, uid1, conditionName);
     conditionCache[0] = ConditionState::kNotEvaluated;
 
-    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
     EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
     // another wake lock acquired by uid2
@@ -439,7 +439,7 @@
     matcherState.push_back(MatchingState::kNotMatched);
     conditionCache[0] = ConditionState::kNotEvaluated;
     changedCache[0] = false;
-    conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
                                        changedCache);
     EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size());
     EXPECT_TRUE(changedCache[0]);
@@ -448,7 +448,7 @@
     const auto queryKey2 = getWakeLockQueryKey(1, uid2, conditionName);
     conditionCache[0] = ConditionState::kNotEvaluated;
 
-    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
     EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
 
@@ -461,7 +461,7 @@
 
     conditionCache[0] = ConditionState::kNotEvaluated;
     changedCache[0] = false;
-    conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache,
+    conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
                                        changedCache);
     EXPECT_TRUE(changedCache[0]);
     EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
@@ -470,14 +470,14 @@
     const auto queryKey3 = getWakeLockQueryKey(1, uid1, conditionName);
     conditionCache[0] = ConditionState::kNotEvaluated;
 
-    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
     EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
 
     // TEST QUERY
     const auto queryKey4 = getWakeLockQueryKey(1, uid2, conditionName);
     conditionCache[0] = ConditionState::kNotEvaluated;
 
-    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
     EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
 }
 
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index a8193dd..9fed4f8 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -118,6 +118,10 @@
     stats.noteMetricDimensionSize(key, "metric1", 201);
     stats.noteMetricDimensionSize(key, "metric1", 202);
 
+    stats.noteAnomalyDeclared(key, "alert1");
+    stats.noteAnomalyDeclared(key, "alert1");
+    stats.noteAnomalyDeclared(key, "alert2");
+
     // broadcast-> 2
     stats.noteBroadcastSent(key);
     stats.noteBroadcastSent(key);
@@ -142,7 +146,6 @@
     EXPECT_EQ(3, configReport.dump_report_time_sec_size());
 
     EXPECT_EQ(2, configReport.matcher_stats_size());
-
     // matcher1 is the first in the list
     if (!configReport.matcher_stats(0).name().compare("matcher1")) {
         EXPECT_EQ(2, configReport.matcher_stats(0).matched_times());
@@ -157,6 +160,13 @@
         EXPECT_EQ("matcher1", configReport.matcher_stats(1).name());
     }
 
+    EXPECT_EQ(2, configReport.alert_stats_size());
+    bool alert1first = !configReport.alert_stats(0).name().compare("alert1");
+    EXPECT_EQ("alert1", configReport.alert_stats(alert1first ? 0 : 1).name());
+    EXPECT_EQ(2, configReport.alert_stats(alert1first ? 0 : 1).declared_times());
+    EXPECT_EQ("alert2", configReport.alert_stats(alert1first ? 1 : 0).name());
+    EXPECT_EQ(1, configReport.alert_stats(alert1first ? 1 : 0).declared_times());
+
     EXPECT_EQ(1, configReport.condition_stats_size());
     EXPECT_EQ("condition1", configReport.condition_stats(0).name());
     EXPECT_EQ(250, configReport.condition_stats(0).max_tuple_counts());
@@ -169,6 +179,7 @@
     stats.noteMatcherMatched(key, "matcher99");
     stats.noteConditionDimensionSize(key, "condition99", 300);
     stats.noteMetricDimensionSize(key, "metric99", 270);
+    stats.noteAnomalyDeclared(key, "alert99");
 
     // now the config stats should only contain the stats about the new event.
     stats.dumpStats(&output, false);
@@ -187,6 +198,10 @@
     EXPECT_EQ(1, configReport2.metric_stats_size());
     EXPECT_EQ("metric99", configReport2.metric_stats(0).name());
     EXPECT_EQ(270, configReport2.metric_stats(0).max_tuple_counts());
+
+    EXPECT_EQ(1, configReport2.alert_stats_size());
+    EXPECT_EQ("alert99", configReport2.alert_stats(0).name());
+    EXPECT_EQ(1, configReport2.alert_stats(0).declared_times());
 }
 
 TEST(StatsdStatsTest, TestAtomLog) {
@@ -224,6 +239,21 @@
     EXPECT_TRUE(sensorAtomGood);
 }
 
+
+TEST(StatsdStatsTest, TestAnomalyMonitor) {
+    StatsdStats stats;
+    stats.noteRegisteredAnomalyAlarmChanged();
+    stats.noteRegisteredAnomalyAlarmChanged();
+
+    vector<uint8_t> output;
+    stats.dumpStats(&output, false);
+    StatsdStatsReport report;
+    bool good = report.ParseFromArray(&output[0], output.size());
+    EXPECT_TRUE(good);
+
+    EXPECT_EQ(2, report.anomaly_alarm_stats().alarms_registered());
+}
+
 TEST(StatsdStatsTest, TestTimestampThreshold) {
     StatsdStats stats;
     vector<int32_t> timestamps;
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index dc42943..d973ba1 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -141,9 +141,9 @@
     metric.set_name("1");
     metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
     metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
-    EventConditionLink* link = metric.add_links();
+    MetricConditionLink* link = metric.add_links();
     link->set_condition("APP_IN_BACKGROUND_PER_UID");
-    link->add_key_in_main()->set_key(1);
+    link->add_key_in_what()->set_key(1);
     link->add_key_in_condition()->set_key(2);
 
     LogEvent event1(1, bucketStartTimeNs + 1);
@@ -196,7 +196,7 @@
     int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
     int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
 
-    sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert);
+    sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey);
 
     CountMetric metric;
     metric.set_name("1");
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 3f2b7cd..3158c27 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -89,7 +89,7 @@
     LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3);
 
     DurationMetricProducer durationProducer(
-            kConfigKey, metric, 0 /*no condition*/, 1 /* start index */, 2 /* stop index */,
+            kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
             3 /* stop_all index */, false /*nesting*/, wizard, {}, bucketStartTimeNs);
     EXPECT_FALSE(durationProducer.mCondition);
     EXPECT_FALSE(durationProducer.isConditionSliced());
@@ -97,12 +97,7 @@
     durationProducer.onMatchedLogEvent(1 /* start index*/, event1, false /* scheduledPull */);
     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2, false /* scheduledPull */);
     durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
-    EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
-                durationProducer.mPastBuckets.end());
-    const auto& buckets1 = durationProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
-    EXPECT_EQ(0UL, buckets1.size());
-
+    EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
 
     durationProducer.onMatchedLogEvent(1 /* start index*/, event3, false /* scheduledPull */);
     durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index 724ad59..f3302fd 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -91,9 +91,9 @@
     EventMetric metric;
     metric.set_name("1");
     metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
-    EventConditionLink* link = metric.add_links();
+    MetricConditionLink* link = metric.add_links();
     link->set_condition("APP_IN_BACKGROUND_PER_UID");
-    link->add_key_in_main()->set_key(1);
+    link->add_key_in_what()->set_key(1);
     link->add_key_in_condition()->set_key(2);
 
     LogEvent event1(1, bucketStartTimeNs + 1);
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index ed13db2..59475d2 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -33,6 +33,8 @@
 namespace os {
 namespace statsd {
 
+const ConfigKey kConfigKey(0, "test");
+
 TEST(GaugeMetricProducerTest, TestWithCondition) {
     int64_t bucketStartTimeNs = 10000000000;
     int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
@@ -148,7 +150,7 @@
     alert.set_metric_name("1");
     alert.set_trigger_if_sum_gt(25);
     alert.set_number_of_buckets(2);
-    sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert);
+    sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey);
     gaugeProducer.addAnomalyTracker(anomalyTracker);
 
     std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1);
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 5d47437..4e5e0d6 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -37,18 +37,19 @@
 namespace statsd {
 
 const ConfigKey kConfigKey(0, "test");
+const string eventKey = "event";
 
 TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     ConditionKey key1;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs,
-                               bucketSizeNs, {}, buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs,
+                               bucketSizeNs, {});
 
     tracker.noteStart("1", true, bucketStartTimeNs, key1);
     // Event starts again. This would not change anything as it already starts.
@@ -60,50 +61,53 @@
     tracker.noteStart("2", true, bucketStartTimeNs + 20, key1);
     tracker.noteStop("2", bucketStartTimeNs + 40, false /*stop all*/);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(20ULL, buckets[0].mDuration);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(20ULL, buckets[eventKey][0].mDuration);
 }
 
 TEST(MaxDurationTrackerTest, TestStopAll) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     ConditionKey key1;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs,
-                               bucketSizeNs, {}, buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs,
+                               bucketSizeNs, {});
 
     tracker.noteStart("1", true, bucketStartTimeNs + 1, key1);
 
     // Another event starts in this bucket.
     tracker.noteStart("2", true, bucketStartTimeNs + 20, key1);
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40, &buckets);
     tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40);
     EXPECT_TRUE(tracker.mInfos.empty());
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40);
-    EXPECT_EQ(2u, buckets.size());
-    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
-    EXPECT_EQ(40ULL, buckets[1].mDuration);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
+
+    tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40, &buckets);
+    EXPECT_EQ(2u, buckets[eventKey].size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
+    EXPECT_EQ(40ULL, buckets[eventKey][1].mDuration);
 }
 
 TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     ConditionKey key1;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs,
-                               bucketSizeNs, {}, buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs,
+                               bucketSizeNs, {});
 
     // The event starts.
     tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
@@ -112,25 +116,26 @@
     tracker.noteStart("", true, bucketStartTimeNs + bucketSizeNs + 1, key1);
 
     // The event stops at early 4th bucket.
-    tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20);
+    tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20, &buckets);
     tracker.noteStop("", bucketStartTimeNs + (3 * bucketSizeNs) + 20, false /*stop all*/);
-    EXPECT_EQ(3u, buckets.size());
-    EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration);
-    EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[1].mDuration);
-    EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[2].mDuration);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(3u, buckets[eventKey].size());
+    EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[eventKey][0].mDuration);
+    EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[eventKey][1].mDuration);
+    EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[eventKey][2].mDuration);
 }
 
 TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     ConditionKey key1;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, true, bucketStartTimeNs,
-                               bucketSizeNs, {}, buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, true, bucketStartTimeNs,
+                               bucketSizeNs, {});
 
     // 2 starts
     tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
@@ -138,20 +143,21 @@
     // one stop
     tracker.noteStop("", bucketStartTimeNs + 20, false /*stop all*/);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1);
+    tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1, &buckets);
 
-    EXPECT_EQ(2u, buckets.size());
-    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
-    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(2u, buckets[eventKey].size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
+    EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
 
     // real stop now.
     tracker.noteStop("", bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
-    tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1);
+    tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1, &buckets);
 
-    EXPECT_EQ(3u, buckets.size());
-    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
-    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
-    EXPECT_EQ(5ULL, buckets[2].mDuration);
+    EXPECT_EQ(3u, buckets[eventKey].size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
+    EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
+    EXPECT_EQ(5ULL, buckets[eventKey][2].mDuration);
 }
 
 TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
@@ -163,15 +169,15 @@
     EXPECT_CALL(*wizard, query(_, key1))  // #4
             .WillOnce(Return(ConditionState::kFalse));
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     int64_t durationTimeNs = 2 * 1000;
 
-    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
-                               bucketSizeNs, {}, buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false, bucketStartTimeNs,
+                               bucketSizeNs, {});
     EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
@@ -180,9 +186,10 @@
 
     tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(5ULL, buckets[0].mDuration);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(5ULL, buckets[eventKey][0].mDuration);
 }
 
 TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
@@ -193,7 +200,7 @@
     alert.set_number_of_buckets(2);
     alert.set_refractory_period_secs(1);
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     ConditionKey key1;
     key1["APP_BACKGROUND"] = "1:maps|";
@@ -201,9 +208,9 @@
     uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
-    sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert);
-    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, true, bucketStartTimeNs,
-                               bucketSizeNs, {anomalyTracker}, buckets);
+    sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey);
+    MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, true, bucketStartTimeNs,
+                               bucketSizeNs, {anomalyTracker});
 
     tracker.noteStart("1", true, eventStartTimeNs, key1);
     tracker.noteStop("1", eventStartTimeNs + 10, false);
@@ -211,7 +218,7 @@
     EXPECT_EQ(10LL, tracker.mDuration);
 
     tracker.noteStart("2", true, eventStartTimeNs + 20, key1);
-    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC);
+    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, &buckets);
     tracker.noteStop("2", eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, false);
     EXPECT_EQ((long long)(4 * NS_PER_SEC + 1LL), tracker.mDuration);
     EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs,
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 6913c81..99d3e05 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -35,6 +35,7 @@
 namespace statsd {
 
 const ConfigKey kConfigKey(0, "test");
+const string eventKey = "event";
 
 TEST(OringDurationTrackerTest, TestDurationOverlap) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -42,15 +43,15 @@
     ConditionKey key1;
     key1["APP_BACKGROUND"] = "1:maps|";
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
-                                 bucketSizeNs, {}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false,
+                                 bucketStartTimeNs, bucketSizeNs, {});
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -58,9 +59,11 @@
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
 
     tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
-    tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(durationTimeNs, buckets[0].mDuration);
+    tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(durationTimeNs, buckets[eventKey][0].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestDurationNested) {
@@ -69,14 +72,14 @@
     ConditionKey key1;
     key1["APP_BACKGROUND"] = "1:maps|";
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, {}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {});
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
@@ -84,9 +87,10 @@
     tracker.noteStop("2:maps", eventStartTimeNs + 2000, false);
     tracker.noteStop("2:maps", eventStartTimeNs + 2003, false);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(2003ULL, buckets[0].mDuration);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(2003ULL, buckets[eventKey][0].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestStopAll) {
@@ -95,23 +99,24 @@
     ConditionKey key1;
     key1["APP_BACKGROUND"] = "1:maps|";
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, {}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {});
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     tracker.noteStart("3:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
 
     tracker.noteStopAll(eventStartTimeNs + 2003);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(2003ULL, buckets[0].mDuration);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(2003ULL, buckets[eventKey][0].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
@@ -120,32 +125,33 @@
     ConditionKey key1;
     key1["APP_BACKGROUND"] = "1:maps|";
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, {}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {});
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
-    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs);
+    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs, &buckets);
     tracker.noteStart("2:maps", true, eventStartTimeNs + 2 * bucketSizeNs, key1);
     EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime);
 
-    EXPECT_EQ(2u, buckets.size());
-    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
-    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+    EXPECT_EQ(2u, buckets[eventKey].size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
+    EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
 
     tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + 10, false);
     tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + 12, false);
-    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12);
-    EXPECT_EQ(2u, buckets.size());
-    EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration);
-    EXPECT_EQ(bucketSizeNs, buckets[1].mDuration);
+    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(2u, buckets[eventKey].size());
+    EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
+    EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestDurationConditionChange) {
@@ -157,15 +163,15 @@
     EXPECT_CALL(*wizard, query(_, key1))  // #4
             .WillOnce(Return(ConditionState::kFalse));
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
-                                 bucketSizeNs, {}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false,
+                                 bucketStartTimeNs, bucketSizeNs, {});
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
 
@@ -173,9 +179,10 @@
 
     tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(5ULL, buckets[0].mDuration);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(5ULL, buckets[eventKey][0].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
@@ -189,15 +196,15 @@
             .WillOnce(Return(ConditionState::kFalse))
             .WillOnce(Return(ConditionState::kTrue));
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
-                                 bucketSizeNs, {}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false,
+                                 bucketStartTimeNs, bucketSizeNs, {});
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     // condition to false; record duration 5n
@@ -207,9 +214,10 @@
     // 2nd duration: 1000ns
     tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(1005ULL, buckets[0].mDuration);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(1005ULL, buckets[eventKey][0].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
@@ -221,14 +229,14 @@
     EXPECT_CALL(*wizard, query(_, key1))  // #4
             .WillOnce(Return(ConditionState::kFalse));
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, {}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {});
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     tracker.noteStart("2:maps", true, eventStartTimeNs + 2, key1);
@@ -239,9 +247,10 @@
 
     tracker.noteStop("2:maps", eventStartTimeNs + 2003, false);
 
-    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1);
-    EXPECT_EQ(1u, buckets.size());
-    EXPECT_EQ(15ULL, buckets[0].mDuration);
+    tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
+    EXPECT_EQ(15ULL, buckets[eventKey][0].mDuration);
 }
 
 TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
@@ -252,7 +261,7 @@
     alert.set_number_of_buckets(2);
     alert.set_refractory_period_secs(1);
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     ConditionKey key1;
     key1["APP_BACKGROUND"] = "1:maps|";
@@ -260,9 +269,9 @@
     uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
-    sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert);
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, {anomalyTracker}, buckets);
+    sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {anomalyTracker});
 
     // Nothing in the past bucket.
     tracker.noteStart("", true, eventStartTimeNs, key1);
@@ -270,7 +279,7 @@
               tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
 
     tracker.noteStop("", eventStartTimeNs + 3, false);
-    EXPECT_EQ(0u, buckets.size());
+    EXPECT_EQ(0u, buckets[eventKey].size());
 
     uint64_t event1StartTimeNs = eventStartTimeNs + 10;
     tracker.noteStart("1", true, event1StartTimeNs, key1);
@@ -279,11 +288,13 @@
               tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs));
 
     uint64_t event1StopTimeNs = eventStartTimeNs + bucketSizeNs + 10;
-    tracker.flushIfNeeded(event1StopTimeNs);
+    tracker.flushIfNeeded(event1StopTimeNs, &buckets);
     tracker.noteStop("1", event1StopTimeNs, false);
-    EXPECT_EQ(1u, buckets.size());
+
+    EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
+    EXPECT_EQ(1u, buckets[eventKey].size());
     EXPECT_EQ(3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10,
-              buckets[0].mDuration);
+              buckets[eventKey][0].mDuration);
 
     const int64_t bucket0Duration = 3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10;
     const int64_t bucket1Duration = eventStartTimeNs + 10 - bucketStartTimeNs;
@@ -312,7 +323,7 @@
     alert.set_number_of_buckets(2);
     alert.set_refractory_period_secs(1);
 
-    vector<DurationBucket> buckets;
+    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     ConditionKey key1;
     key1["APP_BACKGROUND"] = "1:maps|";
@@ -320,9 +331,9 @@
     uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
-    sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert);
-    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true /*nesting*/,
-                                 bucketStartTimeNs, bucketSizeNs, {anomalyTracker}, buckets);
+    sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey);
+    OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true /*nesting*/,
+                                 bucketStartTimeNs, bucketSizeNs, {anomalyTracker});
 
     tracker.noteStart("", true, eventStartTimeNs, key1);
     tracker.noteStop("", eventStartTimeNs + 10, false);
@@ -336,7 +347,7 @@
     EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
     EXPECT_EQ((long long)(51ULL * NS_PER_SEC),
               (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC));
-    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25);
+    tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25, &buckets);
     tracker.noteStop("", eventStartTimeNs + 2 * bucketSizeNs + 25, false);
     EXPECT_EQ(anomalyTracker->getSumOverPastBuckets("event"), (long long)(bucketSizeNs));
     EXPECT_EQ((long long)(eventStartTimeNs + 2 * bucketSizeNs + 25),
diff --git a/cmds/statsd/tools/loadtest/AndroidManifest.xml b/cmds/statsd/tools/loadtest/AndroidManifest.xml
index d74c707..2bf8ca9 100644
--- a/cmds/statsd/tools/loadtest/AndroidManifest.xml
+++ b/cmds/statsd/tools/loadtest/AndroidManifest.xml
@@ -39,5 +39,6 @@
     </activity>
     <receiver android:name=".LoadtestActivity$PusherAlarmReceiver" />
     <receiver android:name=".LoadtestActivity$StopperAlarmReceiver"/>
+    <receiver android:name=".PerfData$PerfAlarmReceiver"/>
   </application>
 </manifest>
diff --git a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml b/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
index 82964ab..1e28f67 100644
--- a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
+++ b/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml
@@ -166,12 +166,6 @@
             android:layout_height="wrap_content"
             android:text="@string/display_output"
             android:textSize="30dp"/>
-        <Button
-            android:id="@+id/display_perf"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/display_perf"
-            android:textSize="30dp"/>
 
         <Space
             android:layout_width="1dp"
@@ -179,6 +173,7 @@
 
         <TextView
             android:id="@+id/report_text"
+            android:gravity="center"
             android:layout_width="match_parent"
             android:layout_height="wrap_content" />
 
diff --git a/cmds/statsd/tools/loadtest/res/values/strings.xml b/cmds/statsd/tools/loadtest/res/values/strings.xml
index 9913503..cb38298 100644
--- a/cmds/statsd/tools/loadtest/res/values/strings.xml
+++ b/cmds/statsd/tools/loadtest/res/values/strings.xml
@@ -21,7 +21,6 @@
     <string name="bucket_label">bucket size (mins):&#160;</string>
     <string name="burst_label">burst:&#160;</string>
     <string name="display_output">Show metrics data</string>
-    <string name="display_perf">Show perf data</string>
     <string name="placebo">placebo</string>
     <string name="period_label">logging period (secs):&#160;</string>
     <string name="replication_label">metric replication:&#160;</string>
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java
new file mode 100644
index 0000000..d2ff892
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java
@@ -0,0 +1,53 @@
+/*
+ * 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.statsd.loadtest;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.Log;
+import java.text.ParseException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class BatteryDataRecorder extends PerfDataRecorder {
+    private static final String TAG = "loadtest.BatteryDataRecorder";
+    private static final String DUMP_FILENAME = TAG + "_dump.tmp";
+
+    public BatteryDataRecorder(boolean placebo, int replication, long bucketMins, long periodSecs,
+        int burst) {
+        super(placebo, replication, bucketMins, periodSecs, burst);
+    }
+
+    @Override
+    public void startRecording(Context context) {
+        // Reset batterystats.
+        runDumpsysStats(context, DUMP_FILENAME, "batterystats", "--reset");
+    }
+
+    @Override
+    public void onAlarm(Context context) {
+        // Nothing to do as for battery, the whole data is in the final dumpsys call.
+    }
+
+    @Override
+    public void stopRecording(Context context) {
+        StringBuilder sb = new StringBuilder();
+        // Don't use --checkin.
+        runDumpsysStats(context, DUMP_FILENAME, "batterystats");
+        readDumpData(context, DUMP_FILENAME, new BatteryStatsParser(), sb);
+        writeData(context, "battery_", "time,battery_level", sb);
+    }
+}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java
index 96e6bef..203d97a 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java
@@ -21,13 +21,13 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-public class BatteryStatsParser {
+public class BatteryStatsParser implements PerfParser {
 
     private static final Pattern LINE_PATTERN =
         Pattern.compile("\\s*\\+*(\\S*)\\s\\(\\d+\\)\\s(\\d\\d\\d)\\s.*");
     private static final Pattern TIME_PATTERN =
         Pattern.compile("(\\d+)?(h)?(\\d+)?(m)?(\\d+)?(s)?(\\d+)?(ms)?");
-    private static final String TAG = "BatteryStatsParser";
+    private static final String TAG = "loadtest.BatteryStatsParser";
 
     private boolean mHistoryStarted;
     private boolean mHistoryEnded;
@@ -35,6 +35,7 @@
     public BatteryStatsParser() {
     }
 
+    @Override
     @Nullable
     public String parseLine(String line) {
         if (mHistoryEnded) {
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
index e7bb539..0d890fb 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
@@ -18,20 +18,19 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.util.Log;
-import android.util.StatsLog;
 
 import com.android.internal.os.StatsdConfigProto.Bucket;
-import com.android.internal.os.StatsdConfigProto.Condition;
+import com.android.internal.os.StatsdConfigProto.Predicate;
 import com.android.internal.os.StatsdConfigProto.CountMetric;
 import com.android.internal.os.StatsdConfigProto.DurationMetric;
-import com.android.internal.os.StatsdConfigProto.EventConditionLink;
+import com.android.internal.os.StatsdConfigProto.MetricConditionLink;
 import com.android.internal.os.StatsdConfigProto.EventMetric;
 import com.android.internal.os.StatsdConfigProto.GaugeMetric;
 import com.android.internal.os.StatsdConfigProto.ValueMetric;
 import com.android.internal.os.StatsdConfigProto.KeyMatcher;
 import com.android.internal.os.StatsdConfigProto.KeyValueMatcher;
 import com.android.internal.os.StatsdConfigProto.AtomMatcher;
-import com.android.internal.os.StatsdConfigProto.SimpleCondition;
+import com.android.internal.os.StatsdConfigProto.SimplePredicate;
 import com.android.internal.os.StatsdConfigProto.StatsdConfig;
 
 import java.io.InputStream;
@@ -45,7 +44,7 @@
 public class ConfigFactory {
     public static final String CONFIG_NAME = "LOADTEST";
 
-    private static final String TAG = "ConfigFactory";
+    private static final String TAG = "loadtest.ConfigFactory";
 
     private final StatsdConfig mTemplate;
 
@@ -113,9 +112,9 @@
                 addValueMetric(metric, i, bucketMillis, config);
                 numMetrics++;
             }
-            // conditions
-            for (Condition condition : mTemplate.getConditionList()) {
-              addCondition(condition, i, config);
+            // predicates
+            for (Predicate predicate : mTemplate.getPredicateList()) {
+              addPredicate(predicate, i, config);
             }
             // matchers
             for (AtomMatcher matcher : mTemplate.getAtomMatcherList()) {
@@ -130,13 +129,13 @@
     }
 
     /**
-     * Creates {@link EventConditionLink}s that are identical to the one passed to this method,
+     * Creates {@link MetricConditionLink}s that are identical to the one passed to this method,
      * except that the names are appended with the provided suffix.
      */
-    private List<EventConditionLink> getLinks(
-        List<EventConditionLink> links, int suffix) {
-        List<EventConditionLink> newLinks = new ArrayList();
-        for (EventConditionLink link : links) {
+    private List<MetricConditionLink> getLinks(
+        List<MetricConditionLink> links, int suffix) {
+        List<MetricConditionLink> newLinks = new ArrayList();
+        for (MetricConditionLink link : links) {
             newLinks.add(link.toBuilder()
                 .setCondition(link.getCondition() + suffix)
                 .build());
@@ -156,7 +155,7 @@
             metric.setCondition(template.getCondition() + suffix);
         }
         if (template.getLinksCount() > 0) {
-            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
             metric.clearLinks();
             metric.addAllLinks(links);
         }
@@ -182,7 +181,7 @@
             metric.setCondition(template.getCondition() + suffix);
         }
         if (template.getLinksCount() > 0) {
-            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
             metric.clearLinks();
             metric.addAllLinks(links);
         }
@@ -203,7 +202,7 @@
             metric.setCondition(template.getCondition() + suffix);
         }
         if (template.getLinksCount() > 0) {
-            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
             metric.clearLinks();
             metric.addAllLinks(links);
         }
@@ -224,7 +223,7 @@
             metric.setCondition(template.getCondition() + suffix);
         }
         if (template.getLinksCount() > 0) {
-            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
             metric.clearLinks();
             metric.addAllLinks(links);
         }
@@ -245,7 +244,7 @@
             metric.setCondition(template.getCondition() + suffix);
         }
         if (template.getLinksCount() > 0) {
-            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
             metric.clearLinks();
             metric.addAllLinks(links);
         }
@@ -254,30 +253,30 @@
     }
 
     /**
-     * Creates a {@link Condition} based on the template. Makes sure that all names
-     * are appended with the provided suffix. Then adds that condition to the config.
+     * Creates a {@link Predicate} based on the template. Makes sure that all names
+     * are appended with the provided suffix. Then adds that predicate to the config.
      */
-    private void addCondition(Condition template, int suffix, StatsdConfig.Builder config) {
-        Condition.Builder condition = template.toBuilder()
+    private void addPredicate(Predicate template, int suffix, StatsdConfig.Builder config) {
+        Predicate.Builder predicate = template.toBuilder()
             .setName(template.getName() + suffix);
         if (template.hasCombination()) {
-            Condition.Combination.Builder cb = template.getCombination().toBuilder()
-                .clearCondition();
-            for (String child : template.getCombination().getConditionList()) {
-                cb.addCondition(child + suffix);
+            Predicate.Combination.Builder cb = template.getCombination().toBuilder()
+                .clearPredicate();
+            for (String child : template.getCombination().getPredicateList()) {
+                cb.addPredicate(child + suffix);
             }
-            condition.setCombination(cb.build());
+            predicate.setCombination(cb.build());
         }
-        if (template.hasSimpleCondition()) {
-            SimpleCondition.Builder sc = template.getSimpleCondition().toBuilder()
-                .setStart(template.getSimpleCondition().getStart() + suffix)
-                .setStop(template.getSimpleCondition().getStop() + suffix);
-            if (template.getSimpleCondition().hasStopAll()) {
-                sc.setStopAll(template.getSimpleCondition().getStopAll() + suffix);
+        if (template.hasSimplePredicate()) {
+            SimplePredicate.Builder sc = template.getSimplePredicate().toBuilder()
+                .setStart(template.getSimplePredicate().getStart() + suffix)
+                .setStop(template.getSimplePredicate().getStop() + suffix);
+            if (template.getSimplePredicate().hasStopAll()) {
+                sc.setStopAll(template.getSimplePredicate().getStopAll() + suffix);
             }
-            condition.setSimpleCondition(sc.build());
+            predicate.setSimplePredicate(sc.build());
         }
-        config.addCondition(condition);
+        config.addPredicate(predicate);
     }
 
     /**
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 3ae85a7..522dea6 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
@@ -65,8 +65,9 @@
 public class LoadtestActivity extends Activity {
 
     private static final String TAG = "StatsdLoadtest";
-    private static final String TYPE = "type";
-    private static final String ALARM = "push_alarm";
+    public static final String TYPE = "type";
+    private static final String PUSH_ALARM = "push_alarm";
+    public static final String PERF_ALARM = "perf_alarm";
     private static final String START = "start";
     private static final String STOP = "stop";
 
@@ -74,7 +75,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             Intent activityIntent = new Intent(context, LoadtestActivity.class);
-            activityIntent.putExtra(TYPE, ALARM);
+            activityIntent.putExtra(TYPE, PUSH_ALARM);
             context.startActivity(activityIntent);
          }
     }
@@ -105,6 +106,9 @@
     private TextView mReportText;
     private CheckBox mPlaceboCheckBox;
 
+    /** When the load test started. */
+    private long mStartedTimeMillis;
+
     /** For measuring perf data. */
     private PerfData mPerfData;
 
@@ -180,7 +184,7 @@
             @Override
             public void onClick(View view) {
                 if (mStarted) {
-                    stopLoadtest(true);
+                    stopLoadtest();
                 } else {
                     startLoadtest();
                 }
@@ -194,20 +198,11 @@
             }
         });
 
-        findViewById(R.id.display_perf).setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                mPerfData.publishData(LoadtestActivity.this, mPlacebo, mReplication, mBucketMins,
-                    mPeriodSecs, mBurst);
-            }
-        });
-
         mAlarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
         mStatsManager = (StatsManager) getSystemService("stats");
         mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
         mFactory = new ConfigFactory(this);
-        mPerfData = new PerfData();
-        stopLoadtest(false);
+        stopLoadtest();
         mReportText.setText("");
     }
 
@@ -218,14 +213,17 @@
             return;
         }
         switch (type) {
-            case ALARM:
-              onAlarm(intent);
-              break;
+            case PERF_ALARM:
+                onPerfAlarm();
+                break;
+            case PUSH_ALARM:
+                onAlarm();
+                break;
             case START:
                 startLoadtest();
                 break;
-          case STOP:
-                stopLoadtest(true);
+            case STOP:
+                stopLoadtest();
                 break;
             default:
                 throw new IllegalArgumentException("Unknown type: " + type);
@@ -235,12 +233,23 @@
     @Override
     public void onDestroy() {
         Log.d(TAG, "Destroying");
-        stopLoadtest(false);
+        mPerfData.onDestroy();
+        stopLoadtest();
         clearConfigs();
         super.onDestroy();
     }
 
-    private void onAlarm(Intent intent) {
+    private void onPerfAlarm() {
+        if (mPerfData != null) {
+            mPerfData.onAlarm(this);
+        }
+        // Piggy-back on that alarm to show the elapsed time.
+        long elapsedTimeMins = (long) Math.floor(
+            (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
+        mReportText.setText("Loadtest in progress. Elapsed time = " + elapsedTimeMins + " min(s)");
+    }
+
+    private void onAlarm() {
         Log.d(TAG, "ON ALARM");
 
         // Set the next task.
@@ -259,7 +268,7 @@
     /** Schedules the next cycle of pushing atoms into logd. */
     private void scheduleNext() {
         Intent intent = new Intent(this, PusherAlarmReceiver.class);
-        intent.putExtra(TYPE, ALARM);
+        intent.putExtra(TYPE, PUSH_ALARM);
         mPushPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
         long nextTime =  SystemClock.elapsedRealtime() + mPeriodSecs * 1000;
         mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mPushPendingIntent);
@@ -271,7 +280,7 @@
         }
 
         // Clean up the state.
-        stopLoadtest(false);
+        stopLoadtest();
 
         // Prepare to push a sequence of atoms to logd.
         mPusher = new SequencePusher(mBurst, mPlacebo);
@@ -291,15 +300,17 @@
         // Log atoms.
         scheduleNext();
 
-        // Reset battery data.
-        mPerfData.resetData(this);
+        // Start tracking performance.
+        mPerfData = new PerfData(this, mPlacebo, mReplication, mBucketMins, mPeriodSecs, mBurst);
+        mPerfData.startRecording(this);
 
-        mReportText.setText("");
+        mReportText.setText("Loadtest in progress.");
+        mStartedTimeMillis = SystemClock.elapsedRealtime();
 
         updateStarted(true);
     }
 
-    private synchronized void stopLoadtest(boolean publishPerfData) {
+    private synchronized void stopLoadtest() {
         if (mPushPendingIntent != null) {
             Log.d(TAG, "Canceling pre-existing push alarm");
             mAlarmMgr.cancel(mPushPendingIntent);
@@ -314,12 +325,17 @@
             mWakeLock.release();
             mWakeLock = null;
         }
-        fetchAndDisplayData();
+        if (mPerfData != null) {
+            mPerfData.stopRecording(this);
+            mPerfData.onDestroy();
+            mPerfData = null;
+        }
+
+        long elapsedTimeMins = (long) Math.floor(
+            (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000);
+        mReportText.setText("Loadtest ended. Elapsed time = " + elapsedTimeMins + " min(s)");
         clearConfigs();
         updateStarted(false);
-        if (publishPerfData) {
-            mPerfData.publishData(this, mPlacebo, mReplication, mBucketMins, mPeriodSecs, mBurst);
-        }
     }
 
     private synchronized void updateStarted(boolean started) {
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemInfoParser.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemInfoParser.java
new file mode 100644
index 0000000..01eebf2
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemInfoParser.java
@@ -0,0 +1,69 @@
+/*
+ * 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.statsd.loadtest;
+
+import android.annotation.Nullable;
+import android.os.SystemClock;
+import android.util.Log;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Parses PSS info from dumpsys meminfo */
+public class MemInfoParser implements PerfParser {
+
+    private static final Pattern LINE_PATTERN =
+        Pattern.compile("\\s*(\\d*,*\\d*)K:\\s(\\S*)\\s\\.*");
+    private static final String PSS_BY_PROCESS = "Total PSS by process:";
+    private static final String TAG = "loadtest.MemInfoParser";
+
+    private boolean mPssStarted;
+    private boolean mPssEnded;
+    private final long mStartTimeMillis;
+
+    public MemInfoParser(long startTimeMillis) {
+        mStartTimeMillis = startTimeMillis;
+    }
+
+    @Override
+    @Nullable
+    public String parseLine(String line) {
+        if (mPssEnded) {
+            return null;
+        }
+        if (!mPssStarted) {
+            if (line.contains(PSS_BY_PROCESS)) {
+                mPssStarted = true;
+            }
+            return null;
+        }
+        if (line.isEmpty()) {
+            mPssEnded = true;
+            return null;
+        }
+        Matcher lineMatcher = LINE_PATTERN.matcher(line);
+        if (lineMatcher.find() && lineMatcher.group(1) != null && lineMatcher.group(2) != null) {
+            if (lineMatcher.group(2).equals("statsd")) {
+                long timeDeltaMillis = SystemClock.elapsedRealtime() - mStartTimeMillis;
+                return timeDeltaMillis + "," + convertToPss(lineMatcher.group(1));
+            }
+        }
+        return null;
+    }
+
+    private String convertToPss(String input) {
+        return input.replace(",", "");
+    }
+}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java
new file mode 100644
index 0000000..d82a0ea
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java
@@ -0,0 +1,51 @@
+/*
+ * 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.statsd.loadtest;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.util.Log;
+
+public class MemoryDataRecorder extends PerfDataRecorder {
+    private static final String TAG = "loadtest.MemoryDataDataRecorder";
+    private static final String DUMP_FILENAME = TAG + "_dump.tmp";
+
+    private long mStartTimeMillis;
+    private StringBuilder mSb;
+
+    public MemoryDataRecorder(boolean placebo, int replication, long bucketMins, long periodSecs,
+        int burst) {
+        super(placebo, replication, bucketMins, periodSecs, burst);
+    }
+
+    @Override
+    public void startRecording(Context context) {
+        mStartTimeMillis = SystemClock.elapsedRealtime();
+        mSb = new StringBuilder();
+    }
+
+    @Override
+    public void onAlarm(Context context) {
+      Log.d(TAG, "GOT ALARM IN MEM");
+        runDumpsysStats(context, DUMP_FILENAME, "meminfo");
+        readDumpData(context, DUMP_FILENAME, new MemInfoParser(mStartTimeMillis), mSb);
+    }
+
+    @Override
+    public void stopRecording(Context context) {
+        writeData(context, "meminfo_", "time,pss", mSb);
+    }
+}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java
index 57f85b5..81a84f5 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java
@@ -15,29 +15,14 @@
  */
 package com.android.statsd.loadtest;
 
-import android.app.Activity;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
 import android.text.Editable;
 import android.text.TextWatcher;
 import android.util.Log;
-import android.util.StatsLog;
-import android.util.StatsManager;
-import android.view.View;
-import android.widget.EditText;
 import android.widget.TextView;
-import android.widget.Toast;
-import android.os.IStatsManager;
-import android.os.ServiceManager;
-import android.view.View.OnFocusChangeListener;
 
 public abstract class NumericalWatcher implements TextWatcher {
 
-  private static final String TAG = "NumericalWatcher";
+  private static final String TAG = "loadtest.NumericalWatcher";
 
     private final TextView mTextView;
     private final int mMin;
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
index e3e23f5..4665247 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java
@@ -16,147 +16,86 @@
 package com.android.statsd.loadtest;
 
 import android.annotation.Nullable;
-import android.app.Activity;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.os.Bundle;
-import android.os.Environment;
+import android.os.SystemClock;
 import android.util.Log;
-import android.os.Debug;
 
-import java.io.BufferedReader;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
 
 /** Prints some information about the device via Dumpsys in order to evaluate health metrics. */
-public class PerfData {
+public class PerfData extends PerfDataRecorder {
 
-    private static final String TAG = "PerfData";
-    private static final String DUMP_FILENAME = TAG + "_dump.tmp";
+    private static final String TAG = "loadtest.PerfData";
 
-    public void resetData(Context context) {
-        runDumpsysStats(context, "batterystats", "--reset");
+    /** Polling period for performance snapshots like memory. */
+    private static final long POLLING_PERIOD_MILLIS = 1 * 60 * 1000;
+
+    public final static class PerfAlarmReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Intent activityIntent = new Intent(context, LoadtestActivity.class);
+            activityIntent.putExtra(LoadtestActivity.TYPE, LoadtestActivity.PERF_ALARM);
+            context.startActivity(activityIntent);
+         }
     }
 
-    public void publishData(Context context, boolean placebo, int replication, long bucketMins,
-        long periodSecs, int burst) {
-        publishBatteryData(context, placebo, replication, bucketMins, periodSecs, burst);
+    private AlarmManager mAlarmMgr;
+
+    /** Used to periodically poll some dumpsys data. */
+    private PendingIntent mPendingIntent;
+
+    private final Set<PerfDataRecorder> mRecorders;
+
+    public PerfData(Context context, boolean placebo, int replication, long bucketMins,
+        long periodSecs,  int burst) {
+        super(placebo, replication, bucketMins, periodSecs, burst);
+        mRecorders = new HashSet();
+        mRecorders.add(new BatteryDataRecorder(placebo, replication, bucketMins, periodSecs, burst));
+        mRecorders.add(new MemoryDataRecorder(placebo, replication, bucketMins, periodSecs, burst));
+        mAlarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
     }
 
-    private void publishBatteryData(Context context, boolean placebo, int replication,
-        long bucketMins, long periodSecs, int burst) {
-        // Don't use --checkin.
-        runDumpsysStats(context, "batterystats");
-        writeBatteryData(context, placebo, replication, bucketMins, periodSecs, burst);
-    }
-
-    private void runDumpsysStats(Context context, String cmd, String... args) {
-        boolean success = false;
-        // Call dumpsys Dump statistics to a file.
-        FileOutputStream fo = null;
-        try {
-            fo = context.openFileOutput(DUMP_FILENAME, Context.MODE_PRIVATE);
-            if (!Debug.dumpService(cmd, fo.getFD(), args)) {
-                Log.w(TAG, "Dumpsys failed.");
-            }
-            success = true;
-        } catch (IOException | SecurityException | NullPointerException e) {
-            // SecurityException may occur when trying to dump multi-user info.
-            // NPE can occur during dumpService  (root cause unknown).
-            throw new RuntimeException(e);
-        } finally {
-            closeQuietly(fo);
+    public void onDestroy() {
+        if (mPendingIntent != null) {
+            mAlarmMgr.cancel(mPendingIntent);
+            mPendingIntent = null;
         }
     }
 
-    private String readDumpFile(Context context) {
-        StringBuilder sb = new StringBuilder();
-        FileInputStream fi = null;
-        BufferedReader br = null;
-        try {
-            fi = context.openFileInput(DUMP_FILENAME);
-            br = new BufferedReader(new InputStreamReader(fi));
-            String line = br.readLine();
-            while (line != null) {
-                sb.append(line);
-                sb.append(System.lineSeparator());
-                line = br.readLine();
-            }
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        } finally {
-            closeQuietly(br);
-        }
-        return sb.toString();
-    }
+    @Override
+    public void startRecording(Context context) {
+        Intent intent = new Intent(context, PerfAlarmReceiver.class);
+        intent.putExtra(LoadtestActivity.TYPE, LoadtestActivity.PERF_ALARM);
+        mPendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
+        mAlarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, -1 /* now */,
+            POLLING_PERIOD_MILLIS, mPendingIntent);
 
-    private static void closeQuietly(@Nullable Closeable c) {
-        if (c != null) {
-            try {
-                c.close();
-            } catch (IOException ignore) {
-            }
+        for (PerfDataRecorder recorder : mRecorders) {
+            recorder.startRecording(context);
         }
     }
 
-    public void writeBatteryData(Context context, boolean placebo, int replication, long bucketMins,
-        long periodSecs, int burst) {
-        BatteryStatsParser parser = new BatteryStatsParser();
-        FileInputStream fi = null;
-        BufferedReader br = null;
-        String suffix = new SimpleDateFormat("YYYY_MM_dd_HH_mm_ss").format(new Date());
-        File batteryDataFile = new File(getStorageDir(), "battery_" + suffix + ".csv");
-        Log.d(TAG, "Writing battery data to " + batteryDataFile.getAbsolutePath());
-
-        FileWriter writer = null;
-        try {
-            fi = context.openFileInput(DUMP_FILENAME);
-            writer = new FileWriter(batteryDataFile);
-            writer.append("time,battery_level"
-                + getColumnName(placebo, replication, bucketMins, periodSecs, burst) + "\n");
-            br = new BufferedReader(new InputStreamReader(fi));
-            String line = br.readLine();
-            while (line != null) {
-                String recordLine = parser.parseLine(line);
-                if (recordLine != null) {
-                    writer.append(recordLine);
-                }
-                line = br.readLine();
-            }
-            writer.flush();
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        } finally {
-            closeQuietly(writer);
-            closeQuietly(br);
+    @Override
+    public void onAlarm(Context context) {
+        for (PerfDataRecorder recorder : mRecorders) {
+            recorder.onAlarm(context);
         }
     }
 
-    private File getStorageDir() {
-        File file = new File(Environment.getExternalStoragePublicDirectory(
-            Environment.DIRECTORY_DOCUMENTS), "loadtest");
-        if (!file.mkdirs()) {
-            Log.e(TAG, "Directory not created");
+    @Override
+    public void stopRecording(Context context) {
+        if (mPendingIntent != null) {
+            mAlarmMgr.cancel(mPendingIntent);
+            mPendingIntent = null;
         }
-        return file;
-    }
 
-    private String getColumnName(boolean placebo, int replication, long bucketMins, long periodSecs,
-        int burst) {
-        if (placebo) {
-            return "_placebo_p=" + periodSecs;
+        for (PerfDataRecorder recorder : mRecorders) {
+            recorder.stopRecording(context);
         }
-        return "_r=" + replication + "_bkt=" + bucketMins + "_p=" + periodSecs + "_bst=" + burst;
     }
 }
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java
new file mode 100644
index 0000000..15a8e5c
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java
@@ -0,0 +1,149 @@
+/*
+ * 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.statsd.loadtest;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Environment;
+import android.util.Log;
+import android.os.Debug;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public abstract class PerfDataRecorder {
+    private static final String TAG = "loadtest.PerfDataRecorder";
+
+    protected final String mFileSuffix;
+    protected final String mColumnSuffix;
+
+    protected PerfDataRecorder(boolean placebo, int replication, long bucketMins, long periodSecs,
+        int burst) {
+        mFileSuffix = new SimpleDateFormat("YYYY_MM_dd_HH_mm_ss").format(new Date());
+        mColumnSuffix = getColumnSuffix(placebo, replication, bucketMins, periodSecs, burst);
+    }
+
+    /** Starts recording performance data. */
+    public abstract void startRecording(Context context);
+
+    /** Called periodically. For the recorder to sample data, if needed. */
+    public abstract void onAlarm(Context context);
+
+    /** Stops recording performance data, and writes it to disk. */
+    public abstract void stopRecording(Context context);
+
+    /** Runs the dumpsys command. */
+    protected void runDumpsysStats(Context context, String dumpFilename, String cmd,
+        String... args) {
+        boolean success = false;
+        // Call dumpsys Dump statistics to a file.
+        FileOutputStream fo = null;
+        try {
+            fo = context.openFileOutput(dumpFilename, Context.MODE_PRIVATE);
+            if (!Debug.dumpService(cmd, fo.getFD(), args)) {
+                Log.w(TAG, "Dumpsys failed.");
+            }
+            success = true;
+        } catch (IOException | SecurityException | NullPointerException e) {
+            // SecurityException may occur when trying to dump multi-user info.
+            // NPE can occur during dumpService  (root cause unknown).
+            throw new RuntimeException(e);
+        } finally {
+            closeQuietly(fo);
+        }
+    }
+
+    /**
+     * Reads a text file and parses each line, one by one. The result of the parsing is stored
+     * in the passed {@link StringBuffer}.
+     */
+    protected void readDumpData(Context context, String dumpFilename, PerfParser parser,
+        StringBuilder sb) {
+        FileInputStream fi = null;
+        BufferedReader br = null;
+        try {
+            fi = context.openFileInput(dumpFilename);
+            br = new BufferedReader(new InputStreamReader(fi));
+            String line = br.readLine();
+            while (line != null) {
+                String recordLine = parser.parseLine(line);
+                if (recordLine != null) {
+                  sb.append(recordLine).append('\n');
+                }
+                line = br.readLine();
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            closeQuietly(br);
+        }
+    }
+
+    /** Writes CSV data to a file. */
+    protected void writeData(Context context, String filePrefix, String columnPrefix,
+        StringBuilder sb) {
+        File dataFile = new File(getStorageDir(), filePrefix + mFileSuffix + ".csv");
+
+        FileWriter writer = null;
+        try {
+            writer = new FileWriter(dataFile);
+            writer.append(columnPrefix + mColumnSuffix + "\n");
+            writer.append(sb.toString());
+            writer.flush();
+            Log.d(TAG, "Finished writing data at " + dataFile.getAbsolutePath());
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            closeQuietly(writer);
+        }
+    }
+
+    /** Gets the suffix to use in the column name for perf data. */
+    private String getColumnSuffix(boolean placebo, int replication, long bucketMins,
+        long periodSecs, int burst) {
+        if (placebo) {
+            return "_placebo_p=" + periodSecs;
+        }
+        return "_r=" + replication + "_bkt=" + bucketMins + "_p=" + periodSecs + "_bst=" + burst;
+    }
+
+
+    private File getStorageDir() {
+        File file = new File(Environment.getExternalStoragePublicDirectory(
+            Environment.DIRECTORY_DOCUMENTS), "loadtest");
+        if (!file.mkdirs()) {
+            Log.e(TAG, "Directory not created");
+        }
+        return file;
+    }
+
+    private void closeQuietly(@Nullable Closeable c) {
+        if (c != null) {
+            try {
+                c.close();
+            } catch (IOException ignore) {
+            }
+        }
+    }
+}
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java
new file mode 100644
index 0000000..e000918
--- /dev/null
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java
@@ -0,0 +1,27 @@
+/*
+ * 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.statsd.loadtest;
+
+import android.annotation.Nullable;
+
+public interface PerfParser {
+
+  /**
+   * Parses one line of the dumpsys output, and returns a string to write to the data file,
+   * or null if no string should be written.
+   */
+  @Nullable String parseLine(String line);
+}
diff --git a/cmds/uiautomator/instrumentation/Android.mk b/cmds/uiautomator/instrumentation/Android.mk
index e6cbdb4..008bb93 100644
--- a/cmds/uiautomator/instrumentation/Android.mk
+++ b/cmds/uiautomator/instrumentation/Android.mk
@@ -22,7 +22,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, testrunner-src) \
     $(call all-java-files-under, ../library/core-src)
 LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := legacy-android-test junit
+LOCAL_STATIC_JAVA_LIBRARIES := junit
 LOCAL_MODULE := uiautomator-instrumentation
 # TODO: change this to 18 when it's available
 LOCAL_SDK_VERSION := current
diff --git a/cmds/uiautomator/library/Android.mk b/cmds/uiautomator/library/Android.mk
index 4bf856f..22cffe6 100644
--- a/cmds/uiautomator/library/Android.mk
+++ b/cmds/uiautomator/library/Android.mk
@@ -28,8 +28,8 @@
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(uiautomator.core_src_files)
 LOCAL_MODULE := uiautomator.core
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
+LOCAL_STATIC_JAVA_LIBRARIES := junit
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 ###############################################
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index e0d60cd..06a9b06 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -16,8 +16,6 @@
 
 package android.accessibilityservice;
 
-import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
-
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -49,6 +47,8 @@
 import java.util.Collections;
 import java.util.List;
 
+import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
+
 /**
  * This class describes an {@link AccessibilityService}. The system notifies an
  * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
@@ -554,7 +554,7 @@
     }
 
     /**
-     * Updates the properties that an AccessibilityService can change dynamically.
+     * Updates the properties that an AccessibilitySerivice can change dynamically.
      *
      * @param other The info from which to update the properties.
      *
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 03a3631..aa099eb 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7070,7 +7070,13 @@
         mActivityTransitionState.enterReady(this);
     }
 
-    final void performRestart() {
+    /**
+     * Restart the activity.
+     * @param start Indicates whether the activity should also be started after restart.
+     *              The option to not start immediately is needed in case a transaction with
+     *              multiple lifecycle transitions is in progress.
+     */
+    final void performRestart(boolean start) {
         mCanEnterPictureInPicture = true;
         mFragments.noteStateNotSaved();
 
@@ -7108,12 +7114,14 @@
                     "Activity " + mComponent.toShortString() +
                     " did not call through to super.onRestart()");
             }
-            performStart();
+            if (start) {
+                performStart();
+            }
         }
     }
 
     final void performResume() {
-        performRestart();
+        performRestart(true /* start */);
 
         mFragments.execPendingActions();
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b84833b..20213199 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -16,6 +16,13 @@
 
 package android.app;
 
+import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
 import static android.view.Display.INVALID_DISPLAY;
 
 import android.annotation.NonNull;
@@ -23,8 +30,12 @@
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.app.backup.BackupAgent;
+import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
 import android.app.servertransaction.ActivityResultItem;
 import android.app.servertransaction.ClientTransaction;
+import android.app.servertransaction.PendingTransactionActions;
+import android.app.servertransaction.PendingTransactionActions.StopInfo;
+import android.app.servertransaction.TransactionExecutor;
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
@@ -84,7 +95,6 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
-import android.os.TransactionTooLargeException;
 import android.os.UserHandle;
 import android.provider.BlockedNumberContract;
 import android.provider.CalendarContract;
@@ -102,7 +112,6 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.LogPrinter;
-import android.util.LogWriter;
 import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
@@ -122,6 +131,7 @@
 import android.webkit.WebView;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.content.ReferrerIntent;
 import com.android.internal.os.BinderInternal;
@@ -129,7 +139,6 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.org.conscrypt.OpenSSLSocketImpl;
 import com.android.org.conscrypt.TrustedCertificateStore;
 import com.android.server.am.proto.MemInfoProto;
@@ -190,7 +199,7 @@
     private static final boolean DEBUG_BACKUP = false;
     public static final boolean DEBUG_CONFIGURATION = false;
     private static final boolean DEBUG_SERVICE = false;
-    private static final boolean DEBUG_MEMORY_TRIM = false;
+    public static final boolean DEBUG_MEMORY_TRIM = false;
     private static final boolean DEBUG_PROVIDER = false;
     private static final boolean DEBUG_ORDER = false;
     private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
@@ -206,10 +215,6 @@
     /** Type for IActivityManager.serviceDoneExecuting: done stopping (destroying) service */
     public static final int SERVICE_DONE_EXECUTING_STOP = 2;
 
-    // Details for pausing activity.
-    private static final int USER_LEAVING = 1;
-    private static final int DONT_REPORT = 2;
-
     // Whether to invoke an activity callback after delivering new configuration.
     private static final boolean REPORT_TO_ACTIVITY = true;
 
@@ -289,12 +294,8 @@
     final ArrayList<ActivityClientRecord> mRelaunchingActivities = new ArrayList<>();
     @GuardedBy("mResourcesManager")
     Configuration mPendingConfiguration = null;
-    // Because we merge activity relaunch operations we can't depend on the ordering provided by
-    // the handler messages. We need to introduce secondary ordering mechanism, which will allow
-    // us to drop certain events, if we know that they happened before relaunch we already executed.
-    // This represents the order of receiving the request from AM.
-    @GuardedBy("mResourcesManager")
-    int mLifecycleSeq = 0;
+    // An executor that performs multi-step transactions.
+    private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this);
 
     private final ResourcesManager mResourcesManager;
 
@@ -342,8 +343,9 @@
 
     Bundle mCoreSettings = null;
 
-    static final class ActivityClientRecord {
-        IBinder token;
+    /** Activity client record, used for bookkeeping for the real {@link Activity} instance. */
+    public static final class ActivityClientRecord {
+        public IBinder token;
         int ident;
         Intent intent;
         String referrer;
@@ -355,6 +357,7 @@
         Activity parent;
         String embeddedID;
         Activity.NonConfigurationInstances lastNonConfigurationInstances;
+        // TODO(lifecycler): Use mLifecycleState instead.
         boolean paused;
         boolean stopped;
         boolean hideForNow;
@@ -371,13 +374,13 @@
 
         ActivityInfo activityInfo;
         CompatibilityInfo compatInfo;
-        LoadedApk packageInfo;
+        public LoadedApk packageInfo;
 
         List<ResultInfo> pendingResults;
         List<ReferrerIntent> pendingIntents;
 
         boolean startsNotResumed;
-        boolean isForward;
+        public final boolean isForward;
         int pendingConfigChanges;
         boolean onlyLocalRequest;
 
@@ -385,15 +388,42 @@
         WindowManager mPendingRemoveWindowManager;
         boolean mPreserveWindow;
 
-        // Set for relaunch requests, indicates the order number of the relaunch operation, so it
-        // can be compared with other lifecycle operations.
-        int relaunchSeq = 0;
+        @LifecycleState
+        private int mLifecycleState = PRE_ON_CREATE;
 
-        // Can only be accessed from the UI thread. This represents the latest processed message
-        // that is related to lifecycle events/
-        int lastProcessedSeq = 0;
+        @VisibleForTesting
+        public ActivityClientRecord() {
+            this.isForward = false;
+            init();
+        }
 
-        ActivityClientRecord() {
+        public ActivityClientRecord(IBinder token, Intent intent, int ident,
+                ActivityInfo info, Configuration overrideConfig, CompatibilityInfo compatInfo,
+                String referrer, IVoiceInteractor voiceInteractor, Bundle state,
+                PersistableBundle persistentState, List<ResultInfo> pendingResults,
+                List<ReferrerIntent> pendingNewIntents, boolean isForward,
+                ProfilerInfo profilerInfo, ClientTransactionHandler client) {
+            this.token = token;
+            this.ident = ident;
+            this.intent = intent;
+            this.referrer = referrer;
+            this.voiceInteractor = voiceInteractor;
+            this.activityInfo = info;
+            this.compatInfo = compatInfo;
+            this.state = state;
+            this.persistentState = persistentState;
+            this.pendingResults = pendingResults;
+            this.pendingIntents = pendingNewIntents;
+            this.isForward = isForward;
+            this.profilerInfo = profilerInfo;
+            this.overrideConfig = overrideConfig;
+            this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo,
+                    compatInfo);
+            init();
+        }
+
+        /** Common initializer for all constructors. */
+        private void init() {
             parent = null;
             embeddedID = null;
             paused = false;
@@ -410,6 +440,38 @@
             };
         }
 
+        /** Get the current lifecycle state. */
+        public int getLifecycleState() {
+            return mLifecycleState;
+        }
+
+        /** Update the current lifecycle state for internal bookkeeping. */
+        public void setState(@LifecycleState int newLifecycleState) {
+            mLifecycleState = newLifecycleState;
+            switch (mLifecycleState) {
+                case ON_CREATE:
+                    paused = true;
+                    stopped = true;
+                    break;
+                case ON_START:
+                    paused = true;
+                    stopped = false;
+                    break;
+                case ON_RESUME:
+                    paused = false;
+                    stopped = false;
+                    break;
+                case ON_PAUSE:
+                    paused = true;
+                    stopped = false;
+                    break;
+                case ON_STOP:
+                    paused = true;
+                    stopped = true;
+                    break;
+            }
+        }
+
         public boolean isPreHoneycomb() {
             if (activity != null) {
                 return activity.getApplicationInfo().targetSdkVersion
@@ -1315,13 +1377,6 @@
         mAppThread.updateProcessState(processState, fromIpc);
     }
 
-    @Override
-    public int getLifecycleSeq() {
-        synchronized (mResourcesManager) {
-            return mLifecycleSeq++;
-        }
-    }
-
     class H extends Handler {
         public static final int BIND_APPLICATION        = 110;
         public static final int EXIT_APPLICATION        = 111;
@@ -1586,7 +1641,7 @@
                             (String[]) ((SomeArgs) msg.obj).arg2);
                     break;
                 case EXECUTE_TRANSACTION:
-                    ((ClientTransaction) msg.obj).execute(ActivityThread.this);
+                    mTransactionExecutor.execute(((ClientTransaction) msg.obj));
                     break;
             }
             Object obj = msg.obj;
@@ -1796,6 +1851,7 @@
                 registerPackage);
     }
 
+    @Override
     public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
             CompatibilityInfo compatInfo) {
         return getPackageInfo(ai, compatInfo, null, false, true, false);
@@ -2479,13 +2535,21 @@
                     + ", comp=" + name
                     + ", token=" + token);
         }
-        return performLaunchActivity(r, null);
+        // TODO(lifecycler): Can't switch to use #handleLaunchActivity() because it will try to
+        // call #reportSizeConfigurations(), but the server might not know anything about the
+        // activity if it was launched from LocalAcvitivyManager.
+        return performLaunchActivity(r);
     }
 
     public final Activity getActivity(IBinder token) {
         return mActivities.get(token).activity;
     }
 
+    @Override
+    public ActivityClientRecord getActivityClient(IBinder token) {
+        return mActivities.get(token);
+    }
+
     public final void sendActivityResult(
             IBinder token, String id, int requestCode,
             int resultCode, Intent data) {
@@ -2553,9 +2617,8 @@
         sendMessage(H.CLEAN_UP_CONTEXT, cci);
     }
 
-    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
-        // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");
-
+    /**  Core implementation of activity launch. */
+    private Activity performLaunchActivity(ActivityClientRecord r) {
         ActivityInfo aInfo = r.activityInfo;
         if (r.packageInfo == null) {
             r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
@@ -2625,9 +2688,6 @@
                         r.embeddedID, r.lastNonConfigurationInstances, config,
                         r.referrer, r.voiceInteractor, window, r.configCallback);
 
-                if (customIntent != null) {
-                    activity.mIntent = customIntent;
-                }
                 r.lastNonConfigurationInstances = null;
                 checkAndBlockForNetworkAccess();
                 activity.mStartedActivity = false;
@@ -2648,37 +2708,8 @@
                         " did not call through to super.onCreate()");
                 }
                 r.activity = activity;
-                r.stopped = true;
-                if (!r.activity.mFinished) {
-                    activity.performStart();
-                    r.stopped = false;
-                }
-                if (!r.activity.mFinished) {
-                    if (r.isPersistable()) {
-                        if (r.state != null || r.persistentState != null) {
-                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
-                                    r.persistentState);
-                        }
-                    } else if (r.state != null) {
-                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
-                    }
-                }
-                if (!r.activity.mFinished) {
-                    activity.mCalled = false;
-                    if (r.isPersistable()) {
-                        mInstrumentation.callActivityOnPostCreate(activity, r.state,
-                                r.persistentState);
-                    } else {
-                        mInstrumentation.callActivityOnPostCreate(activity, r.state);
-                    }
-                    if (!activity.mCalled) {
-                        throw new SuperNotCalledException(
-                            "Activity " + r.intent.getComponent().toShortString() +
-                            " did not call through to super.onPostCreate()");
-                    }
-                }
             }
-            r.paused = true;
+            r.setState(ON_CREATE);
 
             mActivities.put(r.token, r);
 
@@ -2696,6 +2727,60 @@
         return activity;
     }
 
+    @Override
+    public void handleStartActivity(ActivityClientRecord r,
+            PendingTransactionActions pendingActions) {
+        final Activity activity = r.activity;
+        if (r.activity == null) {
+            // TODO(lifecycler): What do we do in this case?
+            return;
+        }
+        if (!r.stopped) {
+            throw new IllegalStateException("Can't start activity that is not stopped.");
+        }
+        if (r.activity.mFinished) {
+            // TODO(lifecycler): How can this happen?
+            return;
+        }
+
+        // Start
+        activity.performStart();
+        r.setState(ON_START);
+
+        if (pendingActions == null) {
+            // No more work to do.
+            return;
+        }
+
+        // Restore instance state
+        if (pendingActions.shouldRestoreInstanceState()) {
+            if (r.isPersistable()) {
+                if (r.state != null || r.persistentState != null) {
+                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
+                            r.persistentState);
+                }
+            } else if (r.state != null) {
+                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
+            }
+        }
+
+        // Call postOnCreate()
+        if (pendingActions.shouldCallOnPostCreate()) {
+            activity.mCalled = false;
+            if (r.isPersistable()) {
+                mInstrumentation.callActivityOnPostCreate(activity, r.state,
+                        r.persistentState);
+            } else {
+                mInstrumentation.callActivityOnPostCreate(activity, r.state);
+            }
+            if (!activity.mCalled) {
+                throw new SuperNotCalledException(
+                        "Activity " + r.intent.getComponent().toShortString()
+                                + " did not call through to super.onPostCreate()");
+            }
+        }
+    }
+
     /**
      * Checks if {@link #mNetworkBlockSeq} is {@link #INVALID_PROC_STATE_SEQ} and if so, returns
      * immediately. Otherwise, makes a blocking call to ActivityManagerService to wait for the
@@ -2742,38 +2827,12 @@
         return appContext;
     }
 
+    /**
+     * Extended implementation of activity launch. Used when server requests a launch or relaunch.
+     */
     @Override
-    public void handleLaunchActivity(IBinder token, Intent intent, int ident, ActivityInfo info,
-            Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer,
-            IVoiceInteractor voiceInteractor, Bundle state, PersistableBundle persistentState,
-            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
-            boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
-        ActivityClientRecord r = new ActivityClientRecord();
-
-        r.token = token;
-        r.ident = ident;
-        r.intent = intent;
-        r.referrer = referrer;
-        r.voiceInteractor = voiceInteractor;
-        r.activityInfo = info;
-        r.compatInfo = compatInfo;
-        r.state = state;
-        r.persistentState = persistentState;
-
-        r.pendingResults = pendingResults;
-        r.pendingIntents = pendingNewIntents;
-
-        r.startsNotResumed = notResumed;
-        r.isForward = isForward;
-
-        r.profilerInfo = profilerInfo;
-
-        r.overrideConfig = overrideConfig;
-        r.packageInfo = getPackageInfoNoCheck(r.activityInfo.applicationInfo, r.compatInfo);
-        handleLaunchActivity(r, null /* customIntent */, "LAUNCH_ACTIVITY");
-    }
-
-    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
+    public Activity handleLaunchActivity(ActivityClientRecord r,
+            PendingTransactionActions pendingActions) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
         unscheduleGcIdler();
@@ -2796,44 +2855,28 @@
         }
         WindowManagerGlobal.initialize();
 
-        Activity a = performLaunchActivity(r, customIntent);
+        final Activity a = performLaunchActivity(r);
 
         if (a != null) {
             r.createdConfig = new Configuration(mConfiguration);
             reportSizeConfigurations(r);
-            Bundle oldState = r.state;
-            handleResumeActivity(r.token, false, r.isForward,
-                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
-
-            if (!r.activity.mFinished && r.startsNotResumed) {
-                // The activity manager actually wants this one to start out paused, because it
-                // needs to be visible but isn't in the foreground. We accomplish this by going
-                // through the normal startup (because activities expect to go through onResume()
-                // the first time they run, before their window is displayed), and then pausing it.
-                // However, in this case we do -not- need to do the full pause cycle (of freezing
-                // and such) because the activity manager assumes it can just retain the current
-                // state it has.
-                performPauseActivityIfNeeded(r, reason);
-
-                // We need to keep around the original state, in case we need to be created again.
-                // But we only do this for pre-Honeycomb apps, which always save their state when
-                // pausing, so we can not have them save their state when restarting from a paused
-                // state. For HC and later, we want to (and can) let the state be saved as the
-                // normal part of stopping the activity.
-                if (r.isPreHoneycomb()) {
-                    r.state = oldState;
-                }
+            if (!r.activity.mFinished && pendingActions != null) {
+                pendingActions.setOldState(r.state);
+                pendingActions.setRestoreInstanceState(true);
+                pendingActions.setCallOnPostCreate(true);
             }
         } else {
             // If there was an error, for any reason, tell the activity manager to stop us.
             try {
                 ActivityManager.getService()
-                    .finishActivity(r.token, Activity.RESULT_CANCELED, null,
-                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
+                        .finishActivity(r.token, Activity.RESULT_CANCELED, null,
+                                Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
             } catch (RemoteException ex) {
                 throw ex.rethrowFromSystemServer();
             }
         }
+
+        return a;
     }
 
     private void reportSizeConfigurations(ActivityClientRecord r) {
@@ -3477,8 +3520,7 @@
         //Slog.i(TAG, "Running services: " + mServices);
     }
 
-    public final ActivityClientRecord performResumeActivity(IBinder token,
-            boolean clearHide, String reason) {
+    ActivityClientRecord performResumeActivity(IBinder token, boolean clearHide, String reason) {
         ActivityClientRecord r = mActivities.get(token);
         if (localLOGV) Slog.v(TAG, "Performing resume of " + r
                 + " finished=" + r.activity.mFinished);
@@ -3518,10 +3560,9 @@
                 EventLog.writeEvent(LOG_AM_ON_RESUME_CALLED, UserHandle.myUserId(),
                         r.activity.getComponentName().getClassName(), reason);
 
-                r.paused = false;
-                r.stopped = false;
                 r.state = null;
                 r.persistentState = null;
+                r.setState(ON_RESUME);
             } catch (Exception e) {
                 if (!mInstrumentation.onException(r.activity, e)) {
                     throw new RuntimeException(
@@ -3553,19 +3594,14 @@
 
     @Override
     public void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
-            boolean reallyResume, int seq, String reason) {
-        ActivityClientRecord r = mActivities.get(token);
-        if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
-            return;
-        }
-
+            String reason) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
         unscheduleGcIdler();
         mSomeActivitiesChanged = true;
 
         // TODO Push resumeArgs into the activity for consideration
-        r = performResumeActivity(token, clearHide, reason);
+        final ActivityClientRecord r = performResumeActivity(token, clearHide, reason);
 
         if (r != null) {
             final Activity a = r.activity;
@@ -3677,16 +3713,6 @@
                 Looper.myQueue().addIdleHandler(new Idler());
             }
             r.onlyLocalRequest = false;
-
-            // Tell the activity manager we have resumed.
-            if (reallyResume) {
-                try {
-                    ActivityManager.getService().activityResumed(token);
-                } catch (RemoteException ex) {
-                    throw ex.rethrowFromSystemServer();
-                }
-            }
-
         } else {
             // If an exception was thrown when trying to resume, then
             // just end this activity.
@@ -3757,21 +3783,17 @@
     }
 
     @Override
-    public void handlePauseActivity(IBinder token, boolean finished,
-            boolean userLeaving, int configChanges, boolean dontReport, int seq) {
+    public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
+            int configChanges, boolean dontReport, PendingTransactionActions pendingActions) {
         ActivityClientRecord r = mActivities.get(token);
-        if (DEBUG_ORDER) Slog.d(TAG, "handlePauseActivity " + r + ", seq: " + seq);
-        if (!checkAndUpdateLifecycleSeq(seq, r, "pauseActivity")) {
-            return;
-        }
         if (r != null) {
-            //Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);
             if (userLeaving) {
                 performUserLeavingActivity(r);
             }
 
             r.activity.mConfigChangeFlags |= configChanges;
-            performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity");
+            performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity",
+                    pendingActions);
 
             // Make sure any pending writes are now committed.
             if (r.isPreHoneycomb()) {
@@ -3795,13 +3817,15 @@
     }
 
     final Bundle performPauseActivity(IBinder token, boolean finished,
-            boolean saveState, String reason) {
+            boolean saveState, String reason, PendingTransactionActions pendingActions) {
         ActivityClientRecord r = mActivities.get(token);
-        return r != null ? performPauseActivity(r, finished, saveState, reason) : null;
+        return r != null
+                ? performPauseActivity(r, finished, saveState, reason, pendingActions)
+                : null;
     }
 
-    final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,
-            boolean saveState, String reason) {
+    private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, boolean saveState,
+            String reason, PendingTransactionActions pendingActions) {
         if (r.paused) {
             if (r.activity.mFinished) {
                 // If we are finishing, we won't call onResume() in certain cases.
@@ -3835,6 +3859,18 @@
             listeners.get(i).onPaused(r.activity);
         }
 
+        final Bundle oldState = pendingActions != null ? pendingActions.getOldState() : null;
+        if (oldState != null) {
+            // We need to keep around the original state, in case we need to be created again.
+            // But we only do this for pre-Honeycomb apps, which always save their state when
+            // pausing, so we can not have them save their state when restarting from a paused
+            // state. For HC and later, we want to (and can) let the state be saved as the
+            // normal part of stopping the activity.
+            if (r.isPreHoneycomb()) {
+                r.state = oldState;
+            }
+        }
+
         return !r.activity.mFinished && saveState ? r.state : null;
     }
 
@@ -3861,7 +3897,7 @@
                         + safeToComponentShortString(r.intent) + ": " + e.toString(), e);
             }
         }
-        r.paused = true;
+        r.setState(ON_PAUSE);
     }
 
     final void performStopActivity(IBinder token, boolean saveState, String reason) {
@@ -3869,37 +3905,6 @@
         performStopActivityInner(r, null, false, saveState, reason);
     }
 
-    private static class StopInfo implements Runnable {
-        ActivityClientRecord activity;
-        Bundle state;
-        PersistableBundle persistentState;
-        CharSequence description;
-
-        @Override public void run() {
-            // Tell activity manager we have been stopped.
-            try {
-                if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + activity);
-                ActivityManager.getService().activityStopped(
-                    activity.token, state, persistentState, description);
-            } catch (RemoteException ex) {
-                // Dump statistics about bundle to help developers debug
-                final LogWriter writer = new LogWriter(Log.WARN, TAG);
-                final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
-                pw.println("Bundle stats:");
-                Bundle.dumpStats(pw, state);
-                pw.println("PersistableBundle stats:");
-                Bundle.dumpStats(pw, persistentState);
-
-                if (ex instanceof TransactionTooLargeException
-                        && activity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
-                    Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
-                    return;
-                }
-                throw ex.rethrowFromSystemServer();
-            }
-        }
-    }
-
     private static final class ProviderRefCount {
         public final ContentProviderHolder holder;
         public final ProviderClientRecord client;
@@ -3930,8 +3935,8 @@
      * For the client, we want to call onStop()/onStart() to indicate when
      * the activity's UI visibility changes.
      */
-    private void performStopActivityInner(ActivityClientRecord r,
-            StopInfo info, boolean keepShown, boolean saveState, String reason) {
+    private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown,
+            boolean saveState, String reason) {
         if (localLOGV) Slog.v(TAG, "Performing stop of " + r);
         if (r != null) {
             if (!keepShown && r.stopped) {
@@ -3956,7 +3961,7 @@
                     // First create a thumbnail for the activity...
                     // For now, don't create the thumbnail here; we are
                     // doing that by doing a screen snapshot.
-                    info.description = r.activity.onCreateDescription();
+                    info.setDescription(r.activity.onCreateDescription());
                 } catch (Exception e) {
                     if (!mInstrumentation.onException(r.activity, e)) {
                         throw new RuntimeException(
@@ -3986,7 +3991,7 @@
                                 + ": " + e.toString(), e);
                     }
                 }
-                r.stopped = true;
+                r.setState(ON_STOP);
                 EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
                         r.activity.getComponentName().getClassName(), reason);
             }
@@ -4022,15 +4027,13 @@
     }
 
     @Override
-    public void handleStopActivity(IBinder token, boolean show, int configChanges, int seq) {
-        ActivityClientRecord r = mActivities.get(token);
-        if (!checkAndUpdateLifecycleSeq(seq, r, "stopActivity")) {
-            return;
-        }
+    public void handleStopActivity(IBinder token, boolean show, int configChanges,
+            PendingTransactionActions pendingActions) {
+        final ActivityClientRecord r = mActivities.get(token);
         r.activity.mConfigChangeFlags |= configChanges;
 
-        StopInfo info = new StopInfo();
-        performStopActivityInner(r, info, show, true, "handleStopActivity");
+        final StopInfo stopInfo = new StopInfo();
+        performStopActivityInner(r, stopInfo, show, true, "handleStopActivity");
 
         if (localLOGV) Slog.v(
             TAG, "Finishing stop of " + r + ": show=" + show
@@ -4043,37 +4046,32 @@
             QueuedWork.waitToFinish();
         }
 
-        // Schedule the call to tell the activity manager we have
-        // stopped.  We don't do this immediately, because we want to
-        // have a chance for any other pending work (in particular memory
-        // trim requests) to complete before you tell the activity
-        // manager to proceed and allow us to go fully into the background.
-        info.activity = r;
-        info.state = r.state;
-        info.persistentState = r.persistentState;
-        mH.post(info);
+        stopInfo.setActivity(r);
+        stopInfo.setState(r.state);
+        stopInfo.setPersistentState(r.persistentState);
+        pendingActions.setStopInfo(stopInfo);
         mSomeActivitiesChanged = true;
     }
 
-    private static boolean checkAndUpdateLifecycleSeq(int seq, ActivityClientRecord r,
-            String action) {
-        if (r == null) {
-            return true;
-        }
-        if (seq < r.lastProcessedSeq) {
-            if (DEBUG_ORDER) Slog.d(TAG, action + " for " + r + " ignored, because seq=" + seq
-                    + " < mCurrentLifecycleSeq=" + r.lastProcessedSeq);
-            return false;
-        }
-        r.lastProcessedSeq = seq;
-        return true;
+    /**
+     * Schedule the call to tell the activity manager we have stopped.  We don't do this
+     * immediately, because we want to have a chance for any other pending work (in particular
+     * memory trim requests) to complete before you tell the activity manager to proceed and allow
+     * us to go fully into the background.
+     */
+    @Override
+    public void reportStop(PendingTransactionActions pendingActions) {
+        mH.post(pendingActions.getStopInfo());
     }
 
-    final void performRestartActivity(IBinder token) {
+    @Override
+    public void performRestartActivity(IBinder token, boolean start) {
         ActivityClientRecord r = mActivities.get(token);
         if (r.stopped) {
-            r.activity.performRestart();
-            r.stopped = false;
+            r.activity.performRestart(start);
+            if (start) {
+                r.setState(ON_START);
+            }
         }
     }
 
@@ -4093,8 +4091,8 @@
             // we are back active so skip it.
             unscheduleGcIdler();
 
-            r.activity.performRestart();
-            r.stopped = false;
+            r.activity.performRestart(true /* start */);
+            r.setState(ON_START);
         }
         if (r.activity.mDecor != null) {
             if (false) Slog.v(
@@ -4132,7 +4130,7 @@
                                 + ": " + e.toString(), e);
                     }
                 }
-                r.stopped = true;
+                r.setState(ON_STOP);
                 EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
                         r.activity.getComponentName().getClassName(), "sleeping");
             }
@@ -4150,8 +4148,8 @@
             }
         } else {
             if (r.stopped && r.activity.mVisibleFromServer) {
-                r.activity.performRestart();
-                r.stopped = false;
+                r.activity.performRestart(true /* start */);
+                r.setState(ON_START);
             }
         }
     }
@@ -4268,11 +4266,8 @@
         }
     }
 
-    public final ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing) {
-        return performDestroyActivity(token, finishing, 0, false);
-    }
-
-    private ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
+    /** Core implementation of activity destroy call. */
+    ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
             int configChanges, boolean getNonConfigInstance) {
         ActivityClientRecord r = mActivities.get(token);
         Class<? extends Activity> activityClass = null;
@@ -4299,7 +4294,7 @@
                                 + ": " + e.toString(), e);
                     }
                 }
-                r.stopped = true;
+                r.setState(ON_STOP);
                 EventLog.writeEvent(LOG_AM_ON_STOP_CALLED, UserHandle.myUserId(),
                         r.activity.getComponentName().getClassName(), "destroy");
             }
@@ -4336,6 +4331,7 @@
                             + ": " + e.toString(), e);
                 }
             }
+            r.setState(ON_DESTROY);
         }
         mActivities.remove(token);
         StrictMode.decrementExpectedActivityCount(activityClass);
@@ -4496,10 +4492,7 @@
                 target.overrideConfig = overrideConfig;
             }
             target.pendingConfigChanges |= configChanges;
-            target.relaunchSeq = getLifecycleSeq();
         }
-        if (DEBUG_ORDER) Slog.d(TAG, "relaunchActivity " + ActivityThread.this + ", target "
-                + target + " operation received seq: " + target.relaunchSeq);
     }
 
     private void handleRelaunchActivity(ActivityClientRecord tmp) {
@@ -4544,12 +4537,6 @@
             }
         }
 
-        if (tmp.lastProcessedSeq > tmp.relaunchSeq) {
-            Slog.wtf(TAG, "For some reason target: " + tmp + " has lower sequence: "
-                    + tmp.relaunchSeq + " than current sequence: " + tmp.lastProcessedSeq);
-        } else {
-            tmp.lastProcessedSeq = tmp.relaunchSeq;
-        }
         if (tmp.createdConfig != null) {
             // If the activity manager is passing us its current config,
             // assume that is really what we want regardless of what we
@@ -4590,9 +4577,6 @@
         r.activity.mConfigChangeFlags |= configChanges;
         r.onlyLocalRequest = tmp.onlyLocalRequest;
         r.mPreserveWindow = tmp.mPreserveWindow;
-        r.lastProcessedSeq = tmp.lastProcessedSeq;
-        r.relaunchSeq = tmp.relaunchSeq;
-        Intent currentIntent = r.activity.mIntent;
 
         r.activity.mChangingConfigurations = true;
 
@@ -4618,7 +4602,8 @@
 
         // Need to ensure state is saved.
         if (!r.paused) {
-            performPauseActivity(r.token, false, r.isPreHoneycomb(), "handleRelaunchActivity");
+            performPauseActivity(r.token, false, r.isPreHoneycomb(), "handleRelaunchActivity",
+                    null /* pendingActions */);
         }
         if (r.state == null && !r.stopped && !r.isPreHoneycomb()) {
             callCallActivityOnSaveInstanceState(r);
@@ -4648,7 +4633,15 @@
         r.startsNotResumed = tmp.startsNotResumed;
         r.overrideConfig = tmp.overrideConfig;
 
-        handleLaunchActivity(r, currentIntent, "handleRelaunchActivity");
+        // TODO(lifecycler): Move relaunch to lifecycler.
+        PendingTransactionActions pendingActions = new PendingTransactionActions();
+        handleLaunchActivity(r, pendingActions);
+        handleStartActivity(r, pendingActions);
+        handleResumeActivity(r.token, false /* clearHide */, r.isForward, "relaunch");
+        if (r.startsNotResumed) {
+            performPauseActivity(r, false /* finished */, r.isPreHoneycomb(), "relaunch",
+                    pendingActions);
+        }
 
         if (!tmp.onlyLocalRequest) {
             try {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 005b7c3..1dbdb59 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -55,6 +55,7 @@
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VersionedPackage;
+import android.content.pm.dex.ArtManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Bitmap;
@@ -121,6 +122,8 @@
     private UserManager mUserManager;
     @GuardedBy("mLock")
     private PackageInstaller mInstaller;
+    @GuardedBy("mLock")
+    private ArtManager mArtManager;
 
     @GuardedBy("mDelegates")
     private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>();
@@ -2750,4 +2753,18 @@
             throw e.rethrowAsRuntimeException();
         }
     }
+
+    @Override
+    public ArtManager getArtManager() {
+        synchronized (mLock) {
+            if (mArtManager == null) {
+                try {
+                    mArtManager = new ArtManager(mPM.getArtManager());
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+            return mArtManager;
+        }
+    }
 }
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index f7f4c71..ef66af0 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -16,15 +16,12 @@
 package android.app;
 
 import android.app.servertransaction.ClientTransaction;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
+import android.app.servertransaction.PendingTransactionActions;
+import android.content.pm.ApplicationInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
-import android.os.Bundle;
 import android.os.IBinder;
-import android.os.PersistableBundle;
 
-import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.content.ReferrerIntent;
 
 import java.util.List;
@@ -40,7 +37,7 @@
 
     /** Prepare and schedule transaction for execution. */
     void scheduleTransaction(ClientTransaction transaction) {
-        transaction.prepare(this);
+        transaction.preExecute(this);
         sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
     }
 
@@ -50,9 +47,6 @@
     // Prepare phase related logic and handlers. Methods that inform about about pending changes or
     // do other internal bookkeeping.
 
-    /** Get current lifecycle request number to maintain correct ordering. */
-    public abstract int getLifecycleSeq();
-
     /** Set pending config in case it will be updated by other transaction item. */
     public abstract void updatePendingConfiguration(Configuration config);
 
@@ -69,15 +63,21 @@
 
     /** Pause the activity. */
     public abstract void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
-            int configChanges, boolean dontReport, int seq);
+            int configChanges, boolean dontReport, PendingTransactionActions pendingActions);
 
     /** Resume the activity. */
     public abstract void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
-            boolean reallyResume, int seq, String reason);
+            String reason);
 
     /** Stop the activity. */
     public abstract void handleStopActivity(IBinder token, boolean show, int configChanges,
-            int seq);
+            PendingTransactionActions pendingActions);
+
+    /** Report that activity was stopped to server. */
+    public abstract void reportStop(PendingTransactionActions pendingActions);
+
+    /** Restart the activity after it was stopped. */
+    public abstract void performRestartActivity(IBinder token, boolean start);
 
     /** Deliver activity (override) configuration change. */
     public abstract void handleActivityConfigurationChanged(IBinder activityToken,
@@ -102,13 +102,23 @@
     public abstract void handleWindowVisibility(IBinder token, boolean show);
 
     /** Perform activity launch. */
-    public abstract void handleLaunchActivity(IBinder token, Intent intent, int ident,
-            ActivityInfo info, Configuration overrideConfig, CompatibilityInfo compatInfo,
-            String referrer, IVoiceInteractor voiceInteractor, Bundle state,
-            PersistableBundle persistentState, List<ResultInfo> pendingResults,
-            List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,
-            ProfilerInfo profilerInfo);
+    public abstract Activity handleLaunchActivity(ActivityThread.ActivityClientRecord r,
+            PendingTransactionActions pendingActions);
+
+    /** Perform activity start. */
+    public abstract void handleStartActivity(ActivityThread.ActivityClientRecord r,
+            PendingTransactionActions pendingActions);
+
+    /** Get package info. */
+    public abstract LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
+            CompatibilityInfo compatInfo);
 
     /** Deliver app configuration change notification. */
     public abstract void handleConfigurationChanged(Configuration config);
+
+    /**
+     * Get {@link android.app.ActivityThread.ActivityClientRecord} instance that corresponds to the
+     * provided token.
+     */
+    public abstract ActivityThread.ActivityClientRecord getActivityClient(IBinder token);
 }
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 21e4227..84c07ec 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -667,4 +667,10 @@
 
      void setShowWhenLocked(in IBinder token, boolean showWhenLocked);
      void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
+
+     /**
+      *  Similar to {@link #startUserInBackground(int userId), but with a listener to report
+      *  user unlock progress.
+      */
+     boolean startUserInBackgroundWithListener(int userid, IProgressListener unlockProgressListener);
 }
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index a07374b..4a85efd 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -79,7 +79,7 @@
      *        passed here as a convenience to the agent.
      */
     void doRestore(in ParcelFileDescriptor data,
-            int appVersionCode, in ParcelFileDescriptor newState,
+            long appVersionCode, in ParcelFileDescriptor newState,
             int token, IBackupManager callbackBinder);
 
     /**
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 1fe2900..d0f84c8 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -382,6 +382,8 @@
     }
 
     /**
+     * @deprecated Use {@link #isKeyguardLocked()} instead.
+     *
      * If keyguard screen is showing or in restricted key input mode (i.e. in
      * keyguard password emergency screen). When in such mode, certain keys,
      * such as the Home key and the right soft keys, don't work.
@@ -389,11 +391,7 @@
      * @return true if in keyguard restricted input mode.
      */
     public boolean inKeyguardRestrictedInputMode() {
-        try {
-            return mWM.inKeyguardRestrictedInputMode();
-        } catch (RemoteException ex) {
-            return false;
-        }
+        return isKeyguardLocked();
     }
 
     /**
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index de6230c..ebd1014 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1646,9 +1646,12 @@
             if (dead) {
                 mConnection.onBindingDied(name);
             }
-            // If there is a new service, it is now connected.
+            // If there is a new viable service, it is now connected.
             if (service != null) {
                 mConnection.onServiceConnected(name, service);
+            } else {
+                // The binding machinery worked, but the remote returned null from onBind().
+                mConnection.onNullBinding(name);
             }
         }
 
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index 3b273bc..998ac5f 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -22,6 +22,7 @@
 import android.os.Bundle;
 import android.util.Log;
 import android.view.Window;
+
 import com.android.internal.content.ReferrerIntent;
 
 import java.util.ArrayList;
@@ -161,12 +162,12 @@
             case CREATED:
                 if (desiredState == STARTED) {
                     if (localLOGV) Log.v(TAG, r.id + ": restarting");
-                    mActivityThread.performRestartActivity(r);
+                    mActivityThread.performRestartActivity(r, true /* start */);
                     r.curState = STARTED;
                 }
                 if (desiredState == RESUMED) {
                     if (localLOGV) Log.v(TAG, r.id + ": restarting and resuming");
-                    mActivityThread.performRestartActivity(r);
+                    mActivityThread.performRestartActivity(r, true /* start */);
                     mActivityThread.performResumeActivity(r, true, "moveToState-CREATED");
                     r.curState = RESUMED;
                 }
@@ -207,7 +208,7 @@
     private void performPause(LocalActivityRecord r, boolean finishing) {
         final boolean needState = r.instanceState == null;
         final Bundle instanceState = mActivityThread.performPauseActivity(
-                r, finishing, needState, "performPause");
+                r, finishing, needState, "performPause", null /* pendingActions */);
         if (needState) {
             r.instanceState = instanceState;
         }
@@ -361,7 +362,8 @@
             performPause(r, finish);
         }
         if (localLOGV) Log.v(TAG, r.id + ": destroying");
-        mActivityThread.performDestroyActivity(r, finish);
+        mActivityThread.performDestroyActivity(r, finish, 0 /* configChanges */,
+                false /* getNonConfigInstance */);
         r.activity = null;
         r.window = null;
         if (finish) {
@@ -625,7 +627,8 @@
         for (int i=0; i<N; i++) {
             LocalActivityRecord r = mActivityArray.get(i);
             if (localLOGV) Log.v(TAG, r.id + ": destroying");
-            mActivityThread.performDestroyActivity(r, finishing);
+            mActivityThread.performDestroyActivity(r, finishing, 0 /* configChanges */,
+                    false /* getNonConfigInstance */);
         }
         mActivities.clear();
         mActivityArray.clear();
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e48946f..f831ae2 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -22,6 +22,7 @@
 import android.app.admin.IDevicePolicyManager;
 import android.app.job.IJobScheduler;
 import android.app.job.JobScheduler;
+import android.app.slice.SliceManager;
 import android.app.timezone.RulesManager;
 import android.app.trust.TrustManager;
 import android.app.usage.IStorageStatsManager;
@@ -944,6 +945,16 @@
                                 ICrossProfileApps.Stub.asInterface(b));
                     }
                 });
+
+        registerService(Context.SLICE_SERVICE, SliceManager.class,
+                new CachedServiceFetcher<SliceManager>() {
+                    @Override
+                    public SliceManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        return new SliceManager(ctx.getOuterContext(),
+                                ctx.mMainThread.getHandler());
+                    }
+            });
     }
 
     /**
diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java
index 392387a..61b90e1 100644
--- a/core/java/android/app/VrManager.java
+++ b/core/java/android/app/VrManager.java
@@ -4,6 +4,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Handler;
@@ -214,4 +215,22 @@
             e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Start VR Input method for the packageName in {@link ComponentName}.
+     * This method notifies InputMethodManagerService to use VR IME instead of
+     * regular phone IME.
+     * @param componentName ComponentName of a VR InputMethod that should be set as selected
+     * input by InputMethodManagerService.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
+    public void setVrInputMethod(ComponentName componentName) {
+        try {
+            mService.setVrInputMethod(componentName);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index f0117f2..562b981 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -57,7 +57,12 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.ContactsContract.Directory;
+import android.security.AttestedKeyPair;
 import android.security.Credentials;
+import android.security.KeyChain;
+import android.security.KeyChainException;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
 import android.service.restrictions.RestrictionsReceiver;
 import android.telephony.TelephonyManager;
 import android.util.ArraySet;
@@ -75,6 +80,7 @@
 import java.net.InetSocketAddress;
 import java.net.Proxy;
 import java.security.KeyFactory;
+import java.security.KeyPair;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.cert.Certificate;
@@ -3943,6 +3949,50 @@
     }
 
     /**
+     * Called by a device or profile owner, or delegated certificate installer, to generate a
+     * new private/public key pair. If the device supports key generation via secure hardware,
+     * this method is useful for creating a key in KeyChain that never left the secure hardware.
+     *
+     * Access to the key is controlled the same way as in {@link #installKeyPair}.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     *            {@code null} if calling from a delegated certificate installer.
+     * @param algorithm The key generation algorithm, see {@link java.security.KeyPairGenerator}.
+     * @param keySpec Specification of the key to generate, see
+     * {@link java.security.KeyPairGenerator}.
+     * @return A non-null {@code AttestedKeyPair} if the key generation succeeded, null otherwise.
+     * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
+     *         owner.
+     * @throws IllegalArgumentException if the alias in {@code keySpec} is empty, or if the
+     *         algorithm specification in {@code keySpec} is not {@code RSAKeyGenParameterSpec}
+     *         or {@code ECGenParameterSpec}.
+     */
+    public AttestedKeyPair generateKeyPair(@Nullable ComponentName admin,
+            @NonNull String algorithm, @NonNull KeyGenParameterSpec keySpec) {
+        throwIfParentInstance("generateKeyPair");
+        try {
+            final ParcelableKeyGenParameterSpec parcelableSpec =
+                    new ParcelableKeyGenParameterSpec(keySpec);
+            final boolean success = mService.generateKeyPair(
+                    admin, mContext.getPackageName(), algorithm, parcelableSpec);
+            if (!success) {
+                Log.e(TAG, "Error generating key via DevicePolicyManagerService.");
+                return null;
+            }
+
+            final KeyPair keyPair = KeyChain.getKeyPair(mContext, keySpec.getKeystoreAlias());
+            return new AttestedKeyPair(keyPair, null);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (KeyChainException e) {
+            Log.w(TAG, "Failed to generate key", e);
+        } catch (InterruptedException e) {
+            Log.w(TAG, "Interrupted while generating key", e);
+            Thread.currentThread().interrupt();
+        }
+        return null;
+    }
+
+    /**
      * @return the alias of a given CA certificate in the certificate store, or {@code null} if it
      * doesn't exist.
      */
@@ -6219,7 +6269,7 @@
      * @return {@code true} if the user was removed, {@code false} otherwise.
      * @throws SecurityException if {@code admin} is not a device owner.
      */
-    public boolean removeUser(@NonNull ComponentName admin, UserHandle userHandle) {
+    public boolean removeUser(@NonNull ComponentName admin, @NonNull UserHandle userHandle) {
         throwIfParentInstance("removeUser");
         try {
             return mService.removeUser(admin, userHandle);
@@ -6230,6 +6280,7 @@
 
     /**
      * Called by a device owner to switch the specified user to the foreground.
+     * <p> This cannot be used to switch to a managed profile.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param userHandle the user to switch to; null will switch to primary.
@@ -6247,6 +6298,65 @@
     }
 
     /**
+     * Called by a device owner to stop the specified secondary user.
+     * <p> This cannot be used to stop the primary user or a managed profile.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param userHandle the user to be stopped.
+     * @return {@code true} if the user can be stopped, {@code false} otherwise.
+     * @throws SecurityException if {@code admin} is not a device owner.
+     */
+    public boolean stopUser(@NonNull ComponentName admin, @NonNull UserHandle userHandle) {
+        throwIfParentInstance("stopUser");
+        try {
+            return mService.stopUser(admin, userHandle);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Called by a profile owner that is affiliated with the device owner to stop the calling user
+     * and switch back to primary.
+     * <p> This has no effect when called on a managed profile.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @return {@code true} if the exit was successful, {@code false} otherwise.
+     * @throws SecurityException if {@code admin} is not a profile owner affiliated with the device
+     * owner.
+     */
+    public boolean logoutUser(@NonNull ComponentName admin) {
+        throwIfParentInstance("logoutUser");
+        try {
+            return mService.logoutUser(admin);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Called by a device owner to list all secondary users on the device, excluding managed
+     * profiles.
+     * <p> Used for various user management APIs, including {@link #switchUser}, {@link #removeUser}
+     * and {@link #stopUser}.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @return list of other {@link UserHandle}s on the device.
+     * @throws SecurityException if {@code admin} is not a device owner.
+     * @see #switchUser
+     * @see #removeUser
+     * @see #stopUser
+     */
+    public List<UserHandle> getSecondaryUsers(@NonNull ComponentName admin) {
+        throwIfParentInstance("getSecondaryUsers");
+        try {
+            return mService.getSecondaryUsers(admin);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Retrieves the application restrictions for a given target application running in the calling
      * user.
      * <p>
@@ -8475,6 +8585,38 @@
     }
 
     /**
+     * Called by a device owner to specify whether a logout button is enabled for all secondary
+     * users. The system may show a logout button that stops the user and switches back to the
+     * primary user.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param enabled whether logout button should be enabled or not.
+     * @throws SecurityException if {@code admin} is not a device owner.
+     */
+    public void setLogoutButtonEnabled(@NonNull ComponentName admin, boolean enabled) {
+        throwIfParentInstance("setLogoutButtonEnabled");
+        try {
+            mService.setLogoutButtonEnabled(admin, enabled);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether logout button is enabled by a device owner.
+     *
+     * @return {@code true} if logout button is enabled by device owner, {@code false} otherwise.
+     */
+    public boolean isLogoutButtonEnabled() {
+        throwIfParentInstance("isLogoutButtonEnabled");
+        try {
+            return mService.isLogoutButtonEnabled();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Callback used in {@link #clearApplicationUserData}
      * to indicate that the clearing of an application's user data is done.
      */
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 802d42f..c525df7 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -36,6 +36,7 @@
 import android.os.PersistableBundle;
 import android.os.RemoteCallback;
 import android.os.UserHandle;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
 
 import java.util.List;
 
@@ -165,6 +166,7 @@
             in byte[] certBuffer, in byte[] certChainBuffer, String alias, boolean requestAccess,
             boolean isUserSelectable);
     boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias);
+    boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, in ParcelableKeyGenParameterSpec keySpec);
     void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback);
 
     void setDelegatedScopes(in ComponentName who, in String delegatePackage, in List<String> scopes);
@@ -215,6 +217,9 @@
     UserHandle createAndManageUser(in ComponentName who, in String name, in ComponentName profileOwner, in PersistableBundle adminExtras, in int flags);
     boolean removeUser(in ComponentName who, in UserHandle userHandle);
     boolean switchUser(in ComponentName who, in UserHandle userHandle);
+    boolean stopUser(in ComponentName who, in UserHandle userHandle);
+    boolean logoutUser(in ComponentName who);
+    List<UserHandle> getSecondaryUsers(in ComponentName who);
 
     void enableSystemApp(in ComponentName admin, in String callerPackage, in String packageName);
     int enableSystemAppWithIntent(in ComponentName admin, in String callerPackage, in Intent intent);
@@ -366,4 +371,7 @@
     StringParceledListSlice getOwnerInstalledCaCerts(in UserHandle user);
 
     boolean clearApplicationUserData(in ComponentName admin, in String packageName, in IPackageDataObserver callback);
+
+    void setLogoutButtonEnabled(in ComponentName admin, boolean enabled);
+    boolean isLogoutButtonEnabled();
 }
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 7aa80d2..861cb9a 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -263,6 +263,17 @@
             ParcelFileDescriptor newState) throws IOException;
 
     /**
+     * New version of {@link #onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor)}
+     * that handles a long app version code.  Default implementation casts the version code to
+     * an int and calls {@link #onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor)}.
+     */
+    public void onRestore(BackupDataInput data, long appVersionCode,
+            ParcelFileDescriptor newState)
+            throws IOException {
+        onRestore(data, (int) appVersionCode, newState);
+    }
+
+    /**
      * The application is having its entire file system contents backed up.  {@code data}
      * points to the backup destination, and the app has the opportunity to choose which
      * files are to be stored.  To commit a file as part of the backup, call the
@@ -947,7 +958,7 @@
         }
 
         @Override
-        public void doRestore(ParcelFileDescriptor data, int appVersionCode,
+        public void doRestore(ParcelFileDescriptor data, long appVersionCode,
                 ParcelFileDescriptor newState,
                 int token, IBackupManager callbackBinder) throws RemoteException {
             // Ensure that we're running with the app's normal permission level
diff --git a/core/java/android/app/backup/BackupManagerMonitor.java b/core/java/android/app/backup/BackupManagerMonitor.java
index ebad16e..ae4a98a 100644
--- a/core/java/android/app/backup/BackupManagerMonitor.java
+++ b/core/java/android/app/backup/BackupManagerMonitor.java
@@ -40,9 +40,14 @@
   /** string : the package name */
   public static final String EXTRA_LOG_EVENT_PACKAGE_NAME =
           "android.app.backup.extra.LOG_EVENT_PACKAGE_NAME";
-  /** int : the versionCode of the package named by EXTRA_LOG_EVENT_PACKAGE_NAME */
+  /** int : the versionCode of the package named by EXTRA_LOG_EVENT_PACKAGE_NAME
+   * @deprecated Use {@link #EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION} */
+  @Deprecated
   public static final String EXTRA_LOG_EVENT_PACKAGE_VERSION =
           "android.app.backup.extra.LOG_EVENT_PACKAGE_VERSION";
+  /** long : the full versionCode of the package named by EXTRA_LOG_EVENT_PACKAGE_NAME */
+  public static final String EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION =
+          "android.app.backup.extra.LOG_EVENT_PACKAGE_FULL_VERSION";
   /** int : the id of the log message, will be a unique identifier */
   public static final String EXTRA_LOG_EVENT_ID = "android.app.backup.extra.LOG_EVENT_ID";
   /**
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index 07001e2..a3fe686c 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -19,6 +19,7 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.view.Display.INVALID_DISPLAY;
 
+import android.app.ClientTransactionHandler;
 import android.content.res.Configuration;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -37,7 +38,8 @@
     }
 
     @Override
-    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         // TODO(lifecycler): detect if PIP or multi-window mode changed and report it here.
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityConfigChanged");
         client.handleActivityConfigurationChanged(token, mConfiguration, INVALID_DISPLAY);
@@ -85,4 +87,9 @@
     public int hashCode() {
         return mConfiguration.hashCode();
     }
+
+    @Override
+    public String toString() {
+        return "ActivityConfigurationChange{config=" + mConfiguration + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
index a64108d..24141e5 100644
--- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java
+++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
@@ -27,16 +27,19 @@
  */
 public abstract class ActivityLifecycleItem extends ClientTransactionItem {
 
-    static final boolean DEBUG_ORDER = false;
-
-    @IntDef({UNDEFINED, RESUMED, PAUSED, STOPPED, DESTROYED})
+    @IntDef({UNDEFINED, PRE_ON_CREATE, ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP,
+            ON_DESTROY, ON_RESTART})
     @Retention(RetentionPolicy.SOURCE)
-    @interface LifecycleState{}
+    public @interface LifecycleState{}
     public static final int UNDEFINED = -1;
-    public static final int RESUMED = 0;
-    public static final int PAUSED = 1;
-    public static final int STOPPED = 2;
-    public static final int DESTROYED = 3;
+    public static final int PRE_ON_CREATE = 0;
+    public static final int ON_CREATE = 1;
+    public static final int ON_START = 2;
+    public static final int ON_RESUME = 3;
+    public static final int ON_PAUSE = 4;
+    public static final int ON_STOP = 5;
+    public static final int ON_DESTROY = 6;
+    public static final int ON_RESTART = 7;
 
     /** A final lifecycle state that an activity should reach. */
     @LifecycleState
diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java
index 76664d8..3a3d5b9 100644
--- a/core/java/android/app/servertransaction/ActivityResultItem.java
+++ b/core/java/android/app/servertransaction/ActivityResultItem.java
@@ -16,9 +16,10 @@
 
 package android.app.servertransaction;
 
-import static android.app.servertransaction.ActivityLifecycleItem.PAUSED;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
+import android.app.ClientTransactionHandler;
 import android.app.ResultInfo;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -41,11 +42,12 @@
 
     @Override
     public int getPreExecutionState() {
-        return PAUSED;
+        return ON_PAUSE;
     }
 
     @Override
-    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult");
         client.handleSendResult(token, mResultInfoList);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -92,4 +94,9 @@
     public int hashCode() {
         return mResultInfoList.hashCode();
     }
+
+    @Override
+    public String toString() {
+        return "ActivityResultItem{resultInfoList=" + mResultInfoList + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/BaseClientRequest.java b/core/java/android/app/servertransaction/BaseClientRequest.java
index 4bd01af..e3473bf 100644
--- a/core/java/android/app/servertransaction/BaseClientRequest.java
+++ b/core/java/android/app/servertransaction/BaseClientRequest.java
@@ -33,13 +33,25 @@
      * @param client Target client handler.
      * @param token  Target activity token.
      */
-    default void prepare(ClientTransactionHandler client, IBinder token) {
+    default void preExecute(ClientTransactionHandler client, IBinder token) {
     }
 
     /**
      * Execute the request.
      * @param client Target client handler.
      * @param token Target activity token.
+     * @param pendingActions Container that may have data pending to be used.
      */
-    void execute(ClientTransactionHandler client, IBinder token);
+    void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions);
+
+    /**
+     * Perform all actions that need to happen after execution, e.g. report the result to server.
+     * @param client Target client handler.
+     * @param token Target activity token.
+     * @param pendingActions Container that may have data pending to be used.
+     */
+    default void postExecute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
+    }
 }
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index d2289ba..7a58962 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -16,6 +16,7 @@
 
 package android.app.servertransaction;
 
+import android.annotation.Nullable;
 import android.app.ClientTransactionHandler;
 import android.app.IApplicationThread;
 import android.os.IBinder;
@@ -69,6 +70,23 @@
         mActivityCallbacks.add(activityCallback);
     }
 
+    /** Get the list of callbacks. */
+    @Nullable
+    List<ClientTransactionItem> getCallbacks() {
+        return mActivityCallbacks;
+    }
+
+    /** Get the target activity. */
+    @Nullable
+    public IBinder getActivityToken() {
+        return mActivityToken;
+    }
+
+    /** Get the target state lifecycle request. */
+    ActivityLifecycleItem getLifecycleStateRequest() {
+        return mLifecycleStateRequest;
+    }
+
     /**
      * Set the lifecycle state in which the client should be after executing the transaction.
      * @param stateRequest A lifecycle request initialized with right parameters.
@@ -82,44 +100,27 @@
      * @param clientTransactionHandler Handler on the client side that will executed all operations
      *                                 requested by transaction items.
      */
-    public void prepare(android.app.ClientTransactionHandler clientTransactionHandler) {
+    public void preExecute(android.app.ClientTransactionHandler clientTransactionHandler) {
         if (mActivityCallbacks != null) {
             final int size = mActivityCallbacks.size();
             for (int i = 0; i < size; ++i) {
-                mActivityCallbacks.get(i).prepare(clientTransactionHandler, mActivityToken);
+                mActivityCallbacks.get(i).preExecute(clientTransactionHandler, mActivityToken);
             }
         }
         if (mLifecycleStateRequest != null) {
-            mLifecycleStateRequest.prepare(clientTransactionHandler, mActivityToken);
-        }
-    }
-
-    /**
-     * Execute the transaction.
-     * @param clientTransactionHandler Handler on the client side that will execute all operations
-     *                                 requested by transaction items.
-     */
-    public void execute(android.app.ClientTransactionHandler clientTransactionHandler) {
-        if (mActivityCallbacks != null) {
-            final int size = mActivityCallbacks.size();
-            for (int i = 0; i < size; ++i) {
-                mActivityCallbacks.get(i).execute(clientTransactionHandler, mActivityToken);
-            }
-        }
-        if (mLifecycleStateRequest != null) {
-            mLifecycleStateRequest.execute(clientTransactionHandler, mActivityToken);
+            mLifecycleStateRequest.preExecute(clientTransactionHandler, mActivityToken);
         }
     }
 
     /**
      * Schedule the transaction after it was initialized. It will be send to client and all its
      * individual parts will be applied in the following sequence:
-     * 1. The client calls {@link #prepare(ClientTransactionHandler)}, which triggers all work that
-     *    needs to be done before actually scheduling the transaction for callbacks and lifecycle
-     *    state request.
+     * 1. The client calls {@link #preExecute(ClientTransactionHandler)}, which triggers all work
+     *    that needs to be done before actually scheduling the transaction for callbacks and
+     *    lifecycle state request.
      * 2. The transaction message is scheduled.
-     * 3. The client calls {@link #execute(ClientTransactionHandler)}, which executes all callbacks
-     *    and necessary lifecycle transitions.
+     * 3. The client calls {@link TransactionExecutor#execute(ClientTransaction)}, which executes
+     *    all callbacks and necessary lifecycle transitions.
      */
     public void schedule() throws RemoteException {
         mClient.scheduleTransaction(this);
diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
index 055923e..ee1effa 100644
--- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java
@@ -16,6 +16,7 @@
 
 package android.app.servertransaction;
 
+import android.app.ClientTransactionHandler;
 import android.content.res.Configuration;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -33,12 +34,13 @@
     }
 
     @Override
-    public void prepare(android.app.ClientTransactionHandler client, IBinder token) {
+    public void preExecute(android.app.ClientTransactionHandler client, IBinder token) {
         client.updatePendingConfiguration(mConfiguration);
     }
 
     @Override
-    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         client.handleConfigurationChanged(mConfiguration);
     }
 
@@ -82,4 +84,9 @@
     public int hashCode() {
         return mConfiguration.hashCode();
     }
+
+    @Override
+    public String toString() {
+        return "ConfigurationChangeItem{config=" + mConfiguration + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index 38fd5fb..3012a7a 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -38,7 +38,8 @@
     }
 
     @Override
-    public void execute(ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");
         client.handleDestroyActivity(token, mFinished, mConfigChanges,
                 false /* getNonConfigInstance */);
@@ -47,7 +48,7 @@
 
     @Override
     public int getTargetState() {
-        return DESTROYED;
+        return ON_DESTROY;
     }
 
 
@@ -96,4 +97,10 @@
         result = 31 * result + mConfigChanges;
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "DestroyActivityItem{finished=" + mFinished + ",mConfigChanges="
+                + mConfigChanges + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 417ebac..e39042f 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -18,6 +18,7 @@
 
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
+import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.app.ProfilerInfo;
 import android.app.ResultInfo;
@@ -42,7 +43,7 @@
  * Request to launch an activity.
  * @hide
  */
-public class LaunchActivityItem extends ActivityLifecycleItem {
+public class LaunchActivityItem extends ClientTransactionItem {
 
     private final Intent mIntent;
     private final int mIdent;
@@ -57,8 +58,6 @@
     private final PersistableBundle mPersistentState;
     private final List<ResultInfo> mPendingResults;
     private final List<ReferrerIntent> mPendingNewIntents;
-    // TODO(lifecycler): use lifecycle request instead of this param.
-    private final boolean mNotResumed;
     private final boolean mIsForward;
     private final ProfilerInfo mProfilerInfo;
 
@@ -66,8 +65,7 @@
             Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo,
             String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,
             PersistableBundle persistentState, List<ResultInfo> pendingResults,
-            List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,
-            ProfilerInfo profilerInfo) {
+            List<ReferrerIntent> pendingNewIntents, boolean isForward, ProfilerInfo profilerInfo) {
         mIntent = intent;
         mIdent = ident;
         mInfo = info;
@@ -81,31 +79,28 @@
         mPersistentState = persistentState;
         mPendingResults = pendingResults;
         mPendingNewIntents = pendingNewIntents;
-        mNotResumed = notResumed;
         mIsForward = isForward;
         mProfilerInfo = profilerInfo;
     }
 
     @Override
-    public void prepare(ClientTransactionHandler client, IBinder token) {
+    public void preExecute(ClientTransactionHandler client, IBinder token) {
         client.updateProcessState(mProcState, false);
         client.updatePendingConfiguration(mCurConfig);
     }
 
     @Override
-    public void execute(ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
-        client.handleLaunchActivity(token, mIntent, mIdent, mInfo, mOverrideConfig, mCompatInfo,
-                mReferrer, mVoiceInteractor, mState, mPersistentState, mPendingResults,
-                mPendingNewIntents, mNotResumed, mIsForward, mProfilerInfo);
+        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
+                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
+                mPendingResults, mPendingNewIntents, mIsForward,
+                mProfilerInfo, client);
+        client.handleLaunchActivity(r, pendingActions);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
-    @Override
-    public int getTargetState() {
-        return mNotResumed ? PAUSED : RESUMED;
-    }
-
 
     // Parcelable implementation
 
@@ -125,7 +120,6 @@
         dest.writePersistableBundle(mPersistentState);
         dest.writeTypedList(mPendingResults, flags);
         dest.writeTypedList(mPendingNewIntents, flags);
-        dest.writeBoolean(mNotResumed);
         dest.writeBoolean(mIsForward);
         dest.writeTypedObject(mProfilerInfo, flags);
     }
@@ -145,7 +139,6 @@
         mPersistentState = in.readPersistableBundle(getClass().getClassLoader());
         mPendingResults = in.createTypedArrayList(ResultInfo.CREATOR);
         mPendingNewIntents = in.createTypedArrayList(ReferrerIntent.CREATOR);
-        mNotResumed = in.readBoolean();
         mIsForward = in.readBoolean();
         mProfilerInfo = in.readTypedObject(ProfilerInfo.CREATOR);
     }
@@ -179,7 +172,7 @@
                 && areBundlesEqual(mPersistentState, other.mPersistentState)
                 && Objects.equals(mPendingResults, other.mPendingResults)
                 && Objects.equals(mPendingNewIntents, other.mPendingNewIntents)
-                && mNotResumed == other.mNotResumed && mIsForward == other.mIsForward
+                && mIsForward == other.mIsForward
                 && Objects.equals(mProfilerInfo, other.mProfilerInfo);
     }
 
@@ -197,7 +190,6 @@
         result = 31 * result + (mPersistentState != null ? mPersistentState.size() : 0);
         result = 31 * result + Objects.hashCode(mPendingResults);
         result = 31 * result + Objects.hashCode(mPendingNewIntents);
-        result = 31 * result + (mNotResumed ? 1 : 0);
         result = 31 * result + (mIsForward ? 1 : 0);
         result = 31 * result + Objects.hashCode(mProfilerInfo);
         return result;
@@ -229,4 +221,14 @@
         }
         return true;
     }
+
+    @Override
+    public String toString() {
+        return "LaunchActivityItem{intent=" + mIntent + ",ident=" + mIdent + ",info=" + mInfo
+                + ",curConfig=" + mCurConfig + ",overrideConfig=" + mOverrideConfig
+                + ",referrer=" + mReferrer + ",procState=" + mProcState + ",state=" + mState
+                + ",persistentState=" + mPersistentState + ",pendingResults=" + mPendingResults
+                + ",pendingNewIntents=" + mPendingNewIntents + ",profilerInfo=" + mProfilerInfo
+                + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index ccd80d8..ee87261 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -18,6 +18,7 @@
 
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
+import android.app.ClientTransactionHandler;
 import android.content.res.Configuration;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -38,7 +39,8 @@
     }
 
     @Override
-    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityMovedToDisplay");
         client.handleActivityConfigurationChanged(token, mConfiguration, mTargetDisplayId);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -90,4 +92,10 @@
         result = 31 * result + mConfiguration.hashCode();
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "MoveToDisplayItem{targetDisplayId=" + mTargetDisplayId
+                + ",configuration=" + mConfiguration + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java b/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java
index a0c617f..04ddb5e 100644
--- a/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java
+++ b/core/java/android/app/servertransaction/MultiWindowModeChangeItem.java
@@ -16,6 +16,7 @@
 
 package android.app.servertransaction;
 
+import android.app.ClientTransactionHandler;
 import android.content.res.Configuration;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -38,7 +39,8 @@
     }
 
     @Override
-    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         client.handleMultiWindowModeChanged(token, mIsInMultiWindowMode, mOverrideConfig);
     }
 
@@ -89,4 +91,10 @@
         result = 31 * result + mOverrideConfig.hashCode();
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "MultiWindowModeChangeItem{isInMultiWindowMode=" + mIsInMultiWindowMode
+                + ",overrideConfig=" + mOverrideConfig + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java
index 61a8965..d01b455 100644
--- a/core/java/android/app/servertransaction/NewIntentItem.java
+++ b/core/java/android/app/servertransaction/NewIntentItem.java
@@ -16,9 +16,7 @@
 
 package android.app.servertransaction;
 
-import static android.app.servertransaction.ActivityLifecycleItem.PAUSED;
-import static android.app.servertransaction.ActivityLifecycleItem.RESUMED;
-
+import android.app.ClientTransactionHandler;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -42,18 +40,20 @@
         mPause = pause;
     }
 
-    @Override
+    // TODO(lifecycler): Switch new intent handling to this scheme.
+    /*@Override
     public int getPreExecutionState() {
-        return PAUSED;
+        return ON_PAUSE;
     }
 
     @Override
     public int getPostExecutionState() {
-        return RESUMED;
-    }
+        return ON_RESUME;
+    }*/
 
     @Override
-    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityNewIntent");
         client.handleNewIntent(token, mIntents, mPause);
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -105,4 +105,9 @@
         result = 31 * result + mIntents.hashCode();
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "NewIntentItem{pause=" + mPause + ",intents=" + mIntents + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index e561a4b..634da04 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -18,11 +18,12 @@
 
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
+import android.app.ActivityManager;
 import android.app.ClientTransactionHandler;
 import android.os.IBinder;
 import android.os.Parcel;
+import android.os.RemoteException;
 import android.os.Trace;
-import android.util.Slog;
 
 /**
  * Request to move an activity to paused state.
@@ -37,7 +38,10 @@
     private final int mConfigChanges;
     private final boolean mDontReport;
 
-    private int mLifecycleSeq;
+    public PauseActivityItem() {
+        this(false /* finished */, false /* userLeaving */, 0 /* configChanges */,
+                true /* dontReport */);
+    }
 
     public PauseActivityItem(boolean finished, boolean userLeaving, int configChanges,
             boolean dontReport) {
@@ -48,27 +52,32 @@
     }
 
     @Override
-    public void prepare(ClientTransactionHandler client, IBinder token) {
-        mLifecycleSeq = client.getLifecycleSeq();
-        if (DEBUG_ORDER) {
-            Slog.d(TAG, "Pause transaction for " + client + " received seq: "
-                    + mLifecycleSeq);
-        }
-    }
-
-    @Override
-    public void execute(ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
         client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, mDontReport,
-                mLifecycleSeq);
+                pendingActions);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
     @Override
     public int getTargetState() {
-        return PAUSED;
+        return ON_PAUSE;
     }
 
+    @Override
+    public void postExecute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
+        if (mDontReport) {
+            return;
+        }
+        try {
+            // TODO(lifecycler): Use interface callback instead of AMS.
+            ActivityManager.getService().activityPaused(token);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
 
     // Parcelable implementation
 
@@ -122,4 +131,10 @@
         result = 31 * result + (mDontReport ? 1 : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "PauseActivityItem{finished=" + mFinished + ",userLeaving=" + mUserLeaving
+                + ",configChanges=" + mConfigChanges + ",dontReport=" + mDontReport + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/PendingTransactionActions.java b/core/java/android/app/servertransaction/PendingTransactionActions.java
new file mode 100644
index 0000000..073d28c
--- /dev/null
+++ b/core/java/android/app/servertransaction/PendingTransactionActions.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 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.app.servertransaction;
+
+import static android.app.ActivityThread.DEBUG_MEMORY_TRIM;
+
+import android.app.ActivityManager;
+import android.app.ActivityThread.ActivityClientRecord;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.os.TransactionTooLargeException;
+import android.util.Log;
+import android.util.LogWriter;
+import android.util.Slog;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+/**
+ * Container that has data pending to be used at later stages of
+ * {@link android.app.servertransaction.ClientTransaction}.
+ * An instance of this class is passed to each individual transaction item, so it can use some
+ * information from previous steps or add some for the following steps.
+ *
+ * @hide
+ */
+public class PendingTransactionActions {
+    private boolean mRestoreInstanceState;
+    private boolean mCallOnPostCreate;
+    private Bundle mOldState;
+    private StopInfo mStopInfo;
+
+    public PendingTransactionActions() {
+        clear();
+    }
+
+    /** Reset the state of the instance to default, non-initialized values. */
+    public void clear() {
+        mRestoreInstanceState = false;
+        mCallOnPostCreate = false;
+        mOldState = null;
+        mStopInfo = null;
+    }
+
+    /** Getter */
+    public boolean shouldRestoreInstanceState() {
+        return mRestoreInstanceState;
+    }
+
+    public void setRestoreInstanceState(boolean restoreInstanceState) {
+        mRestoreInstanceState = restoreInstanceState;
+    }
+
+    /** Getter */
+    public boolean shouldCallOnPostCreate() {
+        return mCallOnPostCreate;
+    }
+
+    public void setCallOnPostCreate(boolean callOnPostCreate) {
+        mCallOnPostCreate = callOnPostCreate;
+    }
+
+    public Bundle getOldState() {
+        return mOldState;
+    }
+
+    public void setOldState(Bundle oldState) {
+        mOldState = oldState;
+    }
+
+    public StopInfo getStopInfo() {
+        return mStopInfo;
+    }
+
+    public void setStopInfo(StopInfo stopInfo) {
+        mStopInfo = stopInfo;
+    }
+
+    /** Reports to server about activity stop. */
+    public static class StopInfo implements Runnable {
+        private static final String TAG = "ActivityStopInfo";
+
+        private ActivityClientRecord mActivity;
+        private Bundle mState;
+        private PersistableBundle mPersistentState;
+        private CharSequence mDescription;
+
+        public void setActivity(ActivityClientRecord activity) {
+            mActivity = activity;
+        }
+
+        public void setState(Bundle state) {
+            mState = state;
+        }
+
+        public void setPersistentState(PersistableBundle persistentState) {
+            mPersistentState = persistentState;
+        }
+
+        public void setDescription(CharSequence description) {
+            mDescription = description;
+        }
+
+        @Override
+        public void run() {
+            // Tell activity manager we have been stopped.
+            try {
+                if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + mActivity);
+                // TODO(lifecycler): Use interface callback instead of AMS.
+                ActivityManager.getService().activityStopped(
+                        mActivity.token, mState, mPersistentState, mDescription);
+            } catch (RemoteException ex) {
+                // Dump statistics about bundle to help developers debug
+                final LogWriter writer = new LogWriter(Log.WARN, TAG);
+                final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
+                pw.println("Bundle stats:");
+                Bundle.dumpStats(pw, mState);
+                pw.println("PersistableBundle stats:");
+                Bundle.dumpStats(pw, mPersistentState);
+
+                if (ex instanceof TransactionTooLargeException
+                        && mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
+                    Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
+                    return;
+                }
+                throw ex.rethrowFromSystemServer();
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/servertransaction/PipModeChangeItem.java b/core/java/android/app/servertransaction/PipModeChangeItem.java
index 923839e..7c74e6f 100644
--- a/core/java/android/app/servertransaction/PipModeChangeItem.java
+++ b/core/java/android/app/servertransaction/PipModeChangeItem.java
@@ -16,6 +16,7 @@
 
 package android.app.servertransaction;
 
+import android.app.ClientTransactionHandler;
 import android.content.res.Configuration;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -37,7 +38,8 @@
     }
 
     @Override
-    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         client.handlePictureInPictureModeChanged(token, mIsInPipMode, mOverrideConfig);
     }
 
@@ -86,4 +88,10 @@
         result = 31 * result + mOverrideConfig.hashCode();
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "PipModeChangeItem{isInPipMode=" + mIsInPipMode
+                + ",overrideConfig=" + mOverrideConfig + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index ea31a46..d659b80 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -18,11 +18,12 @@
 
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
+import android.app.ActivityManager;
 import android.app.ClientTransactionHandler;
 import android.os.IBinder;
 import android.os.Parcel;
+import android.os.RemoteException;
 import android.os.Trace;
-import android.util.Slog;
 
 /**
  * Request to move an activity to resumed state.
@@ -33,36 +34,50 @@
     private static final String TAG = "ResumeActivityItem";
 
     private final int mProcState;
+    private final boolean mUpdateProcState;
     private final boolean mIsForward;
 
-    private int mLifecycleSeq;
+    public ResumeActivityItem(boolean isForward) {
+        mProcState = ActivityManager.PROCESS_STATE_UNKNOWN;
+        mUpdateProcState = false;
+        mIsForward = isForward;
+    }
 
     public ResumeActivityItem(int procState, boolean isForward) {
         mProcState = procState;
+        mUpdateProcState = true;
         mIsForward = isForward;
     }
 
     @Override
-    public void prepare(ClientTransactionHandler client, IBinder token) {
-        mLifecycleSeq = client.getLifecycleSeq();
-        if (DEBUG_ORDER) {
-            Slog.d(TAG, "Resume transaction for " + client + " received seq: "
-                    + mLifecycleSeq);
+    public void preExecute(ClientTransactionHandler client, IBinder token) {
+        if (mUpdateProcState) {
+            client.updateProcessState(mProcState, false);
         }
-        client.updateProcessState(mProcState, false);
     }
 
     @Override
-    public void execute(ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
-        client.handleResumeActivity(token, true /* clearHide */, mIsForward,
-                true /* reallyResume */, mLifecycleSeq, "RESUME_ACTIVITY");
+        client.handleResumeActivity(token, true /* clearHide */, mIsForward, "RESUME_ACTIVITY");
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
     @Override
+    public void postExecute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
+        try {
+            // TODO(lifecycler): Use interface callback instead of AMS.
+            ActivityManager.getService().activityResumed(token);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
     public int getTargetState() {
-        return RESUMED;
+        return ON_RESUME;
     }
 
 
@@ -72,12 +87,14 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mProcState);
+        dest.writeBoolean(mUpdateProcState);
         dest.writeBoolean(mIsForward);
     }
 
     /** Read from Parcel. */
     private ResumeActivityItem(Parcel in) {
         mProcState = in.readInt();
+        mUpdateProcState = in.readBoolean();
         mIsForward = in.readBoolean();
     }
 
@@ -101,14 +118,22 @@
             return false;
         }
         final ResumeActivityItem other = (ResumeActivityItem) o;
-        return mProcState == other.mProcState && mIsForward == other.mIsForward;
+        return mProcState == other.mProcState && mUpdateProcState == other.mUpdateProcState
+                && mIsForward == other.mIsForward;
     }
 
     @Override
     public int hashCode() {
         int result = 17;
         result = 31 * result + mProcState;
+        result = 31 * result + (mUpdateProcState ? 1 : 0);
         result = 31 * result + (mIsForward ? 1 : 0);
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "ResumeActivityItem{procState=" + mProcState
+                + ",updateProcState=" + mUpdateProcState + ",isForward=" + mIsForward + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index d62c507..6e49386 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -22,7 +22,6 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Trace;
-import android.util.Slog;
 
 /**
  * Request to move an activity to stopped state.
@@ -35,32 +34,28 @@
     private final boolean mShowWindow;
     private final int mConfigChanges;
 
-    private int mLifecycleSeq;
-
     public StopActivityItem(boolean showWindow, int configChanges) {
         mShowWindow = showWindow;
         mConfigChanges = configChanges;
     }
 
     @Override
-    public void prepare(ClientTransactionHandler client, IBinder token) {
-        mLifecycleSeq = client.getLifecycleSeq();
-        if (DEBUG_ORDER) {
-            Slog.d(TAG, "Stop transaction for " + client + " received seq: "
-                    + mLifecycleSeq);
-        }
-    }
-
-    @Override
-    public void execute(ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStop");
-        client.handleStopActivity(token, mShowWindow, mConfigChanges, mLifecycleSeq);
+        client.handleStopActivity(token, mShowWindow, mConfigChanges, pendingActions);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
     @Override
+    public void postExecute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
+        client.reportStop(pendingActions);
+    }
+
+    @Override
     public int getTargetState() {
-        return STOPPED;
+        return ON_STOP;
     }
 
 
@@ -109,4 +104,10 @@
         result = 31 * result + mConfigChanges;
         return result;
     }
+
+    @Override
+    public String toString() {
+        return "StopActivityItem{showWindow=" + mShowWindow + ",configChanges=" + mConfigChanges
+                + "}";
+    }
 }
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
new file mode 100644
index 0000000..bd24824
--- /dev/null
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 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.app.servertransaction;
+
+import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESTART;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+
+import android.app.ActivityThread.ActivityClientRecord;
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.util.IntArray;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
+/**
+ * Class that manages transaction execution in the correct order.
+ * @hide
+ */
+public class TransactionExecutor {
+
+    private static final boolean DEBUG_RESOLVER = false;
+    private static final String TAG = "TransactionExecutor";
+
+    private ClientTransactionHandler mTransactionHandler;
+    private PendingTransactionActions mPendingActions = new PendingTransactionActions();
+
+    // Temp holder for lifecycle path.
+    // No direct transition between two states should take more than one complete cycle of 6 states.
+    @ActivityLifecycleItem.LifecycleState
+    private IntArray mLifecycleSequence = new IntArray(6);
+
+    /** Initialize an instance with transaction handler, that will execute all requested actions. */
+    public TransactionExecutor(ClientTransactionHandler clientTransactionHandler) {
+        mTransactionHandler = clientTransactionHandler;
+    }
+
+    /**
+     * Resolve transaction.
+     * First all callbacks will be executed in the order they appear in the list. If a callback
+     * requires a certain pre- or post-execution state, the client will be transitioned accordingly.
+     * Then the client will cycle to the final lifecycle state if provided. Otherwise, it will
+     * either remain in the initial state, or last state needed by a callback.
+     */
+    public void execute(ClientTransaction transaction) {
+        final IBinder token = transaction.getActivityToken();
+        log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);
+
+        executeCallbacks(transaction);
+
+        executeLifecycleState(transaction);
+        mPendingActions.clear();
+        log("End resolving transaction");
+    }
+
+    /** Cycle through all states requested by callbacks and execute them at proper times. */
+    @VisibleForTesting
+    public void executeCallbacks(ClientTransaction transaction) {
+        final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
+        if (callbacks == null) {
+            // No callbacks to execute, return early.
+            return;
+        }
+        log("Resolving callbacks");
+
+        final IBinder token = transaction.getActivityToken();
+        ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
+        final int size = callbacks.size();
+        for (int i = 0; i < size; ++i) {
+            final ClientTransactionItem item = callbacks.get(i);
+            log("Resolving callback: " + item);
+            final int preExecutionState = item.getPreExecutionState();
+            if (preExecutionState != UNDEFINED) {
+                cycleToPath(r, preExecutionState);
+            }
+
+            item.execute(mTransactionHandler, token, mPendingActions);
+            item.postExecute(mTransactionHandler, token, mPendingActions);
+            if (r == null) {
+                // Launch activity request will create an activity record.
+                r = mTransactionHandler.getActivityClient(token);
+            }
+
+            final int postExecutionState = item.getPostExecutionState();
+            if (postExecutionState != UNDEFINED) {
+                cycleToPath(r, postExecutionState);
+            }
+        }
+    }
+
+    /** Transition to the final state if requested by the transaction. */
+    private void executeLifecycleState(ClientTransaction transaction) {
+        final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
+        if (lifecycleItem == null) {
+            // No lifecycle request, return early.
+            return;
+        }
+        log("Resolving lifecycle state: " + lifecycleItem);
+
+        final IBinder token = transaction.getActivityToken();
+        final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
+
+        // Cycle to the state right before the final requested state.
+        cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */);
+
+        // Execute the final transition with proper parameters.
+        lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
+        lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
+    }
+
+    /** Transition the client between states. */
+    @VisibleForTesting
+    public void cycleToPath(ActivityClientRecord r, int finish) {
+        cycleToPath(r, finish, false /* excludeLastState */);
+    }
+
+    /**
+     * Transition the client between states with an option not to perform the last hop in the
+     * sequence. This is used when resolving lifecycle state request, when the last transition must
+     * be performed with some specific parameters.
+     */
+    private void cycleToPath(ActivityClientRecord r, int finish,
+            boolean excludeLastState) {
+        final int start = r.getLifecycleState();
+        log("Cycle from: " + start + " to: " + finish + " excludeLastState:" + excludeLastState);
+        initLifecyclePath(start, finish, excludeLastState);
+        performLifecycleSequence(r);
+    }
+
+    /** Transition the client through previously initialized state sequence. */
+    private void performLifecycleSequence(ActivityClientRecord r) {
+        final int size = mLifecycleSequence.size();
+        for (int i = 0, state; i < size; i++) {
+            state = mLifecycleSequence.get(i);
+            log("Transitioning to state: " + state);
+            switch (state) {
+                case ON_CREATE:
+                    mTransactionHandler.handleLaunchActivity(r, mPendingActions);
+                    break;
+                case ON_START:
+                    mTransactionHandler.handleStartActivity(r, mPendingActions);
+                    break;
+                case ON_RESUME:
+                    mTransactionHandler.handleResumeActivity(r.token, false /* clearHide */,
+                            r.isForward, "LIFECYCLER_RESUME_ACTIVITY");
+                    break;
+                case ON_PAUSE:
+                    mTransactionHandler.handlePauseActivity(r.token, false /* finished */,
+                            false /* userLeaving */, 0 /* configChanges */,
+                            true /* dontReport */, mPendingActions);
+                    break;
+                case ON_STOP:
+                    mTransactionHandler.handleStopActivity(r.token, false /* show */,
+                            0 /* configChanges */, mPendingActions);
+                    break;
+                case ON_DESTROY:
+                    mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
+                            0 /* configChanges */, false /* getNonConfigInstance */);
+                    break;
+                case ON_RESTART:
+                    mTransactionHandler.performRestartActivity(r.token, false /* start */);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unexpected lifecycle state: " + state);
+            }
+        }
+    }
+
+    /**
+     * Calculate the path through main lifecycle states for an activity and fill
+     * @link #mLifecycleSequence} with values starting with the state that follows the initial
+     * state.
+     */
+    public void initLifecyclePath(int start, int finish, boolean excludeLastState) {
+        mLifecycleSequence.clear();
+        if (finish >= start) {
+            // just go there
+            for (int i = start + 1; i <= finish; i++) {
+                mLifecycleSequence.add(i);
+            }
+        } else { // finish < start, can't just cycle down
+            if (start == ON_PAUSE && finish == ON_RESUME) {
+                // Special case when we can just directly go to resumed state.
+                mLifecycleSequence.add(ON_RESUME);
+            } else if (start <= ON_STOP && finish >= ON_START) {
+                // Restart and go to required state.
+
+                // Go to stopped state first.
+                for (int i = start + 1; i <= ON_STOP; i++) {
+                    mLifecycleSequence.add(i);
+                }
+                // Restart
+                mLifecycleSequence.add(ON_RESTART);
+                // Go to required state
+                for (int i = ON_START; i <= finish; i++) {
+                    mLifecycleSequence.add(i);
+                }
+            } else {
+                // Relaunch and go to required state
+
+                // Go to destroyed state first.
+                for (int i = start + 1; i <= ON_DESTROY; i++) {
+                    mLifecycleSequence.add(i);
+                }
+                // Go to required state
+                for (int i = ON_CREATE; i <= finish; i++) {
+                    mLifecycleSequence.add(i);
+                }
+            }
+        }
+
+        // Remove last transition in case we want to perform it with some specific params.
+        if (excludeLastState && mLifecycleSequence.size() != 0) {
+            mLifecycleSequence.remove(mLifecycleSequence.size() - 1);
+        }
+    }
+
+    @VisibleForTesting
+    public int[] getLifecycleSequence() {
+        return mLifecycleSequence.toArray();
+    }
+
+    private static void log(String message) {
+        if (DEBUG_RESOLVER) {
+            Slog.d(TAG, message);
+        }
+    }
+}
diff --git a/core/java/android/app/servertransaction/WindowVisibilityItem.java b/core/java/android/app/servertransaction/WindowVisibilityItem.java
index 8e88b38..6fcdcfa 100644
--- a/core/java/android/app/servertransaction/WindowVisibilityItem.java
+++ b/core/java/android/app/servertransaction/WindowVisibilityItem.java
@@ -18,6 +18,7 @@
 
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 
+import android.app.ClientTransactionHandler;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Trace;
@@ -35,7 +36,8 @@
     }
 
     @Override
-    public void execute(android.app.ClientTransactionHandler client, IBinder token) {
+    public void execute(ClientTransactionHandler client, IBinder token,
+            PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityShowWindow");
         client.handleWindowVisibility(token, mShowWindow);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -82,4 +84,9 @@
     public int hashCode() {
         return 17 + 31 * (mShowWindow ? 1 : 0);
     }
+
+    @Override
+    public String toString() {
+        return "WindowVisibilityItem{showWindow=" + mShowWindow + "}";
+    }
 }
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
new file mode 100644
index 0000000..6e52f38
--- /dev/null
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -0,0 +1,21 @@
+/**
+ * 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.app.slice;
+
+/** @hide */
+interface ISliceManager {
+}
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
new file mode 100644
index 0000000..e99f676
--- /dev/null
+++ b/core/java/android/app/slice/SliceManager.java
@@ -0,0 +1,39 @@
+/*
+ * 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.app.slice;
+
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.Handler;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+
+/**
+ * @hide
+ */
+@SystemService(Context.SLICE_SERVICE)
+public class SliceManager {
+
+    private final ISliceManager mService;
+    private final Context mContext;
+
+    public SliceManager(Context context, Handler handler) throws ServiceNotFoundException {
+        mContext = context;
+        mService = ISliceManager.Stub.asInterface(
+                ServiceManager.getServiceOrThrow(Context.SLICE_SERVICE));
+    }
+}
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index fd1b0e0..75ce4fb 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -17,15 +17,17 @@
 package android.appwidget;
 
 import android.annotation.NonNull;
+import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.res.ResourceId;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.content.ComponentName;
 import android.os.UserHandle;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
@@ -69,6 +71,23 @@
     public static final int WIDGET_CATEGORY_SEARCHBOX = 4;
 
     /**
+     * The widget can be reconfigured anytime after it is bound by starting the
+     * {@link #configure} activity.
+     *
+     * @see #widgetFeatures
+     */
+    public static final int WIDGET_FEATURE_RECONFIGURABLE = 1;
+
+    /**
+     * The widget is added directly by the app, and the host may hide this widget when providing
+     * the user with the list of available widgets to choose from.
+     *
+     * @see AppWidgetManager#requestPinAppWidget(ComponentName, Bundle, PendingIntent)
+     * @see #widgetFeatures
+     */
+    public static final int WIDGET_FEATURE_HIDE_FROM_PICKER = 2;
+
+    /**
      * Identity of this AppWidget component.  This component should be a {@link
      * android.content.BroadcastReceiver}, and it will be sent the AppWidget intents
      * {@link android.appwidget as described in the AppWidget package documentation}.
@@ -209,6 +228,15 @@
      */
     public int widgetCategory;
 
+    /**
+     * Flags indicating various features supported by the widget. These are hints to the widget
+     * host, and do not actually change the behavior of the widget.
+     *
+     * @see #WIDGET_FEATURE_RECONFIGURABLE
+     * @see #WIDGET_FEATURE_HIDE_FROM_PICKER
+     */
+    public int widgetFeatures;
+
     /** @hide */
     public ActivityInfo providerInfo;
 
@@ -221,9 +249,7 @@
      */
     @SuppressWarnings("deprecation")
     public AppWidgetProviderInfo(Parcel in) {
-        if (0 != in.readInt()) {
-            this.provider = new ComponentName(in);
-        }
+        this.provider = in.readTypedObject(ComponentName.CREATOR);
         this.minWidth = in.readInt();
         this.minHeight = in.readInt();
         this.minResizeWidth = in.readInt();
@@ -231,16 +257,15 @@
         this.updatePeriodMillis = in.readInt();
         this.initialLayout = in.readInt();
         this.initialKeyguardLayout = in.readInt();
-        if (0 != in.readInt()) {
-            this.configure = new ComponentName(in);
-        }
+        this.configure = in.readTypedObject(ComponentName.CREATOR);
         this.label = in.readString();
         this.icon = in.readInt();
         this.previewImage = in.readInt();
         this.autoAdvanceViewId = in.readInt();
         this.resizeMode = in.readInt();
         this.widgetCategory = in.readInt();
-        this.providerInfo = in.readParcelable(null);
+        this.providerInfo = in.readTypedObject(ActivityInfo.CREATOR);
+        this.widgetFeatures = in.readInt();
     }
 
     /**
@@ -308,13 +333,8 @@
 
     @Override
     @SuppressWarnings("deprecation")
-    public void writeToParcel(android.os.Parcel out, int flags) {
-        if (this.provider != null) {
-            out.writeInt(1);
-            this.provider.writeToParcel(out, flags);
-        } else {
-            out.writeInt(0);
-        }
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeTypedObject(this.provider, flags);
         out.writeInt(this.minWidth);
         out.writeInt(this.minHeight);
         out.writeInt(this.minResizeWidth);
@@ -322,19 +342,15 @@
         out.writeInt(this.updatePeriodMillis);
         out.writeInt(this.initialLayout);
         out.writeInt(this.initialKeyguardLayout);
-        if (this.configure != null) {
-            out.writeInt(1);
-            this.configure.writeToParcel(out, flags);
-        } else {
-            out.writeInt(0);
-        }
+        out.writeTypedObject(this.configure, flags);
         out.writeString(this.label);
         out.writeInt(this.icon);
         out.writeInt(this.previewImage);
         out.writeInt(this.autoAdvanceViewId);
         out.writeInt(this.resizeMode);
         out.writeInt(this.widgetCategory);
-        out.writeParcelable(this.providerInfo, flags);
+        out.writeTypedObject(this.providerInfo, flags);
+        out.writeInt(this.widgetFeatures);
     }
 
     @Override
@@ -357,6 +373,7 @@
         that.resizeMode = this.resizeMode;
         that.widgetCategory = this.widgetCategory;
         that.providerInfo = this.providerInfo;
+        that.widgetFeatures = this.widgetFeatures;
         return that;
     }
 
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 578a5b8..c7be0f3 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2009-2016 The Android Open Source Project
- * Copyright (C) 2015 Samsung LSI
+ * Copyright 2009-2016 The Android Open Source Project
+ * Copyright 2015 Samsung LSI
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -132,9 +132,8 @@
      * respectively.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_STATE_CHANGED =
-            "android.bluetooth.adapter.action.STATE_CHANGED";
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
+            ACTION_STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED";
 
     /**
      * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
@@ -144,8 +143,7 @@
      * {@link #STATE_ON},
      * {@link #STATE_TURNING_OFF},
      */
-    public static final String EXTRA_STATE =
-            "android.bluetooth.adapter.extra.STATE";
+    public static final String EXTRA_STATE = "android.bluetooth.adapter.extra.STATE";
     /**
      * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
      * intents to request the previous power state. Possible values are:
@@ -158,11 +156,17 @@
             "android.bluetooth.adapter.extra.PREVIOUS_STATE";
 
     /** @hide */
-    @IntDef({STATE_OFF, STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, STATE_BLE_TURNING_ON,
-            STATE_BLE_ON, STATE_BLE_TURNING_OFF})
+    @IntDef({
+            STATE_OFF,
+            STATE_TURNING_ON,
+            STATE_ON,
+            STATE_TURNING_OFF,
+            STATE_BLE_TURNING_ON,
+            STATE_BLE_ON,
+            STATE_BLE_TURNING_OFF
+    })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface AdapterState {
-    }
+    public @interface AdapterState {}
 
     /**
      * Indicates the local Bluetooth adapter is off.
@@ -254,9 +258,8 @@
      * application can be notified when the device has ended discoverability.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
      */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_REQUEST_DISCOVERABLE =
-            "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
+            ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
 
     /**
      * Used as an optional int extra field in {@link
@@ -282,9 +285,8 @@
      * for global notification whenever Bluetooth is turned on or off.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
      */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_REQUEST_ENABLE =
-            "android.bluetooth.adapter.action.REQUEST_ENABLE";
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
+            ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE";
 
     /**
      * Activity Action: Show a system activity that allows the user to turn off
@@ -305,9 +307,8 @@
      *
      * @hide
      */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_REQUEST_DISABLE =
-            "android.bluetooth.adapter.action.REQUEST_DISABLE";
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
+            ACTION_REQUEST_DISABLE = "android.bluetooth.adapter.action.REQUEST_DISABLE";
 
     /**
      * Activity Action: Show a system activity that allows user to enable BLE scans even when
@@ -334,9 +335,8 @@
      * respectively.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
      */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_SCAN_MODE_CHANGED =
-            "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
+            ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
 
     /**
      * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
@@ -359,8 +359,7 @@
     /** @hide */
     @IntDef({SCAN_MODE_NONE, SCAN_MODE_CONNECTABLE, SCAN_MODE_CONNECTABLE_DISCOVERABLE})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface ScanMode {
-    }
+    public @interface ScanMode {}
 
     /**
      * Indicates that both inquiry scan and page scan are disabled on the local
@@ -396,17 +395,15 @@
      * discovery.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_DISCOVERY_STARTED =
-            "android.bluetooth.adapter.action.DISCOVERY_STARTED";
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
+            ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED";
     /**
      * Broadcast Action: The local Bluetooth adapter has finished the device
      * discovery process.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_DISCOVERY_FINISHED =
-            "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
+            ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
 
     /**
      * Broadcast Action: The local Bluetooth adapter has changed its friendly
@@ -416,9 +413,8 @@
      * the name.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_LOCAL_NAME_CHANGED =
-            "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
+            ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
     /**
      * Used as a String extra field in {@link #ACTION_LOCAL_NAME_CHANGED}
      * intents to request the local Bluetooth name.
@@ -451,8 +447,8 @@
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
+            ACTION_CONNECTION_STATE_CHANGED =
             "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
 
     /**
@@ -476,8 +472,7 @@
      *
      * @hide
      */
-    @SystemApi
-    public static final String ACTION_BLE_STATE_CHANGED =
+    @SystemApi public static final String ACTION_BLE_STATE_CHANGED =
             "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
 
     /**
@@ -574,8 +569,7 @@
 
     private final IBluetoothManager mManagerService;
     private IBluetooth mService;
-    private final ReentrantReadWriteLock mServiceLock =
-            new ReentrantReadWriteLock();
+    private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
 
     private final Object mLock = new Object();
     private final Map<LeScanCallback, ScanCallback> mLeScanClients;
@@ -655,8 +649,9 @@
         if (address == null || address.length != 6) {
             throw new IllegalArgumentException("Bluetooth address must have 6 bytes");
         }
-        return new BluetoothDevice(String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
-                address[0], address[1], address[2], address[3], address[4], address[5]));
+        return new BluetoothDevice(
+                String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1],
+                        address[2], address[3], address[4], address[5]));
     }
 
     /**
@@ -668,7 +663,9 @@
      * on this device before calling this method.
      */
     public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
-        if (!getLeAccess()) return null;
+        if (!getLeAccess()) {
+            return null;
+        }
         synchronized (mLock) {
             if (sBluetoothLeAdvertiser == null) {
                 sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService);
@@ -698,8 +695,7 @@
 
         synchronized (mLock) {
             if (sPeriodicAdvertisingManager == null) {
-                sPeriodicAdvertisingManager =
-                        new PeriodicAdvertisingManager(mManagerService);
+                sPeriodicAdvertisingManager = new PeriodicAdvertisingManager(mManagerService);
             }
         }
         return sPeriodicAdvertisingManager;
@@ -709,7 +705,9 @@
      * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations.
      */
     public BluetoothLeScanner getBluetoothLeScanner() {
-        if (!getLeAccess()) return null;
+        if (!getLeAccess()) {
+            return null;
+        }
         synchronized (mLock) {
             if (sBluetoothLeScanner == null) {
                 sBluetoothLeScanner = new BluetoothLeScanner(mManagerService);
@@ -729,7 +727,9 @@
     public boolean isEnabled() {
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isEnabled();
+            if (mService != null) {
+                return mService.isEnabled();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -750,7 +750,9 @@
     @SystemApi
     public boolean isLeEnabled() {
         final int state = getLeState();
-        if (DBG) Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state));
+        if (DBG) {
+            Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state));
+        }
         return (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON);
     }
 
@@ -781,12 +783,16 @@
      */
     @SystemApi
     public boolean disableBLE() {
-        if (!isBleScanAlwaysAvailable()) return false;
+        if (!isBleScanAlwaysAvailable()) {
+            return false;
+        }
 
         int state = getLeState();
         if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON) {
             String packageName = ActivityThread.currentPackageName();
-            if (DBG) Log.d(TAG, "disableBLE(): de-registering " + packageName);
+            if (DBG) {
+                Log.d(TAG, "disableBLE(): de-registering " + packageName);
+            }
             try {
                 mManagerService.updateBleAppCount(mToken, false, packageName);
             } catch (RemoteException e) {
@@ -795,7 +801,9 @@
             return true;
         }
 
-        if (DBG) Log.d(TAG, "disableBLE(): Already disabled");
+        if (DBG) {
+            Log.d(TAG, "disableBLE(): Already disabled");
+        }
         return false;
     }
 
@@ -832,16 +840,22 @@
      */
     @SystemApi
     public boolean enableBLE() {
-        if (!isBleScanAlwaysAvailable()) return false;
+        if (!isBleScanAlwaysAvailable()) {
+            return false;
+        }
 
         try {
             String packageName = ActivityThread.currentPackageName();
             mManagerService.updateBleAppCount(mToken, true, packageName);
             if (isLeEnabled()) {
-                if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled");
+                if (DBG) {
+                    Log.d(TAG, "enableBLE(): Bluetooth already enabled");
+                }
                 return true;
             }
-            if (DBG) Log.d(TAG, "enableBLE(): Calling enable");
+            if (DBG) {
+                Log.d(TAG, "enableBLE(): Calling enable");
+            }
             return mManagerService.enable(packageName);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
@@ -877,19 +891,16 @@
         }
 
         // Consider all internal states as OFF
-        if (state == BluetoothAdapter.STATE_BLE_ON
-                || state == BluetoothAdapter.STATE_BLE_TURNING_ON
+        if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON
                 || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
             if (VDBG) {
-                Log.d(TAG,
-                        "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF");
+                Log.d(TAG, "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF");
             }
             state = BluetoothAdapter.STATE_OFF;
         }
         if (VDBG) {
-            Log.d(TAG,
-                    "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState(
-                            state));
+            Log.d(TAG, "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState(
+                    state));
         }
         return state;
     }
@@ -926,7 +937,9 @@
             mServiceLock.readLock().unlock();
         }
 
-        if (VDBG) Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state));
+        if (VDBG) {
+            Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state));
+        }
         return state;
     }
 
@@ -967,7 +980,9 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean enable() {
         if (isEnabled()) {
-            if (DBG) Log.d(TAG, "enable(): BT already enabled!");
+            if (DBG) {
+                Log.d(TAG, "enable(): BT already enabled!");
+            }
             return true;
         }
         try {
@@ -1093,10 +1108,14 @@
      * @hide
      */
     public ParcelUuid[] getUuids() {
-        if (getState() != STATE_ON) return null;
+        if (getState() != STATE_ON) {
+            return null;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.getUuids();
+            if (mService != null) {
+                return mService.getUuids();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1121,10 +1140,14 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean setName(String name) {
-        if (getState() != STATE_ON) return false;
+        if (getState() != STATE_ON) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.setName(name);
+            if (mService != null) {
+                return mService.setName(name);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1143,10 +1166,14 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public BluetoothClass getBluetoothClass() {
-        if (getState() != STATE_ON) return null;
+        if (getState() != STATE_ON) {
+            return null;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.getBluetoothClass();
+            if (mService != null) {
+                return mService.getBluetoothClass();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1168,10 +1195,14 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setBluetoothClass(BluetoothClass bluetoothClass) {
-        if (getState() != STATE_ON) return false;
+        if (getState() != STATE_ON) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.setBluetoothClass(bluetoothClass);
+            if (mService != null) {
+                return mService.setBluetoothClass(bluetoothClass);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1198,10 +1229,14 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     @ScanMode
     public int getScanMode() {
-        if (getState() != STATE_ON) return SCAN_MODE_NONE;
+        if (getState() != STATE_ON) {
+            return SCAN_MODE_NONE;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.getScanMode();
+            if (mService != null) {
+                return mService.getScanMode();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1239,10 +1274,14 @@
      * @hide
      */
     public boolean setScanMode(@ScanMode int mode, int duration) {
-        if (getState() != STATE_ON) return false;
+        if (getState() != STATE_ON) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.setScanMode(mode, duration);
+            if (mService != null) {
+                return mService.setScanMode(mode, duration);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1253,17 +1292,23 @@
 
     /** @hide */
     public boolean setScanMode(int mode) {
-        if (getState() != STATE_ON) return false;
+        if (getState() != STATE_ON) {
+            return false;
+        }
         /* getDiscoverableTimeout() to use the latest from NV than use 0 */
         return setScanMode(mode, getDiscoverableTimeout());
     }
 
     /** @hide */
     public int getDiscoverableTimeout() {
-        if (getState() != STATE_ON) return -1;
+        if (getState() != STATE_ON) {
+            return -1;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.getDiscoverableTimeout();
+            if (mService != null) {
+                return mService.getDiscoverableTimeout();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1274,10 +1319,14 @@
 
     /** @hide */
     public void setDiscoverableTimeout(int timeout) {
-        if (getState() != STATE_ON) return;
+        if (getState() != STATE_ON) {
+            return;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) mService.setDiscoverableTimeout(timeout);
+            if (mService != null) {
+                mService.setDiscoverableTimeout(timeout);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1296,7 +1345,9 @@
     public long getDiscoveryEndMillis() {
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.getDiscoveryEndMillis();
+            if (mService != null) {
+                return mService.getDiscoveryEndMillis();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1336,10 +1387,14 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean startDiscovery() {
-        if (getState() != STATE_ON) return false;
+        if (getState() != STATE_ON) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.startDiscovery();
+            if (mService != null) {
+                return mService.startDiscovery();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1366,10 +1421,14 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean cancelDiscovery() {
-        if (getState() != STATE_ON) return false;
+        if (getState() != STATE_ON) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.cancelDiscovery();
+            if (mService != null) {
+                return mService.cancelDiscovery();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1398,10 +1457,14 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     public boolean isDiscovering() {
-        if (getState() != STATE_ON) return false;
+        if (getState() != STATE_ON) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isDiscovering();
+            if (mService != null) {
+                return mService.isDiscovering();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1416,10 +1479,14 @@
      * @return true if Multiple Advertisement feature is supported
      */
     public boolean isMultipleAdvertisementSupported() {
-        if (getState() != STATE_ON) return false;
+        if (getState() != STATE_ON) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isMultiAdvertisementSupported();
+            if (mService != null) {
+                return mService.isMultiAdvertisementSupported();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isMultipleAdvertisementSupported, error: ", e);
         } finally {
@@ -1454,10 +1521,14 @@
      * @return true if chipset supports on-chip filtering
      */
     public boolean isOffloadedFilteringSupported() {
-        if (!getLeAccess()) return false;
+        if (!getLeAccess()) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isOffloadedFilteringSupported();
+            if (mService != null) {
+                return mService.isOffloadedFilteringSupported();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e);
         } finally {
@@ -1472,10 +1543,14 @@
      * @return true if chipset supports on-chip scan batching
      */
     public boolean isOffloadedScanBatchingSupported() {
-        if (!getLeAccess()) return false;
+        if (!getLeAccess()) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isOffloadedScanBatchingSupported();
+            if (mService != null) {
+                return mService.isOffloadedScanBatchingSupported();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isOffloadedScanBatchingSupported, error: ", e);
         } finally {
@@ -1490,10 +1565,14 @@
      * @return true if chipset supports LE 2M PHY feature
      */
     public boolean isLe2MPhySupported() {
-        if (!getLeAccess()) return false;
+        if (!getLeAccess()) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isLe2MPhySupported();
+            if (mService != null) {
+                return mService.isLe2MPhySupported();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isExtendedAdvertisingSupported, error: ", e);
         } finally {
@@ -1508,10 +1587,14 @@
      * @return true if chipset supports LE Coded PHY feature
      */
     public boolean isLeCodedPhySupported() {
-        if (!getLeAccess()) return false;
+        if (!getLeAccess()) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isLeCodedPhySupported();
+            if (mService != null) {
+                return mService.isLeCodedPhySupported();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isLeCodedPhySupported, error: ", e);
         } finally {
@@ -1526,10 +1609,14 @@
      * @return true if chipset supports LE Extended Advertising feature
      */
     public boolean isLeExtendedAdvertisingSupported() {
-        if (!getLeAccess()) return false;
+        if (!getLeAccess()) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isLeExtendedAdvertisingSupported();
+            if (mService != null) {
+                return mService.isLeExtendedAdvertisingSupported();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isLeExtendedAdvertisingSupported, error: ", e);
         } finally {
@@ -1544,10 +1631,14 @@
      * @return true if chipset supports LE Periodic Advertising feature
      */
     public boolean isLePeriodicAdvertisingSupported() {
-        if (!getLeAccess()) return false;
+        if (!getLeAccess()) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isLePeriodicAdvertisingSupported();
+            if (mService != null) {
+                return mService.isLePeriodicAdvertisingSupported();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isLePeriodicAdvertisingSupported, error: ", e);
         } finally {
@@ -1563,10 +1654,14 @@
      * @return the maximum LE advertising data length.
      */
     public int getLeMaximumAdvertisingDataLength() {
-        if (!getLeAccess()) return 0;
+        if (!getLeAccess()) {
+            return 0;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.getLeMaximumAdvertisingDataLength();
+            if (mService != null) {
+                return mService.getLeMaximumAdvertisingDataLength();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get getLeMaximumAdvertisingDataLength, error: ", e);
         } finally {
@@ -1582,7 +1677,9 @@
      * @hide
      */
     public boolean isHardwareTrackingFiltersAvailable() {
-        if (!getLeAccess()) return false;
+        if (!getLeAccess()) {
+            return false;
+        }
         try {
             IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
             if (iGatt == null) {
@@ -1669,7 +1766,9 @@
         }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return toDeviceSet(mService.getBondedDevices());
+            if (mService != null) {
+                return toDeviceSet(mService.getBondedDevices());
+            }
             return toDeviceSet(new BluetoothDevice[0]);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
@@ -1723,10 +1822,14 @@
      * @hide
      */
     public int getConnectionState() {
-        if (getState() != STATE_ON) return BluetoothAdapter.STATE_DISCONNECTED;
+        if (getState() != STATE_ON) {
+            return BluetoothAdapter.STATE_DISCONNECTED;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.getAdapterConnectionState();
+            if (mService != null) {
+                return mService.getAdapterConnectionState();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "getConnectionState:", e);
         } finally {
@@ -1750,10 +1853,14 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     public int getProfileConnectionState(int profile) {
-        if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED;
+        if (getState() != STATE_ON) {
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.getProfileConnectionState(profile);
+            if (mService != null) {
+                return mService.getProfileConnectionState(profile);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "getProfileConnectionState:", e);
         } finally {
@@ -1790,7 +1897,7 @@
      * <p>Valid RFCOMM channels are in range 1 to 30.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
      * <p>To auto assign a channel without creating a SDP record use
-     * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number.
+     * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number.
      *
      * @param channel RFCOMM channel to listen on
      * @param mitm enforce man-in-the-middle protection for authentication.
@@ -1802,10 +1909,10 @@
      * @hide
      */
     public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm,
-            boolean min16DigitPin)
-            throws IOException {
-        BluetoothServerSocket socket = new BluetoothServerSocket(
-                BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm, min16DigitPin);
+            boolean min16DigitPin) throws IOException {
+        BluetoothServerSocket socket =
+                new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm,
+                        min16DigitPin);
         int errno = socket.mSocket.bindListen();
         if (channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
             socket.setChannel(socket.mSocket.getPort());
@@ -1913,8 +2020,8 @@
      * permissions, or channel in use.
      * @hide
      */
-    public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(
-            String name, UUID uuid) throws IOException {
+    public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid)
+            throws IOException {
         return createNewRfcommSocketAndRecord(name, uuid, false, true);
     }
 
@@ -1922,8 +2029,8 @@
     private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid,
             boolean auth, boolean encrypt) throws IOException {
         BluetoothServerSocket socket;
-        socket = new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, auth,
-                encrypt, new ParcelUuid(uuid));
+        socket = new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, auth, encrypt,
+                new ParcelUuid(uuid));
         socket.setServiceName(name);
         int errno = socket.mSocket.bindListen();
         if (errno != 0) {
@@ -1945,8 +2052,8 @@
      * @hide
      */
     public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
-        BluetoothServerSocket socket = new BluetoothServerSocket(
-                BluetoothSocket.TYPE_RFCOMM, false, false, port);
+        BluetoothServerSocket socket =
+                new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, false, port);
         int errno = socket.mSocket.bindListen();
         if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
             socket.setChannel(socket.mSocket.getPort());
@@ -1969,10 +2076,9 @@
      * permissions.
      * @hide
      */
-    public BluetoothServerSocket listenUsingEncryptedRfcommOn(int port)
-            throws IOException {
-        BluetoothServerSocket socket = new BluetoothServerSocket(
-                BluetoothSocket.TYPE_RFCOMM, false, true, port);
+    public BluetoothServerSocket listenUsingEncryptedRfcommOn(int port) throws IOException {
+        BluetoothServerSocket socket =
+                new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, true, port);
         int errno = socket.mSocket.bindListen();
         if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
             socket.setChannel(socket.mSocket.getPort());
@@ -1996,8 +2102,8 @@
      * @hide
      */
     public static BluetoothServerSocket listenUsingScoOn() throws IOException {
-        BluetoothServerSocket socket = new BluetoothServerSocket(
-                BluetoothSocket.TYPE_SCO, false, false, -1);
+        BluetoothServerSocket socket =
+                new BluetoothServerSocket(BluetoothSocket.TYPE_SCO, false, false, -1);
         int errno = socket.mSocket.bindListen();
         if (errno < 0) {
             //TODO(BT): Throw the same exception error code
@@ -2011,7 +2117,7 @@
      * Construct an encrypted, authenticated, L2CAP server socket.
      * Call #accept to retrieve connections to this socket.
      * <p>To auto assign a port without creating a SDP record use
-     * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
+     * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
      *
      * @param port the PSM to listen on
      * @param mitm enforce man-in-the-middle protection for authentication.
@@ -2024,8 +2130,9 @@
      */
     public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin)
             throws IOException {
-        BluetoothServerSocket socket = new BluetoothServerSocket(
-                BluetoothSocket.TYPE_L2CAP, true, true, port, mitm, min16DigitPin);
+        BluetoothServerSocket socket =
+                new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, true, true, port, mitm,
+                        min16DigitPin);
         int errno = socket.mSocket.bindListen();
         if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
             socket.setChannel(socket.mSocket.getPort());
@@ -2043,7 +2150,7 @@
      * Construct an encrypted, authenticated, L2CAP server socket.
      * Call #accept to retrieve connections to this socket.
      * <p>To auto assign a port without creating a SDP record use
-     * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
+     * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
      *
      * @param port the PSM to listen on
      * @return An L2CAP BluetoothServerSocket
@@ -2060,7 +2167,7 @@
      * Construct an insecure L2CAP server socket.
      * Call #accept to retrieve connections to this socket.
      * <p>To auto assign a port without creating a SDP record use
-     * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
+     * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
      *
      * @param port the PSM to listen on
      * @return An L2CAP BluetoothServerSocket
@@ -2069,8 +2176,9 @@
      * @hide
      */
     public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException {
-        BluetoothServerSocket socket = new BluetoothServerSocket(
-                BluetoothSocket.TYPE_L2CAP, false, false, port, false, false);
+        BluetoothServerSocket socket =
+                new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, false, false, port, false,
+                        false);
         int errno = socket.mSocket.bindListen();
         if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
             socket.setChannel(socket.mSocket.getPort());
@@ -2114,7 +2222,9 @@
      */
     public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
             int profile) {
-        if (context == null || listener == null) return false;
+        if (context == null || listener == null) {
+            return false;
+        }
 
         if (profile == BluetoothProfile.HEADSET) {
             BluetoothHeadset headset = new BluetoothHeadset(context, listener);
@@ -2172,7 +2282,9 @@
      * @param proxy Profile proxy object
      */
     public void closeProfileProxy(int profile, BluetoothProfile proxy) {
-        if (proxy == null) return;
+        if (proxy == null) {
+            return;
+        }
 
         switch (profile) {
             case BluetoothProfile.HEADSET:
@@ -2241,7 +2353,9 @@
     private final IBluetoothManagerCallback mManagerCallback =
             new IBluetoothManagerCallback.Stub() {
                 public void onBluetoothServiceUp(IBluetooth bluetoothService) {
-                    if (DBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService);
+                    if (DBG) {
+                        Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService);
+                    }
 
                     mServiceLock.writeLock().lock();
                     mService = bluetoothService;
@@ -2263,14 +2377,22 @@
                 }
 
                 public void onBluetoothServiceDown() {
-                    if (DBG) Log.d(TAG, "onBluetoothServiceDown: " + mService);
+                    if (DBG) {
+                        Log.d(TAG, "onBluetoothServiceDown: " + mService);
+                    }
 
                     try {
                         mServiceLock.writeLock().lock();
                         mService = null;
-                        if (mLeScanClients != null) mLeScanClients.clear();
-                        if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup();
-                        if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup();
+                        if (mLeScanClients != null) {
+                            mLeScanClients.clear();
+                        }
+                        if (sBluetoothLeAdvertiser != null) {
+                            sBluetoothLeAdvertiser.cleanup();
+                        }
+                        if (sBluetoothLeScanner != null) {
+                            sBluetoothLeScanner.cleanup();
+                        }
                     } finally {
                         mServiceLock.writeLock().unlock();
                     }
@@ -2291,7 +2413,9 @@
                 }
 
                 public void onBrEdrDown() {
-                    if (VDBG) Log.i(TAG, "onBrEdrDown: " + mService);
+                    if (VDBG) {
+                        Log.i(TAG, "onBrEdrDown: " + mService);
+                    }
                 }
             };
 
@@ -2305,7 +2429,9 @@
     @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
     public boolean enableNoAutoConnect() {
         if (isEnabled()) {
-            if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT already enabled!");
+            if (DBG) {
+                Log.d(TAG, "enableNoAutoConnect(): BT already enabled!");
+            }
             return true;
         }
         try {
@@ -2354,7 +2480,10 @@
      * @hide
      */
     public interface BluetoothStateChangeCallback {
-        public void onBluetoothStateChange(boolean on);
+        /**
+         * @hide
+         */
+        void onBluetoothStateChange(boolean on);
     }
 
     /**
@@ -2363,8 +2492,7 @@
     public class StateChangeCallbackWrapper extends IBluetoothStateChangeCallback.Stub {
         private BluetoothStateChangeCallback mCallback;
 
-        StateChangeCallbackWrapper(BluetoothStateChangeCallback
-                callback) {
+        StateChangeCallbackWrapper(BluetoothStateChangeCallback callback) {
             mCallback = callback;
         }
 
@@ -2461,7 +2589,7 @@
          * if no RSSI value is available.
          * @param scanRecord The content of the advertisement record offered by the remote device.
          */
-        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord);
+        void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord);
     }
 
     /**
@@ -2497,20 +2625,28 @@
     @Deprecated
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) {
-        if (DBG) Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids));
+        if (DBG) {
+            Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids));
+        }
         if (callback == null) {
-            if (DBG) Log.e(TAG, "startLeScan: null callback");
+            if (DBG) {
+                Log.e(TAG, "startLeScan: null callback");
+            }
             return false;
         }
         BluetoothLeScanner scanner = getBluetoothLeScanner();
         if (scanner == null) {
-            if (DBG) Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner");
+            if (DBG) {
+                Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner");
+            }
             return false;
         }
 
         synchronized (mLeScanClients) {
             if (mLeScanClients.containsKey(callback)) {
-                if (DBG) Log.e(TAG, "LE Scan has already started");
+                if (DBG) {
+                    Log.e(TAG, "LE Scan has already started");
+                }
                 return false;
             }
 
@@ -2540,7 +2676,9 @@
                             }
                             List<ParcelUuid> scanServiceUuids = scanRecord.getServiceUuids();
                             if (scanServiceUuids == null || !scanServiceUuids.containsAll(uuids)) {
-                                if (DBG) Log.d(TAG, "uuids does not match");
+                                if (DBG) {
+                                    Log.d(TAG, "uuids does not match");
+                                }
                                 return;
                             }
                         }
@@ -2548,16 +2686,18 @@
                                 scanRecord.getBytes());
                     }
                 };
-                ScanSettings settings = new ScanSettings.Builder()
-                        .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
-                        .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
+                ScanSettings settings = new ScanSettings.Builder().setCallbackType(
+                        ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
+                        .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+                        .build();
 
                 List<ScanFilter> filters = new ArrayList<ScanFilter>();
                 if (serviceUuids != null && serviceUuids.length > 0) {
                     // Note scan filter does not support matching an UUID array so we put one
                     // UUID to hardware and match the whole array in callback.
-                    ScanFilter filter = new ScanFilter.Builder().setServiceUuid(
-                            new ParcelUuid(serviceUuids[0])).build();
+                    ScanFilter filter =
+                            new ScanFilter.Builder().setServiceUuid(new ParcelUuid(serviceUuids[0]))
+                                    .build();
                     filters.add(filter);
                 }
                 scanner.startScan(filters, settings, scanCallback);
@@ -2582,7 +2722,9 @@
     @Deprecated
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public void stopLeScan(LeScanCallback callback) {
-        if (DBG) Log.d(TAG, "stopLeScan()");
+        if (DBG) {
+            Log.d(TAG, "stopLeScan()");
+        }
         BluetoothLeScanner scanner = getBluetoothLeScanner();
         if (scanner == null) {
             return;
@@ -2590,7 +2732,9 @@
         synchronized (mLeScanClients) {
             ScanCallback scanCallback = mLeScanClients.remove(callback);
             if (scanCallback == null) {
-                if (DBG) Log.d(TAG, "scan not started yet");
+                if (DBG) {
+                    Log.d(TAG, "scan not started yet");
+                }
                 return;
             }
             scanner.stopScan(scanCallback);
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 1241f23..838d315 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -678,33 +678,6 @@
     }
 
     /**
-     * Get battery usage hint for Bluetooth Headset service.
-     * This is a monotonically increasing integer. Wraps to 0 at
-     * Integer.MAX_INT, and at boot.
-     * Current implementation returns the number of AT commands handled since
-     * boot. This is a good indicator for spammy headset/handsfree units that
-     * can keep the device awake by polling for cellular status updates. As a
-     * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms
-     *
-     * @param device the bluetooth headset.
-     * @return monotonically increasing battery usage hint, or a negative error code on error
-     * @hide
-     */
-    public int getBatteryUsageHint(BluetoothDevice device) {
-        if (VDBG) log("getBatteryUsageHint()");
-        final IBluetoothHeadset service = mService;
-        if (service != null && isEnabled() && isValidDevice(device)) {
-            try {
-                return service.getBatteryUsageHint(device);
-            } catch (RemoteException e) {
-                Log.e(TAG, Log.getStackTraceString(new Throwable()));
-            }
-        }
-        if (service == null) Log.w(TAG, "Proxy not attached to service");
-        return -1;
-    }
-
-    /**
      * Indicates if current platform supports voice dialing over bluetooth SCO.
      *
      * @return true if voice dialing over bluetooth is supported, false otherwise.
@@ -716,49 +689,6 @@
     }
 
     /**
-     * Accept the incoming connection.
-     * Note: This is an internal function and shouldn't be exposed
-     *
-     * @hide
-     */
-    public boolean acceptIncomingConnect(BluetoothDevice device) {
-        if (DBG) log("acceptIncomingConnect");
-        final IBluetoothHeadset service = mService;
-        if (service != null && isEnabled()) {
-            try {
-                return service.acceptIncomingConnect(device);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
-        return false;
-    }
-
-    /**
-     * Reject the incoming connection.
-     *
-     * @hide
-     */
-    public boolean rejectIncomingConnect(BluetoothDevice device) {
-        if (DBG) log("rejectIncomingConnect");
-        final IBluetoothHeadset service = mService;
-        if (service != null) {
-            try {
-                return service.rejectIncomingConnect(device);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
-        return false;
-    }
-
-    /**
      * Get the current audio state of the Headset.
      * Note: This is an internal function and shouldn't be exposed
      *
@@ -1053,50 +983,6 @@
     }
 
     /**
-     * enable WBS codec setting.
-     *
-     * @return true if successful false if there was some error such as there is no connected
-     * headset
-     * @hide
-     */
-    public boolean enableWBS() {
-        final IBluetoothHeadset service = mService;
-        if (service != null && isEnabled()) {
-            try {
-                return service.enableWBS();
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
-        return false;
-    }
-
-    /**
-     * disable WBS codec settting. It set NBS codec.
-     *
-     * @return true if successful false if there was some error such as there is no connected
-     * headset
-     * @hide
-     */
-    public boolean disableWBS() {
-        final IBluetoothHeadset service = mService;
-        if (service != null && isEnabled()) {
-            try {
-                return service.disableWBS();
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
-        return false;
-    }
-
-    /**
      * check if in-band ringing is supported for this platform.
      *
      * @return true if in-band ringing is supported false if in-band ringing is not supported
@@ -1107,30 +993,6 @@
                 com.android.internal.R.bool.config_bluetooth_hfp_inband_ringing_support);
     }
 
-    /**
-     * Send Headset the BIND response from AG to report change in the status of the
-     * HF indicators to the headset
-     *
-     * @param indId Assigned Number of the indicator (defined by SIG)
-     * @param indStatus possible values- false-Indicator is disabled, no value changes shall be
-     * sent for this indicator true-Indicator is enabled, value changes may be sent for this
-     * indicator
-     * @hide
-     */
-    public void bindResponse(int indId, boolean indStatus) {
-        final IBluetoothHeadset service = mService;
-        if (service != null && isEnabled()) {
-            try {
-                service.bindResponse(indId, indStatus);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-            if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
-        }
-    }
-
     private final IBluetoothProfileServiceConnection mConnection =
             new IBluetoothProfileServiceConnection.Stub() {
         @Override
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index 6692e13..4a00486 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -31,36 +31,33 @@
 import java.util.List;
 
 /**
- * Provides the public APIs to control the Bluetooth HID Device
- * profile.
+ * Provides the public APIs to control the Bluetooth HID Device profile.
  *
- * BluetoothHidDevice is a proxy object for controlling the Bluetooth HID
- * Device Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
- * the BluetoothHidDevice proxy object.
+ * <p>BluetoothHidDevice is a proxy object for controlling the Bluetooth HID Device Service via IPC.
+ * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object.
  *
- * {@hide}
+ * <p>{@hide}
  */
 public final class BluetoothHidDevice implements BluetoothProfile {
 
     private static final String TAG = BluetoothHidDevice.class.getSimpleName();
 
     /**
-     * Intent used to broadcast the change in connection state of the Input
-     * Host profile.
+     * Intent used to broadcast the change in connection state of the Input Host profile.
      *
      * <p>This intent will have 3 extras:
+     *
      * <ul>
-     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
-     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
-     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+     *   <li>{@link #EXTRA_STATE} - The current state of the profile.
+     *   <li>{@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.
+     *   <li>{@link BluetoothDevice#EXTRA_DEVICE} - The remote device.
      * </ul>
      *
-     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
-     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
-     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of {@link
+     * #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, {@link #STATE_CONNECTED}, {@link
+     * #STATE_DISCONNECTING}.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
-     * receive.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to receive.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_CONNECTION_STATE_CHANGED =
@@ -69,9 +66,8 @@
     /**
      * Constants representing device subclass.
      *
-     * @see #registerApp
-     * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)
      */
     public static final byte SUBCLASS1_NONE = (byte) 0x00;
     public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40;
@@ -110,8 +106,8 @@
     public static final byte ERROR_RSP_UNKNOWN = (byte) 14;
 
     /**
-     * Constants representing protocol mode used set by host. Default is always
-     * {@link #PROTOCOL_REPORT_MODE} unless notified otherwise.
+     * Constants representing protocol mode used set by host. Default is always {@link
+     * #PROTOCOL_REPORT_MODE} unless notified otherwise.
      *
      * @see BluetoothHidDeviceCallback#onSetProtocol(BluetoothDevice, byte)
      */
@@ -126,8 +122,8 @@
 
     private BluetoothAdapter mAdapter;
 
-    private static class BluetoothHidDeviceCallbackWrapper extends
-            IBluetoothHidDeviceCallback.Stub {
+    private static class BluetoothHidDeviceCallbackWrapper
+            extends IBluetoothHidDeviceCallback.Stub {
 
         private BluetoothHidDeviceCallback mCallback;
 
@@ -136,9 +132,8 @@
         }
 
         @Override
-        public void onAppStatusChanged(BluetoothDevice pluggedDevice,
-                BluetoothHidDeviceAppConfiguration config, boolean registered) {
-            mCallback.onAppStatusChanged(pluggedDevice, config, registered);
+        public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
+            mCallback.onAppStatusChanged(pluggedDevice, registered);
         }
 
         @Override
@@ -185,13 +180,11 @@
                                     doBind();
                                 }
                             } catch (IllegalStateException e) {
-                                Log.e(TAG,
-                                        "onBluetoothStateChange: could not bind to HID Dev "
-                                                + "service: ", e);
+                                Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev "
+                                        + "service: ", e);
                             } catch (SecurityException e) {
-                                Log.e(TAG,
-                                        "onBluetoothStateChange: could not bind to HID Dev "
-                                                + "service: ", e);
+                                Log.e(TAG, "onBluetoothStateChange: could not bind to HID Dev "
+                                        + "service: ", e);
                             }
                         } else {
                             Log.d(TAG, "Unbinding service...");
@@ -201,23 +194,25 @@
                 }
             };
 
-    private final ServiceConnection mConnection = new ServiceConnection() {
-        public void onServiceConnected(ComponentName className, IBinder service) {
-            Log.d(TAG, "onServiceConnected()");
-            mService = IBluetoothHidDevice.Stub.asInterface(service);
-            if (mServiceListener != null) {
-                mServiceListener.onServiceConnected(BluetoothProfile.HID_DEVICE,
-                        BluetoothHidDevice.this);
-            }
-        }
-        public void onServiceDisconnected(ComponentName className) {
-            Log.d(TAG, "onServiceDisconnected()");
-            mService = null;
-            if (mServiceListener != null) {
-                mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE);
-            }
-        }
-    };
+    private final ServiceConnection mConnection =
+            new ServiceConnection() {
+                public void onServiceConnected(ComponentName className, IBinder service) {
+                    Log.d(TAG, "onServiceConnected()");
+                    mService = IBluetoothHidDevice.Stub.asInterface(service);
+                    if (mServiceListener != null) {
+                        mServiceListener.onServiceConnected(
+                                BluetoothProfile.HID_DEVICE, BluetoothHidDevice.this);
+                    }
+                }
+
+                public void onServiceDisconnected(ComponentName className) {
+                    Log.d(TAG, "onServiceDisconnected()");
+                    mService = null;
+                    if (mServiceListener != null) {
+                        mServiceListener.onServiceDisconnected(BluetoothProfile.HID_DEVICE);
+                    }
+                }
+            };
 
     BluetoothHidDevice(Context context, ServiceListener listener) {
         Log.v(TAG, "BluetoothHidDevice");
@@ -281,9 +276,7 @@
         mServiceListener = null;
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public List<BluetoothDevice> getConnectedDevices() {
         Log.v(TAG, "getConnectedDevices()");
@@ -302,9 +295,7 @@
         return new ArrayList<BluetoothDevice>();
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
         Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states));
@@ -323,9 +314,7 @@
         return new ArrayList<BluetoothDevice>();
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
     public int getConnectionState(BluetoothDevice device) {
         Log.v(TAG, "getConnectionState(): device=" + device);
@@ -345,33 +334,31 @@
     }
 
     /**
-     * Registers application to be used for HID device. Connections to HID
-     * Device are only possible when application is registered. Only one
-     * application can be registered at time. When no longer used, application
-     * should be unregistered using
-     * {@link #unregisterApp(BluetoothHidDeviceAppConfiguration)}.
-     * The registration status should be tracked by the application by handling callback from
-     * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related
-     * to the return value of this method.
+     * Registers application to be used for HID device. Connections to HID Device are only possible
+     * when application is registered. Only one application can be registered at one time. When an
+     * application is registered, the HID Host service will be disabled until it is unregistered.
+     * When no longer used, application should be unregistered using {@link #unregisterApp()}. The
+     * registration status should be tracked by the application by handling callback from
+     * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related to
+     * the return value of this method.
      *
-     * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record.
-     * The HID Device SDP record is required.
-     * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of Incoming QoS Settings.
-     * The Incoming QoS Settings is not required. Use null or default
-     * BluetoothHidDeviceAppQosSettings.Builder for default values.
-     * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings.
-     * The Outgoing QoS Settings is not required. Use null or default
-     * BluetoothHidDeviceAppQosSettings.Builder for default values.
+     * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record. The HID
+     *     Device SDP record is required.
+     * @param inQos {@link BluetoothHidDeviceAppQosSettings} object of Incoming QoS Settings. The
+     *     Incoming QoS Settings is not required. Use null or default
+     *     BluetoothHidDeviceAppQosSettings.Builder for default values.
+     * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings. The
+     *     Outgoing QoS Settings is not required. Use null or default
+     *     BluetoothHidDeviceAppQosSettings.Builder for default values.
      * @param callback {@link BluetoothHidDeviceCallback} object to which callback messages will be
-     * sent.
-     * The BluetoothHidDeviceCallback object is required.
+     *     sent. The BluetoothHidDeviceCallback object is required.
      * @return true if the command is successfully sent; otherwise false.
      */
     public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp,
             BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos,
             BluetoothHidDeviceCallback callback) {
         Log.v(TAG, "registerApp(): sdp=" + sdp + " inQos=" + inQos + " outQos=" + outQos
-                + " callback=" + callback);
+                        + " callback=" + callback);
 
         boolean result = false;
 
@@ -382,11 +369,9 @@
         final IBluetoothHidDevice service = mService;
         if (service != null) {
             try {
-                BluetoothHidDeviceAppConfiguration config =
-                        new BluetoothHidDeviceAppConfiguration();
                 BluetoothHidDeviceCallbackWrapper cbw =
                         new BluetoothHidDeviceCallbackWrapper(callback);
-                result = service.registerApp(config, sdp, inQos, outQos, cbw);
+                result = service.registerApp(sdp, inQos, outQos, cbw);
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
             }
@@ -398,22 +383,17 @@
     }
 
     /**
-     * Unregisters application. Active connection will be disconnected and no
-     * new connections will be allowed until registered again using
-     * {@link #registerApp
+     * Unregisters application. Active connection will be disconnected and no new connections will
+     * be allowed until registered again using {@link #registerApp
      * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)}
-     * The registration status should be tracked by the application by handling callback from
-     * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related
-     * to the return value of this method.
+     * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)} The registration status should
+     * be tracked by the application by handling callback from
+     * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related to
+     * the return value of this method.
      *
-     * @param config {@link BluetoothHidDeviceAppConfiguration} object as obtained from {@link
-     * BluetoothHidDeviceCallback#onAppStatusChanged(BluetoothDevice,
-     * BluetoothHidDeviceAppConfiguration,
-     * boolean)}
      * @return true if the command is successfully sent; otherwise false.
      */
-    public boolean unregisterApp(BluetoothHidDeviceAppConfiguration config) {
+    public boolean unregisterApp() {
         Log.v(TAG, "unregisterApp()");
 
         boolean result = false;
@@ -421,7 +401,7 @@
         final IBluetoothHidDevice service = mService;
         if (service != null) {
             try {
-                result = service.unregisterApp(config);
+                result = service.unregisterApp();
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
             }
@@ -436,7 +416,7 @@
      * Sends report to remote host using interrupt channel.
      *
      * @param id Report Id, as defined in descriptor. Can be 0 in case Report Id are not defined in
-     * descriptor.
+     *     descriptor.
      * @param data Report data, not including Report Id.
      * @return true if the command is successfully sent; otherwise false.
      */
@@ -458,8 +438,8 @@
     }
 
     /**
-     * Sends report to remote host as reply for GET_REPORT request from
-     * {@link BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)}.
+     * Sends report to remote host as reply for GET_REPORT request from {@link
+     * BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)}.
      *
      * @param type Report Type, as in request.
      * @param id Report Id, as in request.
@@ -486,8 +466,8 @@
     }
 
     /**
-     * Sends error handshake message as reply for invalid SET_REPORT request
-     * from {@link BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}.
+     * Sends error handshake message as reply for invalid SET_REPORT request from {@link
+     * BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}.
      *
      * @param error Error to be sent for SET_REPORT via HANDSHAKE.
      * @return true if the command is successfully sent; otherwise false.
@@ -515,6 +495,7 @@
      * Sends Virtual Cable Unplug to currently connected host.
      *
      * @return
+     * {@hide}
      */
     public boolean unplug(BluetoothDevice device) {
         Log.v(TAG, "unplug(): device=" + device);
@@ -536,11 +517,11 @@
     }
 
     /**
-     * Initiates connection to host which is currently paired with this device.
-     * If the application is not registered, #connect(BluetoothDevice) will fail.
-     * The connection state should be tracked by the application by handling callback from
-     * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related
-     * to the return value of this method.
+     * Initiates connection to host which is currently paired with this device. If the application
+     * is not registered, #connect(BluetoothDevice) will fail. The connection state should be
+     * tracked by the application by handling callback from
+     * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related to
+     * the return value of this method.
      *
      * @return true if the command is successfully sent; otherwise false.
      */
@@ -564,10 +545,9 @@
     }
 
     /**
-     * Disconnects from currently connected host.
-     * The connection state should be tracked by the application by handling callback from
-     * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related
-     * to the return value of this method.
+     * Disconnects from currently connected host. The connection state should be tracked by the
+     * application by handling callback from BluetoothHidDeviceCallback#onConnectionStateChanged.
+     * The connection state is not related to the return value of this method.
      *
      * @return true if the command is successfully sent; otherwise false.
      */
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java b/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java
deleted file mode 100644
index d1efa2d..0000000
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppConfiguration.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Random;
-
-/**
- * Represents the app configuration for a Bluetooth HID Device application.
- *
- * The app needs a BluetoothHidDeviceAppConfiguration token to unregister
- * the Bluetooth HID Device service.
- *
- * {@see BluetoothHidDevice}
- *
- * {@hide}
- */
-public final class BluetoothHidDeviceAppConfiguration implements Parcelable {
-    private final long mHash;
-
-    BluetoothHidDeviceAppConfiguration() {
-        Random rnd = new Random();
-        mHash = rnd.nextLong();
-    }
-
-    BluetoothHidDeviceAppConfiguration(long hash) {
-        mHash = hash;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof BluetoothHidDeviceAppConfiguration) {
-            BluetoothHidDeviceAppConfiguration config = (BluetoothHidDeviceAppConfiguration) o;
-            return mHash == config.mHash;
-        }
-        return false;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    public static final Parcelable.Creator<BluetoothHidDeviceAppConfiguration> CREATOR =
-            new Parcelable.Creator<BluetoothHidDeviceAppConfiguration>() {
-
-                @Override
-                public BluetoothHidDeviceAppConfiguration createFromParcel(Parcel in) {
-                    long hash = in.readLong();
-                    return new BluetoothHidDeviceAppConfiguration(hash);
-                }
-
-                @Override
-                public BluetoothHidDeviceAppConfiguration[] newArray(int size) {
-                    return new BluetoothHidDeviceAppConfiguration[size];
-                }
-            };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeLong(mHash);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
index 881ae98..4609d52 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
@@ -20,15 +20,14 @@
 import android.os.Parcelable;
 
 /**
- * Represents the Quality of Service (QoS) settings for a Bluetooth HID Device
- * application.
+ * Represents the Quality of Service (QoS) settings for a Bluetooth HID Device application.
  *
- * The BluetoothHidDevice framework will update the L2CAP QoS settings for the
- * app during registration.
+ * <p>The BluetoothHidDevice framework will update the L2CAP QoS settings for the app during
+ * registration.
  *
- * {@see BluetoothHidDevice}
+ * <p>{@see BluetoothHidDevice}
  *
- * {@hide}
+ * <p>{@hide}
  */
 public final class BluetoothHidDeviceAppQosSettings implements Parcelable {
 
@@ -46,13 +45,12 @@
     public static final int MAX = (int) 0xffffffff;
 
     /**
-     * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel.
-     * The QoS Settings is optional.
-     * Recommended to use BluetoothHidDeviceAppQosSettings.Builder.
-     * {@see <a href="https://www.bluetooth.com/specifications/profiles-overview">
-     *     https://www.bluetooth.com/specifications/profiles-overview
-     *     </a>
-     *     Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D }
+     * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS
+     * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. {@see <a
+     * href="https://www.bluetooth.com/specifications/profiles-overview">
+     * https://www.bluetooth.com/specifications/profiles-overview </a> Bluetooth HID Specfication
+     * v1.1.1 Section 5.2 and Appendix D }
+     *
      * @param serviceType L2CAP service type
      * @param tokenRate L2CAP token rate
      * @param tokenBucketSize L2CAP token bucket size
@@ -123,13 +121,11 @@
     /** @return an int array representation of this instance */
     public int[] toArray() {
         return new int[] {
-                serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation
+            serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation
         };
     }
 
-    /**
-     * A helper to build the BluetoothHidDeviceAppQosSettings object.
-     */
+    /** A helper to build the BluetoothHidDeviceAppQosSettings object. */
     public static class Builder {
         // Optional parameters - initialized to default values
         private int mServiceType = SERVICE_BEST_EFFORT;
@@ -141,8 +137,9 @@
 
         /**
          * Set the service type.
+         *
          * @param val service type. Should be one of {SERVICE_NO_TRAFFIC, SERVICE_BEST_EFFORT,
-         * SERVICE_GUARANTEED}, with SERVICE_BEST_EFFORT being the default one.
+         *     SERVICE_GUARANTEED}, with SERVICE_BEST_EFFORT being the default one.
          * @return BluetoothHidDeviceAppQosSettings Builder with specified service type.
          */
         public Builder serviceType(int val) {
@@ -151,6 +148,7 @@
         }
         /**
          * Set the token rate.
+         *
          * @param val token rate
          * @return BluetoothHidDeviceAppQosSettings Builder with specified token rate.
          */
@@ -161,6 +159,7 @@
 
         /**
          * Set the bucket size.
+         *
          * @param val bucket size
          * @return BluetoothHidDeviceAppQosSettings Builder with specified bucket size.
          */
@@ -171,6 +170,7 @@
 
         /**
          * Set the peak bandwidth.
+         *
          * @param val peak bandwidth
          * @return BluetoothHidDeviceAppQosSettings Builder with specified peak bandwidth.
          */
@@ -180,6 +180,7 @@
         }
         /**
          * Set the latency.
+         *
          * @param val latency
          * @return BluetoothHidDeviceAppQosSettings Builder with specified latency.
          */
@@ -190,6 +191,7 @@
 
         /**
          * Set the delay variation.
+         *
          * @param val delay variation
          * @return BluetoothHidDeviceAppQosSettings Builder with specified delay variation.
          */
@@ -200,6 +202,7 @@
 
         /**
          * Build the BluetoothHidDeviceAppQosSettings object.
+         *
          * @return BluetoothHidDeviceAppQosSettings object with current settings.
          */
         public BluetoothHidDeviceAppQosSettings build() {
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
index 4669637..2da64e5 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
@@ -22,16 +22,14 @@
 import java.util.Arrays;
 
 /**
- * Represents the Service Discovery Protocol (SDP) settings for a Bluetooth
- * HID Device application.
+ * Represents the Service Discovery Protocol (SDP) settings for a Bluetooth HID Device application.
  *
- * The BluetoothHidDevice framework adds the SDP record during app
- * registration, so that the Android device can be discovered as a Bluetooth
- * HID Device.
+ * <p>The BluetoothHidDevice framework adds the SDP record during app registration, so that the
+ * Android device can be discovered as a Bluetooth HID Device.
  *
- * {@see BluetoothHidDevice}
+ * <p>{@see BluetoothHidDevice}
  *
- * {@hide}
+ * <p>{@hide}
  */
 public final class BluetoothHidDeviceAppSdpSettings implements Parcelable {
 
@@ -43,18 +41,19 @@
 
     /**
      * Create a BluetoothHidDeviceAppSdpSettings object for the Bluetooth SDP record.
+     *
      * @param name Name of this Bluetooth HID device. Maximum length is 50 bytes.
      * @param description Description for this Bluetooth HID device. Maximum length is 50 bytes.
      * @param provider Provider of this Bluetooth HID device. Maximum length is 50 bytes.
-     * @param subclass Subclass of this Bluetooth HID device.
-     * See <a href="www.usb.org/developers/hidpage/HID1_11.pdf">
+     * @param subclass Subclass of this Bluetooth HID device. See <a
+     *     href="www.usb.org/developers/hidpage/HID1_11.pdf">
      *     www.usb.org/developers/hidpage/HID1_11.pdf Section 4.2</a>
-     * @param descriptors Descriptors of this Bluetooth HID device.
-     * See <a href="www.usb.org/developers/hidpage/HID1_11.pdf">
+     * @param descriptors Descriptors of this Bluetooth HID device. See <a
+     *     href="www.usb.org/developers/hidpage/HID1_11.pdf">
      *     www.usb.org/developers/hidpage/HID1_11.pdf Chapter 6</a> Maximum length is 2048 bytes.
      */
-    public BluetoothHidDeviceAppSdpSettings(String name, String description, String provider,
-            byte subclass, byte[] descriptors) {
+    public BluetoothHidDeviceAppSdpSettings(
+            String name, String description, String provider, byte subclass, byte[] descriptors) {
         this.name = name;
         this.description = description;
         this.provider = provider;
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
index 5ccda0d..6ed1965 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
@@ -19,50 +19,43 @@
 import android.util.Log;
 
 /**
- * The template class that applications use to call callback functions on
- * events from the HID host. Callback functions are wrapped in this class and
- * registered to the Android system during app registration.
+ * The template class that applications use to call callback functions on events from the HID host.
+ * Callback functions are wrapped in this class and registered to the Android system during app
+ * registration.
  *
- * {@see BluetoothHidDevice}
+ * <p>{@see BluetoothHidDevice}
  *
- * {@hide}
+ * <p>{@hide}
  */
 public abstract class BluetoothHidDeviceCallback {
 
     private static final String TAG = "BluetoothHidDevCallback";
 
     /**
-     * Callback called when application registration state changes. Usually it's
-     * called due to either
-     * {@link BluetoothHidDevice#registerApp
-     * (String, String, String, byte, byte[], BluetoothHidDeviceCallback)}
-     * or
-     * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)}
-     * , but can be also unsolicited in case e.g. Bluetooth was turned off in
-     * which case application is unregistered automatically.
+     * Callback called when application registration state changes. Usually it's called due to
+     * either {@link BluetoothHidDevice#registerApp (String, String, String, byte, byte[],
+     * BluetoothHidDeviceCallback)} or {@link BluetoothHidDevice#unregisterApp()} , but can be also
+     * unsolicited in case e.g. Bluetooth was turned off in which case application is unregistered
+     * automatically.
      *
      * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently has
-     * Virtual Cable established with device. Only valid when application is registered, can be
-     * <code>null</code>.
-     * @param config {@link BluetoothHidDeviceAppConfiguration} object which represents token
-     * required to unregister application using
-     * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)}.
+     *     Virtual Cable established with device. Only valid when application is registered, can be
+     *     <code>null</code>.
      * @param registered <code>true</code> if application is registered, <code>false</code>
-     * otherwise.
+     *     otherwise.
      */
-    public void onAppStatusChanged(BluetoothDevice pluggedDevice,
-            BluetoothHidDeviceAppConfiguration config, boolean registered) {
-        Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered="
-                + registered);
+    public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
+        Log.d(TAG,
+                "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered=" + registered);
     }
 
     /**
-     * Callback called when connection state with remote host was changed.
-     * Application can assume than Virtual Cable is established when called with
-     * {@link BluetoothProfile#STATE_CONNECTED} <code>state</code>.
+     * Callback called when connection state with remote host was changed. Application can assume
+     * than Virtual Cable is established when called with {@link BluetoothProfile#STATE_CONNECTED}
+     * <code>state</code>.
      *
      * @param device {@link BluetoothDevice} object representing host device which connection state
-     * was changed.
+     *     was changed.
      * @param state Connection state as defined in {@link BluetoothProfile}.
      */
     public void onConnectionStateChanged(BluetoothDevice device, int state) {
@@ -70,14 +63,14 @@
     }
 
     /**
-     * Callback called when GET_REPORT is received from remote host. Should be
-     * replied by application using
-     * {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte, byte[])}.
+     * Callback called when GET_REPORT is received from remote host. Should be replied by
+     * application using {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte,
+     * byte[])}.
      *
      * @param type Requested Report Type.
      * @param id Requested Report Id, can be 0 if no Report Id are defined in descriptor.
      * @param bufferSize Requested buffer size, application shall respond with at least given number
-     * of bytes.
+     *     of bytes.
      */
     public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
         Log.d(TAG, "onGetReport: device=" + device + " type=" + type + " id=" + id + " bufferSize="
@@ -85,9 +78,9 @@
     }
 
     /**
-     * Callback called when SET_REPORT is received from remote host. In case
-     * received data are invalid, application shall respond with
-     * {@link BluetoothHidDevice#reportError(BluetoothDevice, byte)}.
+     * Callback called when SET_REPORT is received from remote host. In case received data are
+     * invalid, application shall respond with {@link
+     * BluetoothHidDevice#reportError(BluetoothDevice, byte)}.
      *
      * @param type Report Type.
      * @param id Report Id.
@@ -98,10 +91,9 @@
     }
 
     /**
-     * Callback called when SET_PROTOCOL is received from remote host.
-     * Application shall use this information to send only reports valid for
-     * given protocol mode. By default,
-     * {@link BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed.
+     * Callback called when SET_PROTOCOL is received from remote host. Application shall use this
+     * information to send only reports valid for given protocol mode. By default, {@link
+     * BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed.
      *
      * @param protocol Protocol Mode.
      */
@@ -110,9 +102,8 @@
     }
 
     /**
-     * Callback called when report data is received over interrupt channel.
-     * Report Type is assumed to be
-     * {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}.
+     * Callback called when report data is received over interrupt channel. Report Type is assumed
+     * to be {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}.
      *
      * @param reportId Report Id.
      * @param data Report data.
@@ -122,10 +113,8 @@
     }
 
     /**
-     * Callback called when Virtual Cable is removed. This can be either due to
-     * {@link BluetoothHidDevice#unplug(BluetoothDevice)} or request from remote
-     * side. After this callback is received connection will be disconnected
-     * automatically.
+     * Callback called when Virtual Cable is removed. After this callback is
+     * received connection will be disconnected automatically.
      */
     public void onVirtualCableUnplug(BluetoothDevice device) {
         Log.d(TAG, "onVirtualCableUnplug: device=" + device);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 71998ee..9800ab0 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2801,10 +2801,17 @@
      * example, if this Context is an Activity that is stopped, the service will
      * not be required to continue running until the Activity is resumed.
      *
-     * <p>This function will throw {@link SecurityException} if you do not
+     * <p>If the service does not support binding, it may return {@code null} from
+     * its {@link android.app.Service#onBind(Intent) onBind()} method.  If it does, then
+     * the ServiceConnection's
+     * {@link ServiceConnection#onNullBinding(ComponentName) onNullBinding()} method
+     * will be invoked instead of
+     * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder) onServiceConnected()}.
+     *
+     * <p>This method will throw {@link SecurityException} if the calling app does not
      * have permission to bind to the given service.
      *
-     * <p class="note">Note: this method <em>can not be called from a
+     * <p class="note">Note: this method <em>cannot be called from a
      * {@link BroadcastReceiver} component</em>.  A pattern you can use to
      * communicate from a BroadcastReceiver to a Service is to call
      * {@link #startService} with the arguments containing the command to be
@@ -2827,8 +2834,8 @@
      *          {@link #BIND_WAIVE_PRIORITY}.
      * @return If you have successfully bound to the service, {@code true} is returned;
      *         {@code false} is returned if the connection is not made so you will not
-     *         receive the service object. However, you should still call
-     *         {@link #unbindService} to release the connection.
+     *         receive the service object. You should still call {@link #unbindService}
+     *         to release the connection even if this method returned {@code false}.
      *
      * @throws SecurityException If the caller does not have permission to access the service
      * or the service can not be found.
@@ -3406,6 +3413,14 @@
     public static final String NETWORKMANAGEMENT_SERVICE = "network_management";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link com.android.server.slice.SliceManagerService} for managing slices.
+     * @hide
+     * @see #getSystemService
+     */
+    public static final String SLICE_SERVICE = "slice";
+
+    /**
      * Use with {@link #getSystemService} to retrieve a {@link
      * android.app.usage.NetworkStatsManager} for querying network usage stats.
      *
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d1e12aa..6fb1afd 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4461,10 +4461,28 @@
     public static final String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION";
 
     /**
+     * A {@link Bundle} of metadata that describes the instanta application that needs to be
+     * installed. This data is populated from the response to
+     * {@link android.content.pm.InstantAppResolveInfo#getExtras()} as provided by the registered
+     * instant application resolver.
+     * @hide
+     */
+    public static final String EXTRA_INSTANT_APP_EXTRAS =
+            "android.intent.extra.INSTANT_APP_EXTRAS";
+
+    /**
+     * The version code of the app to install components from.
+     * @deprecated Use {@link #EXTRA_LONG_VERSION_CODE).
+     * @hide
+     */
+    @Deprecated
+    public static final String EXTRA_VERSION_CODE = "android.intent.extra.VERSION_CODE";
+
+    /**
      * The version code of the app to install components from.
      * @hide
      */
-    public static final String EXTRA_VERSION_CODE = "android.intent.extra.VERSION_CODE";
+    public static final String EXTRA_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
 
     /**
      * The app that triggered the ephemeral installation.
@@ -4897,8 +4915,9 @@
      * <li>Enumeration of features here is not meant to restrict capabilities of the quick viewer.
      * Quick viewer can implement features not listed below.
      * <li>Features included at this time are: {@link QuickViewConstants#FEATURE_VIEW},
-     * {@link QuickViewConstants#FEATURE_EDIT}, {@link QuickViewConstants#FEATURE_DOWNLOAD},
-     * {@link QuickViewConstants#FEATURE_SEND}, {@link QuickViewConstants#FEATURE_PRINT}.
+     * {@link QuickViewConstants#FEATURE_EDIT}, {@link QuickViewConstants#FEATURE_DELETE},
+     * {@link QuickViewConstants#FEATURE_DOWNLOAD}, {@link QuickViewConstants#FEATURE_SEND},
+     * {@link QuickViewConstants#FEATURE_PRINT}.
      * <p>
      * Requirements:
      * <li>Quick viewer shouldn't show a feature if the feature is absent in
diff --git a/core/java/android/content/QuickViewConstants.java b/core/java/android/content/QuickViewConstants.java
index 7455d0c..a25513d 100644
--- a/core/java/android/content/QuickViewConstants.java
+++ b/core/java/android/content/QuickViewConstants.java
@@ -33,7 +33,7 @@
     public static final String FEATURE_VIEW = "android:view";
 
     /**
-     * Feature to view a document using system standard editing mechanism, like
+     * Feature to edit a document using system standard editing mechanism, like
      * {@link Intent#ACTION_EDIT}.
      *
      * @see Intent#EXTRA_QUICK_VIEW_FEATURES
@@ -42,6 +42,15 @@
     public static final String FEATURE_EDIT = "android:edit";
 
     /**
+     * Feature to delete an individual document. Quick viewer implementations must use
+     * Storage Access Framework to both verify delete permission and to delete content.
+     *
+     * @see DocumentsContract#Document#FLAG_SUPPORTS_DELETE
+     * @see DocumentsContract#deleteDocument(ContentResolver resolver, Uri documentUri)
+     */
+    public static final String FEATURE_DELETE = "android:delete";
+
+    /**
      * Feature to view a document using system standard sending mechanism, like
      * {@link Intent#ACTION_SEND}.
      *
diff --git a/core/java/android/content/ServiceConnection.java b/core/java/android/content/ServiceConnection.java
index 6ff4900..c16dbbe 100644
--- a/core/java/android/content/ServiceConnection.java
+++ b/core/java/android/content/ServiceConnection.java
@@ -63,4 +63,21 @@
      */
     default void onBindingDied(ComponentName name) {
     }
+
+    /**
+     * Called when the service being bound has returned {@code null} from its
+     * {@link android.app.Service#onBind(Intent) onBind()} method.  This indicates
+     * that the attempting service binding represented by this ServiceConnection
+     * will never become usable.
+     *
+     * <p class="note">The app which requested the binding must still call
+     * {@link Context#unbindService(ServiceConnection)} to release the tracking
+     * resources associated with this ServiceConnection even if this callback was
+     * invoked following {@link Context#bindService Context.bindService() bindService()}.
+     *
+     * @param name The concrete component name of the service whose binding
+     *     has been rejected by the Service implementation.
+     */
+    default void onNullBinding(ComponentName name) {
+    }
 }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 5298f57..84b1ff3 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -896,7 +896,7 @@
      * The app's declared version code.
      * @hide
      */
-    public int versionCode;
+    public long versionCode;
 
     /**
      * The user-visible SDK version (ex. 26) of the framework against which the application claims
@@ -1323,7 +1323,7 @@
         dest.writeInt(uid);
         dest.writeInt(minSdkVersion);
         dest.writeInt(targetSdkVersion);
-        dest.writeInt(versionCode);
+        dest.writeLong(versionCode);
         dest.writeInt(enabled ? 1 : 0);
         dest.writeInt(enabledSetting);
         dest.writeInt(installLocation);
@@ -1392,7 +1392,7 @@
         uid = source.readInt();
         minSdkVersion = source.readInt();
         targetSdkVersion = source.readInt();
-        versionCode = source.readInt();
+        versionCode = source.readLong();
         enabled = source.readInt() != 0;
         enabledSetting = source.readInt();
         installLocation = source.readInt();
diff --git a/core/java/android/content/pm/AuxiliaryResolveInfo.java b/core/java/android/content/pm/AuxiliaryResolveInfo.java
index 067363d..6bdcefb 100644
--- a/core/java/android/content/pm/AuxiliaryResolveInfo.java
+++ b/core/java/android/content/pm/AuxiliaryResolveInfo.java
@@ -45,7 +45,7 @@
     /** Opaque token to track the instant application resolution */
     public final String token;
     /** The version code of the package */
-    public final int versionCode;
+    public final long versionCode;
     /** An intent to start upon failure to install */
     public final Intent failureIntent;
 
@@ -71,7 +71,7 @@
     public AuxiliaryResolveInfo(@NonNull String packageName,
             @Nullable String splitName,
             @Nullable ComponentName failureActivity,
-            int versionCode,
+            long versionCode,
             @Nullable Intent failureIntent) {
         super();
         this.packageName = packageName;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 64d33d5..56a0def 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -48,6 +48,7 @@
 import android.content.pm.UserInfo;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VersionedPackage;
+import android.content.pm.dex.IArtManager;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.os.Bundle;
@@ -657,4 +658,6 @@
     ComponentName getInstantAppInstallerComponent();
 
     String getInstantAppAndroidId(String packageName, int userId);
+
+    IArtManager getArtManager();
 }
diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java
index 22e994f..19cb932 100644
--- a/core/java/android/content/pm/InstantAppResolveInfo.java
+++ b/core/java/android/content/pm/InstantAppResolveInfo.java
@@ -19,8 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.content.IntentFilter;
-import android.net.Uri;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -44,10 +43,18 @@
     /** The filters used to match domain */
     private final List<InstantAppIntentFilter> mFilters;
     /** The version code of the app that this class resolves to */
-    private final int mVersionCode;
+    private final long mVersionCode;
+    /** Data about the app that should be passed along to the Instant App installer on resolve */
+    private final Bundle mExtras;
 
     public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
             @Nullable List<InstantAppIntentFilter> filters, int versionCode) {
+        this(digest, packageName, filters, (long) versionCode, null /* extras */);
+    }
+
+    public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
+            @Nullable List<InstantAppIntentFilter> filters, long versionCode,
+            @Nullable Bundle extras) {
         // validate arguments
         if ((packageName == null && (filters != null && filters.size() != 0))
                 || (packageName != null && (filters == null || filters.size() == 0))) {
@@ -62,11 +69,13 @@
         }
         mPackageName = packageName;
         mVersionCode = versionCode;
+        mExtras = extras;
     }
 
     public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName,
             @Nullable List<InstantAppIntentFilter> filters) {
-        this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/);
+        this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/,
+                null /* extras */);
     }
 
     InstantAppResolveInfo(Parcel in) {
@@ -74,7 +83,8 @@
         mPackageName = in.readString();
         mFilters = new ArrayList<InstantAppIntentFilter>();
         in.readList(mFilters, null /*loader*/);
-        mVersionCode = in.readInt();
+        mVersionCode = in.readLong();
+        mExtras = in.readBundle();
     }
 
     public byte[] getDigestBytes() {
@@ -93,10 +103,23 @@
         return mFilters;
     }
 
+    /**
+     * @deprecated Use {@link #getLongVersionCode} instead.
+     */
+    @Deprecated
     public int getVersionCode() {
+        return (int) (mVersionCode & 0xffffffff);
+    }
+
+    public long getLongVersionCode() {
         return mVersionCode;
     }
 
+    @Nullable
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -107,7 +130,8 @@
         out.writeParcelable(mDigest, flags);
         out.writeString(mPackageName);
         out.writeList(mFilters);
-        out.writeInt(mVersionCode);
+        out.writeLong(mVersionCode);
+        out.writeBundle(mExtras);
     }
 
     public static final Parcelable.Creator<InstantAppResolveInfo> CREATOR
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 9e54e23..d09ba0b 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -265,6 +265,14 @@
 
         /**
          * Include pinned shortcuts in the result.
+         *
+         * <p>If you are the selected assistant app, and wishes to fetch all shortcuts that the
+         * user owns on the launcher (or by other launchers, in case the user has multiple), use
+         * {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} instead.
+         *
+         * <p>If you're a regular launcher app, there's no way to get shortcuts pinned by other
+         * launchers, and {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} will be ignored. So use this
+         * flag to get own pinned shortcuts.
          */
         public static final int FLAG_MATCH_PINNED = 1 << 1;
 
@@ -285,8 +293,15 @@
          * Include all pinned shortcuts by any launchers, not just by the caller,
          * in the result.
          *
-         * The caller must be the selected assistant app to use this flag, or have the system
+         * <p>The caller must be the selected assistant app to use this flag, or have the system
          * {@code ACCESS_SHORTCUTS} permission.
+         *
+         * <p>If you are the selected assistant app, and wishes to fetch all shortcuts that the
+         * user owns on the launcher (or by other launchers, in case the user has multiple), use
+         * {@link #FLAG_MATCH_PINNED_BY_ANY_LAUNCHER} instead.
+         *
+         * <p>If you're a regular launcher app (or any app that's not the selected assistant app)
+         * then this flag will be ignored.
          */
         public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1 << 10;
 
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index f8889b6..0c893b0 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -37,13 +37,56 @@
     public String[] splitNames;
 
     /**
+     * @deprecated Use {@link #getLongVersionCode()} instead, which includes both
+     * this and the additional
+     * {@link android.R.styleable#AndroidManifest_versionCodeMajor versionCodeMajor} attribute.
      * The version number of this package, as specified by the &lt;manifest&gt;
      * tag's {@link android.R.styleable#AndroidManifest_versionCode versionCode}
      * attribute.
+     * @see #getLongVersionCode()
      */
+    @Deprecated
     public int versionCode;
 
     /**
+     * @hide
+     * The major version number of this package, as specified by the &lt;manifest&gt;
+     * tag's {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor}
+     * attribute.
+     * @see #getLongVersionCode()
+     */
+    public int versionCodeMajor;
+
+    /**
+     * Return {@link android.R.styleable#AndroidManifest_versionCode versionCode} and
+     * {@link android.R.styleable#AndroidManifest_versionCodeMajor versionCodeMajor} combined
+     * together as a single long value.  The
+     * {@link android.R.styleable#AndroidManifest_versionCodeMajor versionCodeMajor} is placed in
+     * the upper 32 bits.
+     */
+    public long getLongVersionCode() {
+        return composeLongVersionCode(versionCodeMajor, versionCode);
+    }
+
+    /**
+     * Set the full version code in this PackageInfo, updating {@link #versionCode}
+     * with the lower bits.
+     * @see #getLongVersionCode()
+     */
+    public void setLongVersionCode(long longVersionCode) {
+        versionCodeMajor = (int) (longVersionCode>>32);
+        versionCode = (int) longVersionCode;
+    }
+
+    /**
+     * @hide Internal implementation for composing a minor and major version code in to
+     * a single long version code.
+     */
+    public static long composeLongVersionCode(int major, int minor) {
+        return (((long) major) << 32) | (((long) minor) & 0xffffffffL);
+    }
+
+    /**
      * The version name of this package, as specified by the &lt;manifest&gt;
      * tag's {@link android.R.styleable#AndroidManifest_versionName versionName}
      * attribute.
@@ -333,6 +376,7 @@
         dest.writeString(packageName);
         dest.writeStringArray(splitNames);
         dest.writeInt(versionCode);
+        dest.writeInt(versionCodeMajor);
         dest.writeString(versionName);
         dest.writeInt(baseRevisionCode);
         dest.writeIntArray(splitRevisionCodes);
@@ -389,6 +433,7 @@
         packageName = source.readString();
         splitNames = source.createStringArray();
         versionCode = source.readInt();
+        versionCodeMajor = source.readInt();
         versionName = source.readString();
         baseRevisionCode = source.readInt();
         splitRevisionCodes = source.createIntArray();
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
index 1efe082..bbf020d 100644
--- a/core/java/android/content/pm/PackageInfoLite.java
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -38,9 +38,27 @@
 
     /**
      * The android:versionCode of the package.
+     * @deprecated Use {@link #getLongVersionCode()} instead, which includes both
+     * this and the additional
+     * {@link android.R.styleable#AndroidManifest_versionCode versionCodeMajor} attribute.
      */
+    @Deprecated
     public int versionCode;
 
+    /**
+     * @hide
+     * The android:versionCodeMajor of the package.
+     */
+    public int versionCodeMajor;
+
+    /**
+     * Return {@link #versionCode} and {@link #versionCodeMajor} combined together as a
+     * single long value.  The {@link #versionCodeMajor} is placed in the upper 32 bits.
+     */
+    public long getLongVersionCode() {
+        return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
+    }
+
     /** Revision code of base APK */
     public int baseRevisionCode;
     /** Revision codes of any split APKs, ordered by parsed splitName */
@@ -55,10 +73,10 @@
 
     /**
      * Specifies the recommended install location. Can be one of
-     * {@link #PackageHelper.RECOMMEND_INSTALL_INTERNAL} to install on internal storage
-     * {@link #PackageHelper.RECOMMEND_INSTALL_EXTERNAL} to install on external media
-     * {@link PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors
-     * {@link PackageHelper.RECOMMEND_FAILED_INVALID_APK} for parse errors.
+     * {@link PackageHelper#RECOMMEND_INSTALL_INTERNAL} to install on internal storage,
+     * {@link PackageHelper#RECOMMEND_INSTALL_EXTERNAL} to install on external media,
+     * {@link PackageHelper#RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors,
+     * or {@link PackageHelper#RECOMMEND_FAILED_INVALID_APK} for parse errors.
      */
     public int recommendedInstallLocation;
     public int installLocation;
@@ -82,6 +100,7 @@
         dest.writeString(packageName);
         dest.writeStringArray(splitNames);
         dest.writeInt(versionCode);
+        dest.writeInt(versionCodeMajor);
         dest.writeInt(baseRevisionCode);
         dest.writeIntArray(splitRevisionCodes);
         dest.writeInt(recommendedInstallLocation);
@@ -111,6 +130,7 @@
         packageName = source.readString();
         splitNames = source.createStringArray();
         versionCode = source.readInt();
+        versionCodeMajor = source.readInt();
         baseRevisionCode = source.readInt();
         splitRevisionCodes = source.createIntArray();
         recommendedInstallLocation = source.readInt();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f796aab..ff02c40 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -42,6 +42,7 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.dex.ArtManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Rect;
@@ -2634,11 +2635,20 @@
 
     /**
      * Extra field name for the version code of a package pending verification.
+     * @deprecated Use {@link #EXTRA_VERIFICATION_LONG_VERSION_CODE} instead.
+     * @hide
+     */
+    @Deprecated
+    public static final String EXTRA_VERIFICATION_VERSION_CODE
+            = "android.content.pm.extra.VERIFICATION_VERSION_CODE";
+
+    /**
+     * Extra field name for the long version code of a package pending verification.
      *
      * @hide
      */
-    public static final String EXTRA_VERIFICATION_VERSION_CODE
-            = "android.content.pm.extra.VERIFICATION_VERSION_CODE";
+    public static final String EXTRA_VERIFICATION_LONG_VERSION_CODE =
+            "android.content.pm.extra.VERIFICATION_LONG_VERSION_CODE";
 
     /**
      * Extra field name for the ID of a intent filter pending verification.
@@ -5842,4 +5852,14 @@
     @SystemApi
     public abstract void registerDexModule(String dexModulePath,
             @Nullable DexModuleRegisterCallback callback);
+
+    /**
+     * Returns the {@link ArtManager} associated with this package manager.
+     *
+     * @hide
+     */
+    @SystemApi
+    public @NonNull ArtManager getArtManager() {
+        throw new UnsupportedOperationException("getArtManager not implemented in subclass");
+    }
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 98c824d..153c944 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -394,6 +394,7 @@
     public static class PackageLite {
         public final String packageName;
         public final int versionCode;
+        public final int versionCodeMajor;
         public final int installLocation;
         public final VerifierInfo[] verifiers;
 
@@ -436,6 +437,7 @@
                 String[] splitCodePaths, int[] splitRevisionCodes) {
             this.packageName = baseApk.packageName;
             this.versionCode = baseApk.versionCode;
+            this.versionCodeMajor = baseApk.versionCodeMajor;
             this.installLocation = baseApk.installLocation;
             this.verifiers = baseApk.verifiers;
             this.splitNames = splitNames;
@@ -476,6 +478,7 @@
         public final String configForSplit;
         public final String usesSplitName;
         public final int versionCode;
+        public final int versionCodeMajor;
         public final int revisionCode;
         public final int installLocation;
         public final VerifierInfo[] verifiers;
@@ -489,11 +492,11 @@
         public final boolean isolatedSplits;
 
         public ApkLite(String codePath, String packageName, String splitName, boolean isFeatureSplit,
-                String configForSplit, String usesSplitName, int versionCode, int revisionCode,
-                int installLocation, List<VerifierInfo> verifiers, Signature[] signatures,
-                Certificate[][] certificates, boolean coreApp, boolean debuggable,
-                boolean multiArch, boolean use32bitAbi, boolean extractNativeLibs,
-                boolean isolatedSplits) {
+                String configForSplit, String usesSplitName, int versionCode, int versionCodeMajor,
+                int revisionCode, int installLocation, List<VerifierInfo> verifiers,
+                Signature[] signatures, Certificate[][] certificates, boolean coreApp,
+                boolean debuggable, boolean multiArch, boolean use32bitAbi,
+                boolean extractNativeLibs, boolean isolatedSplits) {
             this.codePath = codePath;
             this.packageName = packageName;
             this.splitName = splitName;
@@ -501,6 +504,7 @@
             this.configForSplit = configForSplit;
             this.usesSplitName = usesSplitName;
             this.versionCode = versionCode;
+            this.versionCodeMajor = versionCodeMajor;
             this.revisionCode = revisionCode;
             this.installLocation = installLocation;
             this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
@@ -513,6 +517,10 @@
             this.extractNativeLibs = extractNativeLibs;
             this.isolatedSplits = isolatedSplits;
         }
+
+        public long getLongVersionCode() {
+            return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
+        }
     }
 
     /**
@@ -663,6 +671,7 @@
         pi.packageName = p.packageName;
         pi.splitNames = p.splitNames;
         pi.versionCode = p.mVersionCode;
+        pi.versionCodeMajor = p.mVersionCodeMajor;
         pi.baseRevisionCode = p.baseRevisionCode;
         pi.splitRevisionCodes = p.splitRevisionCodes;
         pi.versionName = p.mVersionName;
@@ -1880,6 +1889,7 @@
 
         int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
         int versionCode = 0;
+        int versionCodeMajor = 0;
         int revisionCode = 0;
         boolean coreApp = false;
         boolean debuggable = false;
@@ -1898,6 +1908,8 @@
                         PARSE_DEFAULT_INSTALL_LOCATION);
             } else if (attr.equals("versionCode")) {
                 versionCode = attrs.getAttributeIntValue(i, 0);
+            } else if (attr.equals("versionCodeMajor")) {
+                versionCodeMajor = attrs.getAttributeIntValue(i, 0);
             } else if (attr.equals("revisionCode")) {
                 revisionCode = attrs.getAttributeIntValue(i, 0);
             } else if (attr.equals("coreApp")) {
@@ -1963,9 +1975,9 @@
         }
 
         return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
-                configForSplit, usesSplitName, versionCode, revisionCode, installLocation,
-                verifiers, signatures, certificates, coreApp, debuggable, multiArch, use32bitAbi,
-                extractNativeLibs, isolatedSplits);
+                configForSplit, usesSplitName, versionCode, versionCodeMajor, revisionCode,
+                installLocation, verifiers, signatures, certificates, coreApp, debuggable,
+                multiArch, use32bitAbi, extractNativeLibs, isolatedSplits);
     }
 
     /**
@@ -2086,8 +2098,11 @@
         TypedArray sa = res.obtainAttributes(parser,
                 com.android.internal.R.styleable.AndroidManifest);
 
-        pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
+        pkg.mVersionCode = sa.getInteger(
                 com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
+        pkg.mVersionCodeMajor = sa.getInteger(
+                com.android.internal.R.styleable.AndroidManifest_versionCodeMajor, 0);
+        pkg.applicationInfo.versionCode = pkg.getLongVersionCode();
         pkg.baseRevisionCode = sa.getInteger(
                 com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
         pkg.mVersionName = sa.getNonConfigurationString(
@@ -2912,7 +2927,7 @@
                 1, additionalCertSha256Digests.length);
 
         pkg.usesStaticLibraries = ArrayUtils.add(pkg.usesStaticLibraries, lname);
-        pkg.usesStaticLibrariesVersions = ArrayUtils.appendInt(
+        pkg.usesStaticLibrariesVersions = ArrayUtils.appendLong(
                 pkg.usesStaticLibrariesVersions, version, true);
         pkg.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
                 pkg.usesStaticLibrariesCertDigests, certSha256Digests, true);
@@ -3867,6 +3882,9 @@
                         com.android.internal.R.styleable.AndroidManifestStaticLibrary_name);
                 final int version = sa.getInt(
                         com.android.internal.R.styleable.AndroidManifestStaticLibrary_version, -1);
+                final int versionMajor = sa.getInt(
+                        com.android.internal.R.styleable.AndroidManifestStaticLibrary_versionMajor,
+                        0);
 
                 sa.recycle();
 
@@ -3894,7 +3912,12 @@
                 }
 
                 owner.staticSharedLibName = lname.intern();
-                owner.staticSharedLibVersion = version;
+                if (version >= 0) {
+                    owner.staticSharedLibVersion =
+                            PackageInfo.composeLongVersionCode(versionMajor, version);
+                } else {
+                    owner.staticSharedLibVersion = version;
+                }
                 ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY;
 
                 XmlUtils.skipCurrentTag(parser);
@@ -5895,11 +5918,11 @@
         public ArrayList<Package> childPackages;
 
         public String staticSharedLibName = null;
-        public int staticSharedLibVersion = 0;
+        public long staticSharedLibVersion = 0;
         public ArrayList<String> libraryNames = null;
         public ArrayList<String> usesLibraries = null;
         public ArrayList<String> usesStaticLibraries = null;
-        public int[] usesStaticLibrariesVersions = null;
+        public long[] usesStaticLibrariesVersions = null;
         public String[][] usesStaticLibrariesCertDigests = null;
         public ArrayList<String> usesOptionalLibraries = null;
         public String[] usesLibraryFiles = null;
@@ -5916,6 +5939,14 @@
         // The version code declared for this package.
         public int mVersionCode;
 
+        // The major version code declared for this package.
+        public int mVersionCodeMajor;
+
+        // Return long containing mVersionCode and mVersionCodeMajor.
+        public long getLongVersionCode() {
+            return PackageInfo.composeLongVersionCode(mVersionCodeMajor, mVersionCode);
+        }
+
         // The version name declared for this package.
         public String mVersionName;
 
@@ -6390,7 +6421,7 @@
             if (staticSharedLibName != null) {
                 staticSharedLibName = staticSharedLibName.intern();
             }
-            staticSharedLibVersion = dest.readInt();
+            staticSharedLibVersion = dest.readLong();
             libraryNames = dest.createStringArrayList();
             internStringArrayList(libraryNames);
             usesLibraries = dest.createStringArrayList();
@@ -6404,8 +6435,8 @@
                 usesStaticLibraries = new ArrayList<>(libCount);
                 dest.readStringList(usesStaticLibraries);
                 internStringArrayList(usesStaticLibraries);
-                usesStaticLibrariesVersions = new int[libCount];
-                dest.readIntArray(usesStaticLibrariesVersions);
+                usesStaticLibrariesVersions = new long[libCount];
+                dest.readLongArray(usesStaticLibrariesVersions);
                 usesStaticLibrariesCertDigests = new String[libCount][];
                 for (int i = 0; i < libCount; i++) {
                     usesStaticLibrariesCertDigests[i] = dest.createStringArray();
@@ -6545,7 +6576,7 @@
             dest.writeParcelableList(childPackages, flags);
 
             dest.writeString(staticSharedLibName);
-            dest.writeInt(staticSharedLibVersion);
+            dest.writeLong(staticSharedLibVersion);
             dest.writeStringList(libraryNames);
             dest.writeStringList(usesLibraries);
             dest.writeStringList(usesOptionalLibraries);
@@ -6556,7 +6587,7 @@
             } else {
                 dest.writeInt(usesStaticLibraries.size());
                 dest.writeStringList(usesStaticLibraries);
-                dest.writeIntArray(usesStaticLibrariesVersions);
+                dest.writeLongArray(usesStaticLibrariesVersions);
                 for (String[] usesStaticLibrariesCertDigest : usesStaticLibrariesCertDigests) {
                     dest.writeStringArray(usesStaticLibrariesCertDigest);
                 }
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index aea843a..56d61ef 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -361,7 +361,7 @@
         }
         IntArray updatedUids = null;
         for (ServiceInfo<V> service : allServices) {
-            int versionCode = service.componentInfo.applicationInfo.versionCode;
+            long versionCode = service.componentInfo.applicationInfo.versionCode;
             String pkg = service.componentInfo.packageName;
             ApplicationInfo newAppInfo = null;
             try {
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 7d301a3..2f1b256 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -73,8 +73,7 @@
 
     private final String mName;
 
-    // TODO: Make long when we change the paltform to use longs
-    private final int mVersion;
+    private final long mVersion;
     private final @Type int mType;
     private final VersionedPackage mDeclaringPackage;
     private final List<VersionedPackage> mDependentPackages;
@@ -90,7 +89,7 @@
      *
      * @hide
      */
-    public SharedLibraryInfo(String name, int version, int type,
+    public SharedLibraryInfo(String name, long version, int type,
             VersionedPackage declaringPackage, List<VersionedPackage> dependentPackages) {
         mName = name;
         mVersion = version;
@@ -100,7 +99,7 @@
     }
 
     private SharedLibraryInfo(Parcel parcel) {
-        this(parcel.readString(), parcel.readInt(), parcel.readInt(),
+        this(parcel.readString(), parcel.readLong(), parcel.readInt(),
                 parcel.readParcelable(null), parcel.readArrayList(null));
     }
 
@@ -124,6 +123,14 @@
     }
 
     /**
+     * @deprecated Use {@link #getLongVersion()} instead.
+     */
+    @Deprecated
+    public @IntRange(from = -1) int getVersion() {
+        return mVersion < 0 ? (int) mVersion : (int) (mVersion & 0x7fffffff);
+    }
+
+    /**
      * Gets the version of the library. For {@link #TYPE_STATIC static} libraries
      * this is the declared version and for {@link #TYPE_DYNAMIC dynamic} and
      * {@link #TYPE_BUILTIN builtin} it is {@link #VERSION_UNDEFINED} as these
@@ -131,7 +138,7 @@
      *
      * @return The version.
      */
-    public @IntRange(from = -1) int getVersion() {
+    public @IntRange(from = -1) long getLongVersion() {
         return mVersion;
     }
 
@@ -192,7 +199,7 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeString(mName);
-        parcel.writeInt(mVersion);
+        parcel.writeLong(mVersion);
         parcel.writeInt(mType);
         parcel.writeParcelable(mDeclaringPackage, flags);
         parcel.writeList(mDependentPackages);
diff --git a/core/java/android/content/pm/VersionedPackage.java b/core/java/android/content/pm/VersionedPackage.java
index 29c5efe..3953466 100644
--- a/core/java/android/content/pm/VersionedPackage.java
+++ b/core/java/android/content/pm/VersionedPackage.java
@@ -28,7 +28,7 @@
  */
 public final class VersionedPackage implements Parcelable {
     private final String mPackageName;
-    private final int mVersionCode;
+    private final long mVersionCode;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -47,9 +47,21 @@
         mVersionCode = versionCode;
     }
 
+    /**
+     * Creates a new instance. Use {@link PackageManager#VERSION_CODE_HIGHEST}
+     * to refer to the highest version code of this package.
+     * @param packageName The package name.
+     * @param versionCode The version code.
+     */
+    public VersionedPackage(@NonNull String packageName,
+            @VersionCode long versionCode) {
+        mPackageName = packageName;
+        mVersionCode = versionCode;
+    }
+
     private VersionedPackage(Parcel parcel) {
         mPackageName = parcel.readString();
-        mVersionCode = parcel.readInt();
+        mVersionCode = parcel.readLong();
     }
 
     /**
@@ -62,11 +74,19 @@
     }
 
     /**
+     * @deprecated use {@link #getLongVersionCode()} instead.
+     */
+    @Deprecated
+    public @VersionCode int getVersionCode() {
+        return (int) (mVersionCode & 0x7fffffff);
+    }
+
+    /**
      * Gets the version code.
      *
      * @return The version code.
      */
-    public @VersionCode int getVersionCode() {
+    public @VersionCode long getLongVersionCode() {
         return mVersionCode;
     }
 
@@ -83,7 +103,7 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeString(mPackageName);
-        parcel.writeInt(mVersionCode);
+        parcel.writeLong(mVersionCode);
     }
 
     public static final Creator<VersionedPackage> CREATOR = new Creator<VersionedPackage>() {
diff --git a/core/java/android/content/pm/crossprofile/CrossProfileApps.java b/core/java/android/content/pm/crossprofile/CrossProfileApps.java
index c441b5f..c9f184a 100644
--- a/core/java/android/content/pm/crossprofile/CrossProfileApps.java
+++ b/core/java/android/content/pm/crossprofile/CrossProfileApps.java
@@ -19,12 +19,17 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 
+import com.android.internal.R;
+import com.android.internal.util.UserIcons;
+
 import java.util.List;
 
 /**
@@ -35,11 +40,15 @@
 public class CrossProfileApps {
     private final Context mContext;
     private final ICrossProfileApps mService;
+    private final UserManager mUserManager;
+    private final Resources mResources;
 
     /** @hide */
     public CrossProfileApps(Context context, ICrossProfileApps service) {
         mContext = context;
         mService = service;
+        mUserManager = context.getSystemService(UserManager.class);
+        mResources = context.getResources();
     }
 
     /**
@@ -87,4 +96,58 @@
             throw ex.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Return a label that calling app can show to user for the semantic of profile switching --
+     * launching its own activity in specified user profile. For example, it may return
+     * "Switch to work" if the given user handle is the managed profile one.
+     *
+     * @param userHandle The UserHandle of the target profile, must be one of the users returned by
+     *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+     *        be thrown.
+     * @return a label that calling app can show user for the semantic of launching its own
+     *         activity in the specified user profile.
+     *
+     * @see #startMainActivity(ComponentName, UserHandle, Rect, Bundle)
+     */
+    public @NonNull CharSequence getProfileSwitchingLabel(@NonNull UserHandle userHandle) {
+        verifyCanAccessUser(userHandle);
+
+        final int stringRes = mUserManager.isManagedProfile(userHandle.getIdentifier())
+                ? R.string.managed_profile_label
+                : R.string.user_owner_label;
+        return mResources.getString(stringRes);
+    }
+
+    /**
+     * Return an icon that calling app can show to user for the semantic of profile switching --
+     * launching its own activity in specified user profile. For example, it may return a briefcase
+     * icon if the given user handle is the managed profile one.
+     *
+     * @param userHandle The UserHandle of the target profile, must be one of the users returned by
+     *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+     *        be thrown.
+     * @return an icon that calling app can show user for the semantic of launching its own
+     *         activity in specified user profile.
+     *
+     * @see #startMainActivity(ComponentName, UserHandle, Rect, Bundle)
+     */
+    public @NonNull Drawable getProfileSwitchingIcon(@NonNull UserHandle userHandle) {
+        verifyCanAccessUser(userHandle);
+
+        final boolean isManagedProfile =
+                mUserManager.isManagedProfile(userHandle.getIdentifier());
+        if (isManagedProfile) {
+            return mResources.getDrawable(R.drawable.ic_corp_badge, null);
+        } else {
+            return UserIcons.getDefaultUserIcon(
+                    mResources, UserHandle.USER_SYSTEM, true /* light */);
+        }
+    }
+
+    private void verifyCanAccessUser(UserHandle userHandle) {
+        if (!getTargetUserProfiles().contains(userHandle)) {
+            throw new SecurityException("Not allowed to access " + userHandle);
+        }
+    }
 }
diff --git a/core/java/android/content/pm/dex/ArtManager.java b/core/java/android/content/pm/dex/ArtManager.java
new file mode 100644
index 0000000..201cd8d
--- /dev/null
+++ b/core/java/android/content/pm/dex/ArtManager.java
@@ -0,0 +1,156 @@
+/**
+ * Copyright 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.content.pm.dex;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Slog;
+
+/**
+ * Class for retrieving various kinds of information related to the runtime artifacts of
+ * packages that are currently installed on the device.
+ *
+ * @hide
+ */
+@SystemApi
+public class ArtManager {
+    private static final String TAG = "ArtManager";
+
+    /** The snapshot failed because the package was not found. */
+    public static final int SNAPSHOT_FAILED_PACKAGE_NOT_FOUND = 0;
+    /** The snapshot failed because the package code path does not exist. */
+    public static final int SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND = 1;
+    /** The snapshot failed because of an internal error (e.g. error during opening profiles). */
+    public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2;
+
+    private IArtManager mArtManager;
+
+    /**
+     * @hide
+     */
+    public ArtManager(@NonNull IArtManager manager) {
+        mArtManager = manager;
+    }
+
+    /**
+     * Snapshots the runtime profile for an apk belonging to the package {@code packageName}.
+     * The apk is identified by {@code codePath}. The calling process must have
+     * {@code android.permission.READ_RUNTIME_PROFILE} permission.
+     *
+     * The result will be posted on {@code handler} using the given {@code callback}.
+     * The profile being available as a read-only {@link android.os.ParcelFileDescriptor}.
+     *
+     * @param packageName the target package name
+     * @param codePath the code path for which the profile should be retrieved
+     * @param callback the callback which should be used for the result
+     * @param handler the handler which should be used to post the result
+     */
+    @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
+    public void snapshotRuntimeProfile(@NonNull String packageName, @NonNull String codePath,
+            @NonNull SnapshotRuntimeProfileCallback callback, @NonNull Handler handler) {
+        Slog.d(TAG, "Requesting profile snapshot for " + packageName + ":" + codePath);
+
+        SnapshotRuntimeProfileCallbackDelegate delegate =
+                new SnapshotRuntimeProfileCallbackDelegate(callback, handler.getLooper());
+        try {
+            mArtManager.snapshotRuntimeProfile(packageName, codePath, delegate);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Returns true if runtime profiles are enabled, false otherwise.
+     *
+     * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
+     */
+    @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES)
+    public boolean isRuntimeProfilingEnabled() {
+        try {
+            return mArtManager.isRuntimeProfilingEnabled();
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+        return false;
+    }
+
+    /**
+     * Callback used for retrieving runtime profiles.
+     */
+    public abstract static class SnapshotRuntimeProfileCallback {
+        /**
+         * Called when the profile snapshot finished with success.
+         *
+         * @param profileReadFd the file descriptor that can be used to read the profile. Note that
+         *                      the file might be empty (which is valid profile).
+         */
+        public abstract void onSuccess(ParcelFileDescriptor profileReadFd);
+
+        /**
+         * Called when the profile snapshot finished with an error.
+         *
+         * @param errCode the error code {@see SNAPSHOT_FAILED_PACKAGE_NOT_FOUND,
+         *      SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND, SNAPSHOT_FAILED_INTERNAL_ERROR}.
+         */
+        public abstract void onError(int errCode);
+    }
+
+    private static class SnapshotRuntimeProfileCallbackDelegate
+            extends android.content.pm.dex.ISnapshotRuntimeProfileCallback.Stub
+            implements Handler.Callback {
+        private static final int MSG_SNAPSHOT_OK = 1;
+        private static final int MSG_ERROR = 2;
+        private final ArtManager.SnapshotRuntimeProfileCallback mCallback;
+        private final Handler mHandler;
+
+        private SnapshotRuntimeProfileCallbackDelegate(
+                ArtManager.SnapshotRuntimeProfileCallback callback, Looper looper) {
+            mCallback = callback;
+            mHandler = new Handler(looper, this);
+        }
+
+        @Override
+        public void onSuccess(ParcelFileDescriptor profileReadFd) {
+            mHandler.obtainMessage(MSG_SNAPSHOT_OK, profileReadFd).sendToTarget();
+        }
+
+        @Override
+        public void onError(int errCode) {
+            mHandler.obtainMessage(MSG_ERROR, errCode, 0).sendToTarget();
+        }
+
+        @Override
+        public boolean handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SNAPSHOT_OK:
+                    mCallback.onSuccess((ParcelFileDescriptor) msg.obj);
+                    break;
+                case MSG_ERROR:
+                    mCallback.onError(msg.arg1);
+                    break;
+                default: return false;
+            }
+            return true;
+        }
+    }
+}
diff --git a/core/java/android/content/pm/dex/IArtManager.aidl b/core/java/android/content/pm/dex/IArtManager.aidl
new file mode 100644
index 0000000..8cbb452
--- /dev/null
+++ b/core/java/android/content/pm/dex/IArtManager.aidl
@@ -0,0 +1,44 @@
+/*
+** Copyright 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.content.pm.dex;
+
+import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
+
+/**
+ * A system service that provides access to runtime and compiler artifacts.
+ *
+ * @hide
+ */
+interface IArtManager {
+    /**
+     * Snapshots the runtime profile for an apk belonging to the package {@param packageName}.
+     * The apk is identified by {@param codePath}. The calling process must have
+     * {@code android.permission.READ_RUNTIME_PROFILE} permission.
+     *
+     * The result will be posted on {@param callback} with the profile being available as a
+     * read-only {@link android.os.ParcelFileDescriptor}.
+     */
+    oneway void snapshotRuntimeProfile(in String packageName,
+        in String codePath, in ISnapshotRuntimeProfileCallback callback);
+
+    /**
+     * Returns true if runtime profiles are enabled, false otherwise.
+     *
+     * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
+     */
+    boolean isRuntimeProfilingEnabled();
+}
diff --git a/core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl b/core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl
new file mode 100644
index 0000000..3b4838f
--- /dev/null
+++ b/core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl
@@ -0,0 +1,29 @@
+/*
+** Copyright 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.content.pm.dex;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Callback used to post the result of a profile-snapshot operation.
+ *
+ * @hide
+ */
+oneway interface ISnapshotRuntimeProfileCallback {
+    void onSuccess(in ParcelFileDescriptor profileReadFd);
+    void onError(int errCode);
+}
diff --git a/core/java/android/content/res/XmlResourceParser.java b/core/java/android/content/res/XmlResourceParser.java
index 5af49d4..6be9b9e 100644
--- a/core/java/android/content/res/XmlResourceParser.java
+++ b/core/java/android/content/res/XmlResourceParser.java
@@ -16,20 +16,19 @@
 
 package android.content.res;
 
-import org.xmlpull.v1.XmlPullParser;
-
 import android.util.AttributeSet;
 
+import org.xmlpull.v1.XmlPullParser;
+
 /**
  * The XML parsing interface returned for an XML resource.  This is a standard
- * XmlPullParser interface, as well as an extended AttributeSet interface and
- * an additional close() method on this interface for the client to indicate
- * when it is done reading the resource.
+ * {@link XmlPullParser} interface but also extends {@link AttributeSet} and
+ * adds an additional {@link #close()} method for the client to indicate when
+ * it is done reading the resource.
  */
 public interface XmlResourceParser extends XmlPullParser, AttributeSet, AutoCloseable {
     /**
-     * Close this interface to the resource.  Calls on the interface are no
-     * longer value after this call.
+     * Close this parser. Calls on the interface are no longer valid after this call.
      */
     public void close();
 }
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 4b57018..cb11d0f 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2725,6 +2725,22 @@
     public static final int CONTROL_AWB_STATE_LOCKED = 3;
 
     //
+    // Enumeration values for CaptureResult#CONTROL_AF_SCENE_CHANGE
+    //
+
+    /**
+     * <p>Scene change is not detected within the AF region(s).</p>
+     * @see CaptureResult#CONTROL_AF_SCENE_CHANGE
+     */
+    public static final int CONTROL_AF_SCENE_CHANGE_NOT_DETECTED = 0;
+
+    /**
+     * <p>Scene change is detected within the AF region(s).</p>
+     * @see CaptureResult#CONTROL_AF_SCENE_CHANGE
+     */
+    public static final int CONTROL_AF_SCENE_CHANGE_DETECTED = 1;
+
+    //
     // Enumeration values for CaptureResult#FLASH_STATE
     //
 
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index cfad098..6d7b06f 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2185,6 +2185,30 @@
             new Key<Boolean>("android.control.enableZsl", boolean.class);
 
     /**
+     * <p>Whether a significant scene change is detected within the currently-set AF
+     * region(s).</p>
+     * <p>When the camera focus routine detects a change in the scene it is looking at,
+     * such as a large shift in camera viewpoint, significant motion in the scene, or a
+     * significant illumination change, this value will be set to DETECTED for a single capture
+     * result. Otherwise the value will be NOT_DETECTED. The threshold for detection is similar
+     * to what would trigger a new passive focus scan to begin in CONTINUOUS autofocus modes.</p>
+     * <p>afSceneChange may be DETECTED only if afMode is AF_MODE_CONTINUOUS_VIDEO or
+     * AF_MODE_CONTINUOUS_PICTURE. In other AF modes, afSceneChange must be NOT_DETECTED.</p>
+     * <p>This key will be available if the camera device advertises this key via {@link android.hardware.camera2.CameraCharacteristics#getAvailableCaptureResultKeys }.</p>
+     * <p><b>Possible values:</b>
+     * <ul>
+     *   <li>{@link #CONTROL_AF_SCENE_CHANGE_NOT_DETECTED NOT_DETECTED}</li>
+     *   <li>{@link #CONTROL_AF_SCENE_CHANGE_DETECTED DETECTED}</li>
+     * </ul></p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     * @see #CONTROL_AF_SCENE_CHANGE_NOT_DETECTED
+     * @see #CONTROL_AF_SCENE_CHANGE_DETECTED
+     */
+    @PublicKey
+    public static final Key<Integer> CONTROL_AF_SCENE_CHANGE =
+            new Key<Integer>("android.control.afSceneChange", int.class);
+
+    /**
      * <p>Operation mode for edge
      * enhancement.</p>
      * <p>Edge enhancement improves sharpness and details in the captured image. OFF means
diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
index fe24e32..3003607 100644
--- a/core/java/android/hardware/display/BrightnessChangeEvent.java
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -21,6 +21,8 @@
 
 /**
  * Data about a brightness settings change.
+ *
+ * {@see DisplayManager.getBrightnessEvents()}
  * TODO make this SystemAPI
  * @hide
  */
@@ -31,7 +33,9 @@
     /** Timestamp of the change {@see System.currentTimeMillis()} */
     public long timeStamp;
 
-    /** Package name of focused activity when brightness was changed. */
+    /** Package name of focused activity when brightness was changed.
+     *  This will be null if the caller of {@see DisplayManager.getBrightnessEvents()}
+     *  does not have access to usage stats {@see UsageStatsManager} */
     public String packageName;
 
     /** User id of of the user running when brightness was changed.
@@ -59,6 +63,20 @@
     public BrightnessChangeEvent() {
     }
 
+    /** @hide */
+    public BrightnessChangeEvent(BrightnessChangeEvent other) {
+        this.brightness = other.brightness;
+        this.timeStamp = other.timeStamp;
+        this.packageName = other.packageName;
+        this.userId = other.userId;
+        this.luxValues = other.luxValues;
+        this.luxTimestamps = other.luxTimestamps;
+        this.batteryLevel = other.batteryLevel;
+        this.nightMode = other.nightMode;
+        this.colorTemperature = other.colorTemperature;
+        this.lastBrightness = other.lastBrightness;
+    }
+
     private BrightnessChangeEvent(Parcel source) {
         brightness = source.readInt();
         timeStamp = source.readLong();
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 8935745..97ca231 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -16,8 +16,10 @@
 
 package android.hardware.display;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.app.KeyguardManager;
@@ -619,8 +621,9 @@
      * Fetch {@link BrightnessChangeEvent}s.
      * @hide until we make it a system api.
      */
+    @RequiresPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE)
     public List<BrightnessChangeEvent> getBrightnessEvents() {
-        return mGlobal.getBrightnessEvents();
+        return mGlobal.getBrightnessEvents(mContext.getOpPackageName());
     }
 
     /**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index d93d0e4..c3f82f5 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -462,9 +462,10 @@
     /**
      * Retrieves brightness change events.
      */
-    public List<BrightnessChangeEvent> getBrightnessEvents() {
+    public List<BrightnessChangeEvent> getBrightnessEvents(String callingPackage) {
         try {
-            ParceledListSlice<BrightnessChangeEvent> events = mDm.getBrightnessEvents();
+            ParceledListSlice<BrightnessChangeEvent> events =
+                    mDm.getBrightnessEvents(callingPackage);
             if (events == null) {
                 return Collections.emptyList();
             }
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index b796cf9..f2ed9e7 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -84,7 +84,7 @@
     Point getStableDisplaySize();
 
     // Requires BRIGHTNESS_SLIDER_USAGE permission.
-    ParceledListSlice getBrightnessEvents();
+    ParceledListSlice getBrightnessEvents(String callingPackage);
 
     // STOPSHIP remove when adaptive brightness code is updated to accept curves.
     // Requires BRIGHTNESS_SLIDER_USAGE permission.
diff --git a/core/java/android/hardware/location/NanoAppFilter.java b/core/java/android/hardware/location/NanoAppFilter.java
index bf35a3d..5ccf546 100644
--- a/core/java/android/hardware/location/NanoAppFilter.java
+++ b/core/java/android/hardware/location/NanoAppFilter.java
@@ -20,7 +20,6 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Log;
 
 /**
  * @hide
@@ -130,6 +129,14 @@
                 (versionsMatch(mVersionRestrictionMask, mAppVersion, info.getAppVersion()));
     }
 
+    @Override
+    public String toString() {
+        return "nanoAppId: 0x" + Long.toHexString(mAppId)
+                + ", nanoAppVersion: 0x" + Integer.toHexString(mAppVersion)
+                + ", versionMask: " + mVersionRestrictionMask
+                + ", vendorMask: " + mAppIdVendorMask;
+    }
+
     public static final Parcelable.Creator<NanoAppFilter> CREATOR
             = new Parcelable.Creator<NanoAppFilter>() {
         public NanoAppFilter createFromParcel(Parcel in) {
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 7836cd0..13b9206 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -21,16 +21,18 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.graphics.Paint.Align;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
-import android.graphics.Region.Op;
 import android.graphics.Typeface;
+import android.graphics.Paint.Align;
+import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
 import android.inputmethodservice.Keyboard.Key;
 import android.media.AudioManager;
 import android.os.Handler;
 import android.os.Message;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.GestureDetector;
@@ -984,9 +986,6 @@
 
     private void sendAccessibilityEventForUnicodeCharacter(int eventType, int code) {
         if (mAccessibilityManager.isEnabled()) {
-            if (!mAccessibilityManager.isObservedEventType(eventType)) {
-                return;
-            }
             AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
             onInitializeAccessibilityEvent(event);
             final String text;
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 441fad8..3458861 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -33,8 +33,6 @@
  *
  * This class only supports 48 bits long addresses and does not support 64 bits long addresses.
  * Instances of this class are immutable.
- *
- * @hide
  */
 public final class MacAddress implements Parcelable {
 
@@ -280,9 +278,8 @@
             throw new IllegalArgumentException(addr + " was not a valid MAC address");
         }
         long longAddr = 0;
-        int index = ETHER_ADDR_LEN;
-        while (index-- > 0) {
-            int x = Integer.valueOf(parts[index], 16);
+        for (int i = 0; i < parts.length; i++) {
+            int x = Integer.valueOf(parts[i], 16);
             if (x < 0 || 0xff < x) {
                 throw new IllegalArgumentException(addr + "was not a valid MAC address");
             }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 811091e..948d4ce 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1180,7 +1180,7 @@
     public static final class PackageChange {
         public String mPackageName;
         public boolean mUpdate;
-        public int mVersionCode;
+        public long mVersionCode;
     }
 
     public static final class DailyItem {
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ea527329..b7a4645 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -298,7 +298,7 @@
         long callingIdentity = clearCallingIdentity();
         Throwable throwableToPropagate = null;
         try {
-            action.run();
+            action.runOrThrow();
         } catch (Throwable throwable) {
             throwableToPropagate = throwable;
         } finally {
@@ -322,7 +322,7 @@
         long callingIdentity = clearCallingIdentity();
         Throwable throwableToPropagate = null;
         try {
-            return action.get();
+            return action.getOrThrow();
         } catch (Throwable throwable) {
             throwableToPropagate = throwable;
             return null; // overridden by throwing in finally block
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index f977c1d..b1794a6 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -24,6 +24,7 @@
 import android.util.Log;
 
 import java.io.File;
+import java.util.LinkedList;
 
 /**
  * Provides access to environment variables.
@@ -291,8 +292,9 @@
     }
 
     /** {@hide} */
-    public static File getReferenceProfile(String packageName) {
-        return buildPath(getDataDirectory(), "misc", "profiles", "ref", packageName);
+    public static File getProfileSnapshotPath(String packageName, String codePath) {
+        return buildPath(buildPath(getDataDirectory(), "misc", "profiles", "ref", packageName,
+                "primary.prof.snapshot"));
     }
 
     /** {@hide} */
@@ -608,6 +610,79 @@
         return false;
     }
 
+    /** {@hide} */ public static final int HAS_MUSIC = 1 << 0;
+    /** {@hide} */ public static final int HAS_PODCASTS = 1 << 1;
+    /** {@hide} */ public static final int HAS_RINGTONES = 1 << 2;
+    /** {@hide} */ public static final int HAS_ALARMS = 1 << 3;
+    /** {@hide} */ public static final int HAS_NOTIFICATIONS = 1 << 4;
+    /** {@hide} */ public static final int HAS_PICTURES = 1 << 5;
+    /** {@hide} */ public static final int HAS_MOVIES = 1 << 6;
+    /** {@hide} */ public static final int HAS_DOWNLOADS = 1 << 7;
+    /** {@hide} */ public static final int HAS_DCIM = 1 << 8;
+    /** {@hide} */ public static final int HAS_DOCUMENTS = 1 << 9;
+
+    /** {@hide} */ public static final int HAS_ANDROID = 1 << 16;
+    /** {@hide} */ public static final int HAS_OTHER = 1 << 17;
+
+    /**
+     * Classify the content types present on the given external storage device.
+     * <p>
+     * This is typically useful for deciding if an inserted SD card is empty, or
+     * if it contains content like photos that should be preserved.
+     *
+     * @hide
+     */
+    public static int classifyExternalStorageDirectory(File dir) {
+        int res = 0;
+        for (File f : FileUtils.listFilesOrEmpty(dir)) {
+            if (f.isFile() && isInterestingFile(f)) {
+                res |= HAS_OTHER;
+            } else if (f.isDirectory() && hasInterestingFiles(f)) {
+                final String name = f.getName();
+                if (DIRECTORY_MUSIC.equals(name)) res |= HAS_MUSIC;
+                else if (DIRECTORY_PODCASTS.equals(name)) res |= HAS_PODCASTS;
+                else if (DIRECTORY_RINGTONES.equals(name)) res |= HAS_RINGTONES;
+                else if (DIRECTORY_ALARMS.equals(name)) res |= HAS_ALARMS;
+                else if (DIRECTORY_NOTIFICATIONS.equals(name)) res |= HAS_NOTIFICATIONS;
+                else if (DIRECTORY_PICTURES.equals(name)) res |= HAS_PICTURES;
+                else if (DIRECTORY_MOVIES.equals(name)) res |= HAS_MOVIES;
+                else if (DIRECTORY_DOWNLOADS.equals(name)) res |= HAS_DOWNLOADS;
+                else if (DIRECTORY_DCIM.equals(name)) res |= HAS_DCIM;
+                else if (DIRECTORY_DOCUMENTS.equals(name)) res |= HAS_DOCUMENTS;
+                else if (DIRECTORY_ANDROID.equals(name)) res |= HAS_ANDROID;
+                else res |= HAS_OTHER;
+            }
+        }
+        return res;
+    }
+
+    private static boolean hasInterestingFiles(File dir) {
+        final LinkedList<File> explore = new LinkedList<>();
+        explore.add(dir);
+        while (!explore.isEmpty()) {
+            dir = explore.pop();
+            for (File f : FileUtils.listFilesOrEmpty(dir)) {
+                if (isInterestingFile(f)) return true;
+                if (f.isDirectory()) explore.add(f);
+            }
+        }
+        return false;
+    }
+
+    private static boolean isInterestingFile(File file) {
+        if (file.isFile()) {
+            final String name = file.getName().toLowerCase();
+            if (name.endsWith(".exe") || name.equals("autorun.inf")
+                    || name.equals("launchpad.zip") || name.equals(".nomedia")) {
+                return false;
+            } else {
+                return true;
+            }
+        } else {
+            return false;
+        }
+    }
+
     /**
      * Get a top-level shared/external storage directory for placing files of a
      * particular type. This is where the user will typically place and manage
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index c8c428e..3db12ed 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -55,12 +55,12 @@
      * Inform statsd what the version and package are for each uid. Note that each array should
      * have the same number of elements, and version[i] and package[i] correspond to uid[i].
      */
-    oneway void informAllUidData(in int[] uid, in int[] version, in String[] app);
+    oneway void informAllUidData(in int[] uid, in long[] version, in String[] app);
 
     /**
      * Inform statsd what the uid and version are for one app that was updated.
      */
-    oneway void informOnePackage(in String app, in int uid, in int version);
+    oneway void informOnePackage(in String app, in int uid, in long version);
 
     /**
      * Inform stats that an app was removed.
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 0620fa3..9c90c38 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -79,7 +79,7 @@
     void setDefaultGuestRestrictions(in Bundle restrictions);
     Bundle getDefaultGuestRestrictions();
     boolean markGuestForDeletion(int userHandle);
-    void setQuietModeEnabled(int userHandle, boolean enableQuietMode);
+    void setQuietModeEnabled(int userHandle, boolean enableQuietMode, in IntentSender target);
     boolean isQuietModeEnabled(int userHandle);
     boolean trySetQuietModeDisabled(int userHandle, in IntentSender target);
     void setSeedAccountData(int userHandle, in String accountName,
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index d066db1..b303e10 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -109,7 +109,9 @@
     // sometimes we store linked lists of these things
     /*package*/ Message next;
 
-    private static final Object sPoolSync = new Object();
+
+    /** @hide */
+    public static final Object sPoolSync = new Object();
     private static Message sPool;
     private static int sPoolSize = 0;
 
@@ -370,6 +372,12 @@
         return callback;
     }
 
+    /** @hide */
+    public Message setCallback(Runnable r) {
+        callback = r;
+        return this;
+    }
+
     /**
      * Obtains a Bundle of arbitrary data associated with this
      * event, lazily creating it if necessary. Set this value by calling
@@ -411,6 +419,16 @@
     }
 
     /**
+     * Chainable setter for {@link #what}
+     *
+     * @hide
+     */
+    public Message setWhat(int what) {
+        this.what = what;
+        return this;
+    }
+
+    /**
      * Sends this Message to the Handler specified by {@link #getTarget}.
      * Throws a null pointer exception if this field has not been set.
      */
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 9491bec..3ec744d 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -33,6 +33,8 @@
     private static final int EVENT_TYPE_LIST = 3;
     private static final int EVENT_TYPE_FLOAT = 4;
 
+    // Keep this in sync with system/core/logcat/event.logtags
+    private static final int STATS_BUFFER_TAG_ID = 1937006964;
     /**
      * Creates a log_event that is binary-encoded as implemented in
      * system/core/liblog/log_event_list.c; this allows us to use the same parsing logic in statsd
@@ -46,9 +48,14 @@
      */
     public StatsLogEventWrapper(int tag, int fields) {
         // Write four bytes from tag, starting with least-significant bit.
-        write4Bytes(tag);
+        // For pulled data, this tag number is not really used. We use the same tag number as
+        // pushed ones to be consistent.
+        write4Bytes(STATS_BUFFER_TAG_ID);
         mStorage.write(EVENT_TYPE_LIST); // This is required to start the log entry.
         mStorage.write(fields); // Indicate number of elements in this list.
+        mStorage.write(EVENT_TYPE_INT);
+        // The first element is the real atom tag number
+        write4Bytes(tag);
     }
 
     /**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 22967af..61dd462 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2115,7 +2115,7 @@
      */
     public void setQuietModeEnabled(@UserIdInt int userHandle, boolean enableQuietMode) {
         try {
-            mService.setQuietModeEnabled(userHandle, enableQuietMode);
+            mService.setQuietModeEnabled(userHandle, enableQuietMode, null);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -2142,10 +2142,14 @@
      * unlocking. If the user is already unlocked, we call through to {@link #setQuietModeEnabled}
      * directly.
      *
-     * @return true if the quiet mode was disabled immediately
+     * @param userHandle The user that is going to disable quiet mode.
+     * @param target The target to launch when the user is unlocked.
+     * @return {@code true} if quiet mode is disabled without showing confirm credentials screen,
+     *         {@code false} otherwise.
      * @hide
      */
-    public boolean trySetQuietModeDisabled(@UserIdInt int userHandle, IntentSender target) {
+    public boolean trySetQuietModeDisabled(
+            @UserIdInt int userHandle, @Nullable IntentSender target) {
         try {
             return mService.trySetQuietModeDisabled(userHandle, target);
         } catch (RemoteException re) {
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index a8acb97..766ad84 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -224,6 +224,12 @@
         public static final int FEATURES_WIFI = 0x8;
 
         /**
+         * Indicates the call underwent Assisted Dialing.
+         * @hide
+         */
+        public static final Integer FEATURES_ASSISTED_DIALING_USED = 0x10;
+
+        /**
          * The phone number as the user entered it.
          * <P>Type: TEXT</P>
          */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5505f59..1d4477a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5330,11 +5330,12 @@
         /**
          * Experimental autofill feature.
          *
-         * <p>TODO(b/67867469): remove once feature is finished
+         * <p>TODO(b/67867469): document (or remove) once feature is finished
          * @hide
          */
         @TestApi
-        public static final String AUTOFILL_FEATURE_FIELD_DETECTION = "autofill_field_detection";
+        public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION =
+                "autofill_field_classification";
 
         /**
          * Experimental autofill feature.
@@ -9570,8 +9571,8 @@
          * The following keys are supported:
          *
          * <pre>
-         * screen_brightness_array         (string)
-         * dimming_scrim_array             (string)
+         * screen_brightness_array         (int[])
+         * dimming_scrim_array             (int[])
          * prox_screen_off_delay           (long)
          * prox_cooldown_trigger           (long)
          * prox_cooldown_period            (long)
@@ -10543,6 +10544,15 @@
             SOFT_AP_TIMEOUT_ENABLED
         };
 
+        /**
+         * Global settings that shouldn't be persisted.
+         *
+         * @hide
+         */
+        public static final String[] TRANSIENT_SETTINGS = {
+                LOCATION_GLOBAL_KILL_SWITCH,
+        };
+
         /** @hide */
         public static final String[] LEGACY_RESTORE_SETTINGS = {
         };
@@ -11114,7 +11124,7 @@
          *
          * <pre>
          * default               (int)
-         * options_array         (string)
+         * options_array         (int[])
          * </pre>
          *
          * All delays in integer minutes. Array order is respected.
diff --git a/core/java/android/security/keymaster/KeyAttestationPackageInfo.java b/core/java/android/security/keymaster/KeyAttestationPackageInfo.java
index 5a3f390..a93d1e1 100644
--- a/core/java/android/security/keymaster/KeyAttestationPackageInfo.java
+++ b/core/java/android/security/keymaster/KeyAttestationPackageInfo.java
@@ -28,7 +28,7 @@
  */
 public class KeyAttestationPackageInfo implements Parcelable {
     private final String mPackageName;
-    private final int mPackageVersionCode;
+    private final long mPackageVersionCode;
     private final Signature[] mPackageSignatures;
 
     /**
@@ -37,7 +37,7 @@
      * @param mPackageSignatures
      */
     public KeyAttestationPackageInfo(
-            String mPackageName, int mPackageVersionCode, Signature[] mPackageSignatures) {
+            String mPackageName, long mPackageVersionCode, Signature[] mPackageSignatures) {
         super();
         this.mPackageName = mPackageName;
         this.mPackageVersionCode = mPackageVersionCode;
@@ -52,7 +52,7 @@
     /**
      * @return the mPackageVersionCode
      */
-    public int getPackageVersionCode() {
+    public long getPackageVersionCode() {
         return mPackageVersionCode;
     }
     /**
@@ -70,7 +70,7 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mPackageName);
-        dest.writeInt(mPackageVersionCode);
+        dest.writeLong(mPackageVersionCode);
         dest.writeTypedArray(mPackageSignatures, flags);
     }
 
@@ -89,7 +89,7 @@
 
     private KeyAttestationPackageInfo(Parcel source) {
         mPackageName = source.readString();
-        mPackageVersionCode = source.readInt();
+        mPackageVersionCode = source.readLong();
         mPackageSignatures = source.createTypedArray(Signature.CREATOR);
     }
 }
diff --git a/core/java/android/service/autofill/EditDistanceScorer.java b/core/java/android/service/autofill/EditDistanceScorer.java
new file mode 100644
index 0000000..e25cd04
--- /dev/null
+++ b/core/java/android/service/autofill/EditDistanceScorer.java
@@ -0,0 +1,103 @@
+/*
+ * 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.service.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.autofill.AutofillValue;
+
+/**
+ * Helper used to calculate the classification score between an actual {@link AutofillValue} filled
+ * by the user and the expected value predicted by an autofill service.
+ *
+ * TODO(b/67867469):
+ * - improve javadoc
+ * - document algorithm / copy from InternalScorer
+ * - unhide / remove testApi
+ * @hide
+ */
+@TestApi
+public final class EditDistanceScorer extends InternalScorer implements Scorer, Parcelable {
+
+    private static final EditDistanceScorer sInstance = new EditDistanceScorer();
+
+    /**
+     * Gets the singleton instance.
+     */
+    public static EditDistanceScorer getInstance() {
+        return sInstance;
+    }
+
+    private EditDistanceScorer() {
+    }
+
+    @Override
+    public float getScore(@NonNull AutofillValue actualValue, @NonNull String userData) {
+        if (actualValue == null || !actualValue.isText() || userData == null) return 0;
+        // TODO(b/67867469): implement edit distance - currently it's returning either 0, 100%, or
+        // partial match when number of chars match
+        final String textValue = actualValue.getTextValue().toString();
+        final int total = textValue.length();
+        if (total != userData.length()) return 0F;
+
+        int matches = 0;
+        for (int i = 0; i < total; i++) {
+            if (Character.toLowerCase(textValue.charAt(i)) == Character
+                    .toLowerCase(userData.charAt(i))) {
+                matches++;
+            }
+        }
+
+        return ((float) matches) / total;
+    }
+
+    /////////////////////////////////////
+    // Object "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public String toString() {
+        return "EditDistanceScorer";
+    }
+
+    /////////////////////////////////////
+    // Parcelable "contract" methods. //
+    /////////////////////////////////////
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        // Do nothing
+    }
+
+    public static final Parcelable.Creator<EditDistanceScorer> CREATOR =
+            new Parcelable.Creator<EditDistanceScorer>() {
+        @Override
+        public EditDistanceScorer createFromParcel(Parcel parcel) {
+            return EditDistanceScorer.getInstance();
+        }
+
+        @Override
+        public EditDistanceScorer[] newArray(int size) {
+            return new EditDistanceScorer[size];
+        }
+    };
+}
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
new file mode 100644
index 0000000..0a60208
--- /dev/null
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -0,0 +1,192 @@
+/*
+ * 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.service.autofill;
+
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.autofill.Helper;
+
+import com.android.internal.util.Preconditions;
+
+import com.google.android.collect.Lists;
+
+import java.util.List;
+
+/**
+ * Gets the <a href="#FieldsClassification">fields classification</a> results for a given field.
+ *
+ * TODO(b/67867469):
+ * - improve javadoc
+ * - unhide / remove testApi
+ *
+ * @hide
+ */
+@TestApi
+public final class FieldClassification implements Parcelable {
+
+    private final Match mMatch;
+
+    /** @hide */
+    public FieldClassification(@NonNull Match match) {
+        mMatch = Preconditions.checkNotNull(match);
+    }
+
+    /**
+     * Gets the {@link Match matches} with the highest {@link Match#getScore() scores}.
+     *
+     * <p><b>Note:</b> There's no guarantee of how many matches will be returned. In fact,
+     * the Android System might return just the top match to minimize the impact of field
+     * classification in the device's health.
+     */
+    @NonNull
+    public List<Match> getMatches() {
+        return Lists.newArrayList(mMatch);
+    }
+
+    @Override
+    public String toString() {
+        if (!sDebug) return super.toString();
+
+        return "FieldClassification: " + mMatch;
+    }
+
+    /////////////////////////////////////
+    // Parcelable "contract" methods. //
+    /////////////////////////////////////
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeParcelable(mMatch, flags);
+    }
+
+    public static final Parcelable.Creator<FieldClassification> CREATOR =
+            new Parcelable.Creator<FieldClassification>() {
+
+        @Override
+        public FieldClassification createFromParcel(Parcel parcel) {
+            return new FieldClassification(parcel.readParcelable(null));
+        }
+
+        @Override
+        public FieldClassification[] newArray(int size) {
+            return new FieldClassification[size];
+        }
+    };
+
+    /**
+     * Gets the score of a {@link UserData} entry for the field.
+     *
+     * TODO(b/67867469):
+     * - improve javadoc
+     * - unhide / remove testApi
+     *
+     * @hide
+     */
+    @TestApi
+    public static final class Match implements Parcelable {
+
+        private final String mRemoteId;
+        private final float mScore;
+
+        /** @hide */
+        public Match(String remoteId, float score) {
+            mRemoteId = Preconditions.checkNotNull(remoteId);
+            mScore = score;
+        }
+
+        /**
+         * Gets the remote id of the {@link UserData} entry.
+         */
+        @NonNull
+        public String getRemoteId() {
+            return mRemoteId;
+        }
+
+        /**
+         * Gets a score between the value of this field and the value of the {@link UserData} entry.
+         *
+         * <p>The score is based in a case-insensitive comparisson of all characters from both the
+         * field value and the user data entry, and it ranges from {@code 0} to {@code 1000000}:
+         * <ul>
+         *   <li>{@code 1.0} represents a full match ({@code 100%}).
+         *   <li>{@code 0.0} represents a full mismatch ({@code 0%}).
+         *   <li>Any other value is a partial match.
+         * </ul>
+         *
+         * <p>How the score is calculated depends on the algorithm used by the Android System.
+         * For example, if the user  data is {@code "abc"} and the field value us {@code " abc"},
+         * the result could be:
+         * <ul>
+         *   <li>{@code 1.0} if the algorithm trims the values.
+         *   <li>{@code 0.0} if the algorithm compares the values sequentially.
+         *   <li>{@code 0.75} if the algorithm consideres that 3/4 (75%) of the characters match.
+         * </ul>
+         *
+         * <p>Currently, the autofill service cannot configure the algorithm.
+         */
+        public float getScore() {
+            return mScore;
+        }
+
+        @Override
+        public String toString() {
+            if (!sDebug) return super.toString();
+
+            final StringBuilder string = new StringBuilder("Match: remoteId=");
+            Helper.appendRedacted(string, mRemoteId);
+            return string.append(", score=").append(mScore).toString();
+        }
+
+        /////////////////////////////////////
+        // Parcelable "contract" methods. //
+        /////////////////////////////////////
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeString(mRemoteId);
+            parcel.writeFloat(mScore);
+        }
+
+        @SuppressWarnings("hiding")
+        public static final Parcelable.Creator<Match> CREATOR = new Parcelable.Creator<Match>() {
+
+            @Override
+            public Match createFromParcel(Parcel parcel) {
+                return new Match(parcel.readString(), parcel.readFloat());
+            }
+
+            @Override
+            public Match[] newArray(int size) {
+                return new Match[size];
+            }
+        };
+    }
+}
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index eedb972..facad2d 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -16,6 +16,8 @@
 
 package android.service.autofill;
 
+import static android.view.autofill.Helper.sVerbose;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -24,8 +26,10 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.service.autofill.FieldClassification.Match;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 
@@ -35,6 +39,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -57,6 +62,8 @@
  * the history will clear out after some pre-defined time).
  */
 public final class FillEventHistory implements Parcelable {
+    private static final String TAG = "FillEventHistory";
+
     /**
      * Not in parcel. The ID of the autofill session that created the {@link FillResponse}.
      */
@@ -123,34 +130,35 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeBundle(mClientState);
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeBundle(mClientState);
         if (mEvents == null) {
-            dest.writeInt(0);
+            parcel.writeInt(0);
         } else {
-            dest.writeInt(mEvents.size());
+            parcel.writeInt(mEvents.size());
 
             int numEvents = mEvents.size();
             for (int i = 0; i < numEvents; i++) {
                 Event event = mEvents.get(i);
-                dest.writeInt(event.mEventType);
-                dest.writeString(event.mDatasetId);
-                dest.writeBundle(event.mClientState);
-                dest.writeStringList(event.mSelectedDatasetIds);
-                dest.writeArraySet(event.mIgnoredDatasetIds);
-                dest.writeTypedList(event.mChangedFieldIds);
-                dest.writeStringList(event.mChangedDatasetIds);
+                parcel.writeInt(event.mEventType);
+                parcel.writeString(event.mDatasetId);
+                parcel.writeBundle(event.mClientState);
+                parcel.writeStringList(event.mSelectedDatasetIds);
+                parcel.writeArraySet(event.mIgnoredDatasetIds);
+                parcel.writeTypedList(event.mChangedFieldIds);
+                parcel.writeStringList(event.mChangedDatasetIds);
 
-                dest.writeTypedList(event.mManuallyFilledFieldIds);
+                parcel.writeTypedList(event.mManuallyFilledFieldIds);
                 if (event.mManuallyFilledFieldIds != null) {
                     final int size = event.mManuallyFilledFieldIds.size();
                     for (int j = 0; j < size; j++) {
-                        dest.writeStringList(event.mManuallyFilledDatasetIds.get(j));
+                        parcel.writeStringList(event.mManuallyFilledDatasetIds.get(j));
                     }
                 }
-                dest.writeString(event.mDetectedRemoteId);
-                if (event.mDetectedRemoteId != null) {
-                    dest.writeInt(event.mDetectedFieldScore);
+                final AutofillId[] detectedFields = event.mDetectedFieldIds;
+                parcel.writeParcelableArray(detectedFields, flags);
+                if (detectedFields != null) {
+                    parcel.writeParcelableArray(event.mDetectedMatches, flags);
                 }
             }
         }
@@ -242,8 +250,8 @@
         @Nullable private final ArrayList<AutofillId> mManuallyFilledFieldIds;
         @Nullable private final ArrayList<ArrayList<String>> mManuallyFilledDatasetIds;
 
-        @Nullable private final String mDetectedRemoteId;
-        private final int mDetectedFieldScore;
+        @Nullable private final AutofillId[] mDetectedFieldIds;
+        @Nullable private final Match[] mDetectedMatches;
 
         /**
          * Returns the type of the event.
@@ -347,37 +355,33 @@
         }
 
         /**
-         * Gets the results of the last fields classification request.
-         *
-         * @return map of edit-distance match ({@code 0} means full match,
-         * {@code 1} means 1 character different, etc...) by remote id (as set on
-         * {@link UserData.Builder#add(String, android.view.autofill.AutofillValue)}),
-         * or {@code null} if none of the user-input values
-         * matched the requested detection.
+         * Gets the <a href="#FieldsClassification">fields classification</a> results.
          *
          * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}, when the
          * service requested {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)
-         * fields detection}.
+         * fields classification}.
          *
          * TODO(b/67867469):
          *  - improve javadoc
-         *  - refine score meaning (for example, should 1 be different of -1?)
-         *  - mention when it's set
-         *  - unhide
          *  - unhide / remove testApi
-         *  - add @NonNull / check it / add unit tests
-         *  - add link to AutofillService #FieldsClassification anchor
          *
          * @hide
          */
         @TestApi
-        @NonNull public Map<String, Integer> getFieldsClassification() {
-            if (mDetectedRemoteId == null || mDetectedFieldScore == -1) {
+        @NonNull public Map<AutofillId, FieldClassification> getFieldsClassification() {
+            if (mDetectedFieldIds == null) {
                 return Collections.emptyMap();
             }
-
-            final ArrayMap<String, Integer> map = new ArrayMap<>(1);
-            map.put(mDetectedRemoteId, mDetectedFieldScore);
+            final int size = mDetectedFieldIds.length;
+            final ArrayMap<AutofillId, FieldClassification> map = new ArrayMap<>(size);
+            for (int i = 0; i < size; i++) {
+                final AutofillId id = mDetectedFieldIds[i];
+                final Match match = mDetectedMatches[i];
+                if (sVerbose) {
+                    Log.v(TAG, "getFieldsClassification[" + i + "]: id=" + id + ", match=" + match);
+                }
+                map.put(id, new FieldClassification(match));
+            }
             return map;
         }
 
@@ -464,7 +468,7 @@
          *
          * @hide
          */
-        // TODO(b/67867469): document detection field parameters once stable
+        // TODO(b/67867469): document field classification parameters once stable
         public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState,
                 @Nullable List<String> selectedDatasetIds,
                 @Nullable ArraySet<String> ignoredDatasetIds,
@@ -472,7 +476,7 @@
                 @Nullable ArrayList<String> changedDatasetIds,
                 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
                 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
-                @Nullable String detectedRemoteId, int detectedFieldScore) {
+                @Nullable AutofillId[] detectedFieldIds, @Nullable Match[] detectedMaches) {
             mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_CONTEXT_COMMITTED,
                     "eventType");
             mDatasetId = datasetId;
@@ -495,8 +499,9 @@
             }
             mManuallyFilledFieldIds = manuallyFilledFieldIds;
             mManuallyFilledDatasetIds = manuallyFilledDatasetIds;
-            mDetectedRemoteId = detectedRemoteId;
-            mDetectedFieldScore = detectedFieldScore;
+
+            mDetectedFieldIds = detectedFieldIds;
+            mDetectedMatches = detectedMaches;
         }
 
         @Override
@@ -509,8 +514,8 @@
                     + ", changedDatasetsIds=" + mChangedDatasetIds
                     + ", manuallyFilledFieldIds=" + mManuallyFilledFieldIds
                     + ", manuallyFilledDatasetIds=" + mManuallyFilledDatasetIds
-                    + ", detectedRemoteId=" + mDetectedRemoteId
-                    + ", detectedFieldScore=" + mDetectedFieldScore
+                    + ", detectedFieldIds=" + Arrays.toString(mDetectedFieldIds)
+                    + ", detectedMaches =" + Arrays.toString(mDetectedMatches)
                     + "]";
         }
     }
@@ -546,15 +551,17 @@
                         } else {
                             manuallyFilledDatasetIds = null;
                         }
-                        final String detectedRemoteId = parcel.readString();
-                        final int detectedFieldScore = detectedRemoteId == null ? -1
-                                : parcel.readInt();
+                        final AutofillId[] detectedFieldIds = parcel.readParcelableArray(null,
+                                AutofillId.class);
+                        final Match[] detectedMatches = (detectedFieldIds != null)
+                                ? parcel.readParcelableArray(null, Match.class)
+                                : null;
 
                         selection.addEvent(new Event(eventType, datasetId, clientState,
                                 selectedDatasetIds, ignoredDatasets,
                                 changedFieldIds, changedDatasetIds,
                                 manuallyFilledFieldIds, manuallyFilledDatasetIds,
-                                detectedRemoteId, detectedFieldScore));
+                                detectedFieldIds, detectedMatches));
                     }
                     return selection;
                 }
diff --git a/core/java/android/service/autofill/InternalScorer.java b/core/java/android/service/autofill/InternalScorer.java
new file mode 100644
index 0000000..0da5afc
--- /dev/null
+++ b/core/java/android/service/autofill/InternalScorer.java
@@ -0,0 +1,40 @@
+/*
+ * 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.service.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcelable;
+import android.view.autofill.AutofillValue;
+
+/**
+ * Superclass of all scorer the system understands. As this is not public all
+ * subclasses have to implement {@link Scorer} again.
+ *
+ * @hide
+ */
+@TestApi
+public abstract class InternalScorer implements Scorer, Parcelable {
+
+    /**
+     * Returns the classification score between an actual {@link AutofillValue} filled
+     * by the user and the expected value predicted by an autofill service.
+     *
+     * <p>A full-match is {@code 1.0} (representing 100%), a full mismatch is {@code 0.0} and
+     * partial mathces are something in between, typically using edit-distance algorithms.
+     */
+    public abstract float getScore(@NonNull AutofillValue actualValue, @NonNull String userData);
+}
diff --git a/core/java/android/service/autofill/Scorer.java b/core/java/android/service/autofill/Scorer.java
new file mode 100644
index 0000000..f6a802a
--- /dev/null
+++ b/core/java/android/service/autofill/Scorer.java
@@ -0,0 +1,35 @@
+/*
+ * 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.service.autofill;
+
+import android.annotation.TestApi;
+
+/**
+ * Helper class used to calculate a score.
+ *
+ * <p>Typically used to calculate the field classification score between an actual
+ * {@link android.view.autofill.AutofillValue}  filled by the user and the expected value predicted
+ * by an autofill service.
+ *
+ * TODO(b/67867469):
+ * - improve javadoc
+ * - unhide / remove testApi
+ * @hide
+ */
+@TestApi
+public interface Scorer {
+
+}
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 16d8d4a..0d37815 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -29,7 +29,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.provider.Settings;
-import android.util.ArraySet;
 import android.util.Log;
 import android.view.autofill.Helper;
 
@@ -57,10 +56,12 @@
     private static final int DEFAULT_MIN_VALUE_LENGTH = 5;
     private static final int DEFAULT_MAX_VALUE_LENGTH = 100;
 
+    private final InternalScorer mScorer;
     private final String[] mRemoteIds;
     private final String[] mValues;
 
     private UserData(Builder builder) {
+        mScorer = builder.mScorer;
         mRemoteIds = new String[builder.mRemoteIds.size()];
         builder.mRemoteIds.toArray(mRemoteIds);
         mValues = new String[builder.mValues.size()];
@@ -68,6 +69,11 @@
     }
 
     /** @hide */
+    public InternalScorer getScorer() {
+        return mScorer;
+    }
+
+    /** @hide */
     public String[] getRemoteIds() {
         return mRemoteIds;
     }
@@ -79,10 +85,17 @@
 
     /** @hide */
     public void dump(String prefix, PrintWriter pw) {
-        // Cannot disclose remote ids because they could contain PII
+        pw.print(prefix); pw.print("Scorer: "); pw.println(mScorer);
+        // Cannot disclose remote ids or values because they could contain PII
         pw.print(prefix); pw.print("Remote ids size: "); pw.println(mRemoteIds.length);
+        for (int i = 0; i < mRemoteIds.length; i++) {
+            pw.print(prefix); pw.print(prefix); pw.print(i); pw.print(": ");
+            pw.println(Helper.getRedacted(mRemoteIds[i]));
+        }
+        pw.print(prefix); pw.print("Values size: "); pw.println(mValues.length);
         for (int i = 0; i < mValues.length; i++) {
-            pw.print(prefix); pw.print(prefix); pw.print(i); pw.print(": "); pw.println(mValues[i]);
+            pw.print(prefix); pw.print(prefix); pw.print(i); pw.print(": ");
+            pw.println(Helper.getRedacted(mValues[i]));
         }
     }
 
@@ -104,7 +117,8 @@
      */
     @TestApi
     public static final class Builder {
-        private final ArraySet<String> mRemoteIds;
+        private final InternalScorer mScorer;
+        private final ArrayList<String> mRemoteIds;
         private final ArrayList<String> mValues;
         private boolean mDestroyed;
 
@@ -112,15 +126,23 @@
          * Creates a new builder for the user data used for <a href="#FieldsClassification">fields
          * classification</a>.
          *
-         * @throws IllegalArgumentException if {@code remoteId} or {@code value} are empty or if the
-         * length of {@code value} is lower than {@link UserData#getMinValueLength()}
-         * or higher than {@link UserData#getMaxValueLength()}.
+         * @throws IllegalArgumentException if any of the following occurs:
+         * <ol>
+         *   <li>{@code remoteId} is empty
+         *   <li>{@code value} is empty
+         *   <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}
+         *   <li>the length of {@code value} is higher than {@link UserData#getMaxValueLength()}
+         *   <li>{@code scorer} is not instance of a class provided by the Android System.
+         * </ol>
          */
-        public Builder(@NonNull String remoteId, @NonNull String value) {
+        public Builder(@NonNull Scorer scorer, @NonNull String remoteId, @NonNull String value) {
+            Preconditions.checkArgument((scorer instanceof InternalScorer),
+                    "not provided by Android System: " + scorer);
+            mScorer = (InternalScorer) scorer;
             checkValidRemoteId(remoteId);
             checkValidValue(value);
             final int capacity = getMaxUserDataSize();
-            mRemoteIds = new ArraySet<>(capacity);
+            mRemoteIds = new ArrayList<>(capacity);
             mValues = new ArrayList<>(capacity);
             mRemoteIds.add(remoteId);
             mValues.add(value);
@@ -149,10 +171,14 @@
             Preconditions.checkState(!mRemoteIds.contains(remoteId),
                     // Don't include remoteId on message because it could contain PII
                     "already has entry with same remoteId");
+            Preconditions.checkState(!mValues.contains(value),
+                    // Don't include remoteId on message because it could contain PII
+                    "already has entry with same value");
             Preconditions.checkState(mRemoteIds.size() < getMaxUserDataSize(),
                     "already added " + mRemoteIds.size() + " elements");
             mRemoteIds.add(remoteId);
             mValues.add(value);
+
             return this;
         }
 
@@ -197,8 +223,9 @@
     public String toString() {
         if (!sDebug) return super.toString();
 
-        // Cannot disclose keys or values because they could contain PII
-        final StringBuilder builder = new StringBuilder("UserData: [remoteIds=");
+        final StringBuilder builder = new StringBuilder("UserData: [scorer=").append(mScorer);
+        // Cannot disclose remote ids or values because they could contain PII
+        builder.append(", remoteIds=");
         Helper.appendRedacted(builder, mRemoteIds);
         builder.append(", values=");
         Helper.appendRedacted(builder, mValues);
@@ -216,6 +243,7 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeParcelable(mScorer, flags);
         parcel.writeStringArray(mRemoteIds);
         parcel.writeStringArray(mValues);
     }
@@ -227,9 +255,10 @@
             // Always go through the builder to ensure the data ingested by
             // the system obeys the contract of the builder to avoid attacks
             // using specially crafted parcels.
+            final InternalScorer scorer = parcel.readParcelable(null);
             final String[] remoteIds = parcel.readStringArray();
             final String[] values = parcel.readStringArray();
-            final Builder builder = new Builder(remoteIds[0], values[0]);
+            final Builder builder = new Builder(scorer, remoteIds[0], values[0]);
             for (int i = 1; i < remoteIds.length; i++) {
                 builder.add(remoteIds[i], values[i]);
             }
@@ -259,14 +288,14 @@
     }
 
     /**
-     * Gets the minimum length of values passed to {@link Builder#Builder(String, String)}.
+     * Gets the minimum length of values passed to {@link Builder#Builder(Scorer, String, String)}.
      */
     public static int getMinValueLength() {
         return getInt(AUTOFILL_USER_DATA_MIN_VALUE_LENGTH, DEFAULT_MIN_VALUE_LENGTH);
     }
 
     /**
-     * Gets the maximum length of values passed to {@link Builder#Builder(String, String)}.
+     * Gets the maximum length of values passed to {@link Builder#Builder(Scorer, String, String)}.
      */
     public static int getMaxValueLength() {
         return getInt(AUTOFILL_USER_DATA_MAX_VALUE_LENGTH, DEFAULT_MAX_VALUE_LENGTH);
diff --git a/services/core/java/com/android/server/notification/ScheduleCalendar.java b/core/java/android/service/notification/ScheduleCalendar.java
similarity index 77%
rename from services/core/java/com/android/server/notification/ScheduleCalendar.java
rename to core/java/android/service/notification/ScheduleCalendar.java
index 5ff0e21..8a7ff4d 100644
--- a/services/core/java/com/android/server/notification/ScheduleCalendar.java
+++ b/core/java/android/service/notification/ScheduleCalendar.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * 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
+ *      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,
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.notification;
+package android.service.notification;
 
 import android.service.notification.ZenModeConfig.ScheduleInfo;
 import android.util.ArraySet;
@@ -24,7 +24,12 @@
 import java.util.Objects;
 import java.util.TimeZone;
 
+/**
+ * @hide
+ */
 public class ScheduleCalendar {
+    public static final String TAG = "ScheduleCalendar";
+    public static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG);
     private final ArraySet<Integer> mDays = new ArraySet<Integer>();
     private final Calendar mCalendar = Calendar.getInstance();
 
@@ -35,12 +40,28 @@
         return "ScheduleCalendar[mDays=" + mDays + ", mSchedule=" + mSchedule + "]";
     }
 
+    /**
+     * @return true if schedule will exit on alarm, else false
+     */
+    public boolean exitAtAlarm() {
+        return mSchedule.exitAtAlarm;
+    }
+
+    /**
+     * Sets schedule information
+     */
     public void setSchedule(ScheduleInfo schedule) {
         if (Objects.equals(mSchedule, schedule)) return;
         mSchedule = schedule;
         updateDays();
     }
 
+    /**
+     * Sets next alarm of the schedule if the saved next alarm has passed or is further
+     * in the future than given nextAlarm
+     * @param now current time in milliseconds
+     * @param nextAlarm time of next alarm in milliseconds
+     */
     public void maybeSetNextAlarm(long now, long nextAlarm) {
         if (mSchedule != null && mSchedule.exitAtAlarm) {
             // alarm canceled
@@ -56,19 +77,26 @@
                     mSchedule.nextAlarm = Math.min(mSchedule.nextAlarm, nextAlarm);
                 }
             } else if (mSchedule.nextAlarm < now) {
-                if (ScheduleConditionProvider.DEBUG) {
-                    Log.d(ScheduleConditionProvider.TAG,
-                            "All alarms are in the past " + mSchedule.nextAlarm);
+                if (DEBUG) {
+                    Log.d(TAG, "All alarms are in the past " + mSchedule.nextAlarm);
                 }
                 mSchedule.nextAlarm = 0;
             }
         }
     }
 
+    /**
+     * Set calendar time zone to tz
+     * @param tz current time zone
+     */
     public void setTimeZone(TimeZone tz) {
         mCalendar.setTimeZone(tz);
     }
 
+    /**
+     * @param now current time in milliseconds
+     * @return next time this rule changes (starts or ends)
+     */
     public long getNextChangeTime(long now) {
         if (mSchedule == null) return 0;
         final long nextStart = getNextTime(now, mSchedule.startHour, mSchedule.startMinute);
@@ -92,6 +120,10 @@
         return mCalendar.getTimeInMillis();
     }
 
+    /**
+     * @param time milliseconds since Epoch
+     * @return true if time is within the schedule, else false
+     */
     public boolean isInSchedule(long time) {
         if (mSchedule == null || mDays.size() == 0) return false;
         final long start = getTime(time, mSchedule.startHour, mSchedule.startMinute);
@@ -102,6 +134,10 @@
         return isInSchedule(-1, time, start, end) || isInSchedule(0, time, start, end);
     }
 
+    /**
+     * @param time milliseconds since Epoch
+     * @return true if should exit at time for next alarm, else false
+     */
     public boolean shouldExitForAlarm(long time) {
         if (mSchedule == null) {
             return false;
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 1ec2406..f658ae0 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -46,8 +46,10 @@
 import java.util.Calendar;
 import java.util.Date;
 import java.util.GregorianCalendar;
+import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
+import java.util.TimeZone;
 import java.util.UUID;
 
 /**
@@ -64,11 +66,13 @@
     public static final int MAX_SOURCE = SOURCE_STAR;
     private static final int DEFAULT_SOURCE = SOURCE_CONTACT;
 
+    public static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
+    public static final String EVERY_NIGHT_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
+    public static final List<String> DEFAULT_RULE_IDS = Arrays.asList(EVERY_NIGHT_DEFAULT_RULE_ID,
+            EVENTS_DEFAULT_RULE_ID);
+
     public static final int[] ALL_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
             Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY };
-    public static final int[] WEEKNIGHT_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
-            Calendar.WEDNESDAY, Calendar.THURSDAY };
-    public static final int[] WEEKEND_DAYS = { Calendar.FRIDAY, Calendar.SATURDAY };
 
     public static final int[] MINUTE_BUCKETS = generateMinuteBuckets();
     private static final int SECONDS_MS = 1000;
@@ -529,6 +533,13 @@
         rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
         rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
         rt.condition = readConditionXml(parser);
+
+        // all default rules and user created rules updated to zenMode important interruptions
+        if (rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+                && Condition.isValidId(rt.conditionId, SYSTEM_AUTHORITY)) {
+            Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name);
+            rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        }
         return rt;
     }
 
@@ -692,6 +703,20 @@
                 suppressedVisualEffects);
     }
 
+    /**
+     * Creates scheduleCalendar from a condition id
+     * @param conditionId
+     * @return ScheduleCalendar with info populated with conditionId
+     */
+    public static ScheduleCalendar toScheduleCalendar(Uri conditionId) {
+        final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(conditionId);
+        if (schedule == null || schedule.days == null || schedule.days.length == 0) return null;
+        final ScheduleCalendar sc = new ScheduleCalendar();
+        sc.setSchedule(schedule);
+        sc.setTimeZone(TimeZone.getDefault());
+        return sc;
+    }
+
     private static int sourceToPrioritySenders(int source, int def) {
         switch (source) {
             case SOURCE_ANYONE: return Policy.PRIORITY_SENDERS_ANY;
@@ -793,7 +818,10 @@
                 Condition.FLAG_RELEVANT_NOW);
     }
 
-    private static CharSequence getFormattedTime(Context context, long time, boolean isSameDay,
+    /**
+     * Creates readable time from time in milliseconds
+     */
+    public static CharSequence getFormattedTime(Context context, long time, boolean isSameDay,
             int userHandle) {
         String skeleton = (!isSameDay ? "EEE " : "")
                 + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma");
@@ -801,7 +829,10 @@
         return DateFormat.format(pattern, time);
     }
 
-    private static boolean isToday(long time) {
+    /**
+     * Determines whether a time in milliseconds is today or not
+     */
+    public static boolean isToday(long time) {
         GregorianCalendar now = new GregorianCalendar();
         GregorianCalendar endTime = new GregorianCalendar();
         endTime.setTimeInMillis(time);
@@ -1081,7 +1112,10 @@
         return UUID.randomUUID().toString().replace("-", "");
     }
 
-    private static String getOwnerCaption(Context context, String owner) {
+    /**
+     * Gets the name of the app associated with owner
+     */
+    public static String getOwnerCaption(Context context, String owner) {
         final PackageManager pm = context.getPackageManager();
         try {
             final ApplicationInfo info = pm.getApplicationInfo(owner, 0);
diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl
index 7285fb4..f7acfc5 100644
--- a/core/java/android/service/vr/IVrManager.aidl
+++ b/core/java/android/service/vr/IVrManager.aidl
@@ -17,6 +17,7 @@
 package android.service.vr;
 
 import android.app.Vr2dDisplayProperties;
+import android.content.ComponentName;
 import android.service.vr.IVrStateCallbacks;
 import android.service.vr.IPersistentVrStateCallbacks;
 
@@ -109,5 +110,13 @@
      * @param standy True if the device is entering standby, false if it's exiting standby.
      */
     void setStandbyEnabled(boolean standby);
+
+    /**
+     * Start VR Input method for the given packageName in {@param componentName}.
+     * This method notifies InputMethodManagerService to use VR IME instead of
+     * regular phone IME.
+     */
+    void setVrInputMethod(in ComponentName componentName);
+
 }
 
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index dd0ae33..e5ab3e1 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -42,6 +42,7 @@
 import android.util.Log;
 import android.util.MergedConfiguration;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.Gravity;
 import android.view.IWindowSession;
 import android.view.InputChannel;
@@ -176,6 +177,9 @@
         final Rect mFinalSystemInsets = new Rect();
         final Rect mFinalStableInsets = new Rect();
         final Rect mBackdropFrame = new Rect();
+        final DisplayCutout.ParcelableWrapper mDisplayCutout =
+                new DisplayCutout.ParcelableWrapper();
+        DisplayCutout mDispatchedDisplayCutout = DisplayCutout.NO_CUTOUT;
         final MergedConfiguration mMergedConfiguration = new MergedConfiguration();
 
         final WindowManager.LayoutParams mLayout
@@ -302,7 +306,8 @@
             public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
                     Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
                     MergedConfiguration mergedConfiguration, Rect backDropRect, boolean forceLayout,
-                    boolean alwaysConsumeNavBar, int displayId) {
+                    boolean alwaysConsumeNavBar, int displayId,
+                    DisplayCutout.ParcelableWrapper displayCutout) {
                 Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED,
                         reportDraw ? 1 : 0, outsets);
                 mCaller.sendMessage(msg);
@@ -750,7 +755,7 @@
                         mInputChannel = new InputChannel();
                         if (mSession.addToDisplay(mWindow, mWindow.mSeq, mLayout, View.VISIBLE,
                             Display.DEFAULT_DISPLAY, mContentInsets, mStableInsets, mOutsets,
-                                mInputChannel) < 0) {
+                                mDisplayCutout, mInputChannel) < 0) {
                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                             return;
                         }
@@ -776,7 +781,7 @@
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
                             mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
-                            mMergedConfiguration, mSurfaceHolder.mSurface);
+                            mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface);
 
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
                             + ", frame=" + mWinFrame);
@@ -800,6 +805,8 @@
                         mStableInsets.top += padding.top;
                         mStableInsets.right += padding.right;
                         mStableInsets.bottom += padding.bottom;
+                        mDisplayCutout.set(mDisplayCutout.get().inset(-padding.left, -padding.top,
+                                -padding.right, -padding.bottom));
                     }
 
                     if (mCurWidth != w) {
@@ -819,6 +826,7 @@
                     insetsChanged |= !mDispatchedContentInsets.equals(mContentInsets);
                     insetsChanged |= !mDispatchedStableInsets.equals(mStableInsets);
                     insetsChanged |= !mDispatchedOutsets.equals(mOutsets);
+                    insetsChanged |= !mDispatchedDisplayCutout.equals(mDisplayCutout.get());
 
                     mSurfaceHolder.setSurfaceFrameSize(w, h);
                     mSurfaceHolder.mSurfaceLock.unlock();
@@ -885,12 +893,13 @@
                             mDispatchedContentInsets.set(mContentInsets);
                             mDispatchedStableInsets.set(mStableInsets);
                             mDispatchedOutsets.set(mOutsets);
+                            mDispatchedDisplayCutout = mDisplayCutout.get();
                             mFinalSystemInsets.set(mDispatchedOverscanInsets);
                             mFinalStableInsets.set(mDispatchedStableInsets);
                             WindowInsets insets = new WindowInsets(mFinalSystemInsets,
                                     null, mFinalStableInsets,
                                     getResources().getConfiguration().isScreenRound(), false,
-                                    null /* displayCutout */);
+                                    mDispatchedDisplayCutout);
                             if (DEBUG) {
                                 Log.v(TAG, "dispatching insets=" + insets);
                             }
diff --git a/core/java/android/text/AutoGrowArray.java b/core/java/android/text/AutoGrowArray.java
new file mode 100644
index 0000000..e428377
--- /dev/null
+++ b/core/java/android/text/AutoGrowArray.java
@@ -0,0 +1,374 @@
+/*
+ * 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.text;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+
+import com.android.internal.util.ArrayUtils;
+
+import libcore.util.EmptyArray;
+
+/**
+ * Implements a growing array of int primitives.
+ *
+ * These arrays are NOT thread safe.
+ *
+ * @hide
+ */
+public final class AutoGrowArray {
+    private static final int MIN_CAPACITY_INCREMENT = 12;
+    private static final int MAX_CAPACITY_TO_BE_KEPT = 10000;
+
+    /**
+     * Returns next capacity size.
+     *
+     * The returned capacity is larger than requested capacity.
+     */
+    private static int computeNewCapacity(int currentSize, int requested) {
+        final int targetCapacity = currentSize + (currentSize < (MIN_CAPACITY_INCREMENT / 2)
+                ?  MIN_CAPACITY_INCREMENT : currentSize >> 1);
+        return targetCapacity > requested ? targetCapacity : requested;
+    }
+
+    /**
+     * An auto growing byte array.
+     */
+    public static class ByteArray {
+
+        private @NonNull byte[] mValues;
+        private @IntRange(from = 0) int mSize;
+
+        /**
+         * Creates an empty ByteArray with the default initial capacity.
+         */
+        public ByteArray() {
+            this(10);
+        }
+
+        /**
+         * Creates an empty ByteArray with the specified initial capacity.
+         */
+        public ByteArray(@IntRange(from = 0) int initialCapacity) {
+            if (initialCapacity == 0) {
+                mValues = EmptyArray.BYTE;
+            } else {
+                mValues = ArrayUtils.newUnpaddedByteArray(initialCapacity);
+            }
+            mSize = 0;
+        }
+
+        /**
+         * Changes the size of this ByteArray. If this ByteArray is shrinked, the backing array
+         * capacity is unchanged.
+         */
+        public void resize(@IntRange(from = 0) int newSize) {
+            if (newSize > mValues.length) {
+                ensureCapacity(newSize - mSize);
+            }
+            mSize = newSize;
+        }
+
+        /**
+         * Appends the specified value to the end of this array.
+         */
+        public void append(byte value) {
+            ensureCapacity(1);
+            mValues[mSize++] = value;
+        }
+
+        /**
+         * Ensures capacity to append at least <code>count</code> values.
+         */
+        private void ensureCapacity(@IntRange int count) {
+            final int requestedSize = mSize + count;
+            if (requestedSize >= mValues.length) {
+                final int newCapacity = computeNewCapacity(mSize, requestedSize);
+                final byte[] newValues = ArrayUtils.newUnpaddedByteArray(newCapacity);
+                System.arraycopy(mValues, 0, newValues, 0, mSize);
+                mValues = newValues;
+            }
+        }
+
+        /**
+         * Removes all values from this array.
+         */
+        public void clear() {
+            mSize = 0;
+        }
+
+        /**
+         * Removes all values from this array and release the internal array object if it is too
+         * large.
+         */
+        public void clearWithReleasingLargeArray() {
+            clear();
+            if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) {
+                mValues = EmptyArray.BYTE;
+            }
+        }
+
+        /**
+         * Returns the value at the specified position in this array.
+         */
+        public byte get(@IntRange(from = 0) int index) {
+            return mValues[index];
+        }
+
+        /**
+         * Sets the value at the specified position in this array.
+         */
+        public void set(@IntRange(from = 0) int index, byte value) {
+            mValues[index] = value;
+        }
+
+        /**
+         * Returns the number of values in this array.
+         */
+        public @IntRange(from = 0) int size() {
+            return mSize;
+        }
+
+        /**
+         * Returns internal raw array.
+         *
+         * Note that this array may have larger size than you requested.
+         * Use size() instead for getting the actual array size.
+         */
+        public @NonNull byte[] getRawArray() {
+            return mValues;
+        }
+    }
+
+    /**
+     * An auto growing int array.
+     */
+    public static class IntArray {
+
+        private @NonNull int[] mValues;
+        private @IntRange(from = 0) int mSize;
+
+        /**
+         * Creates an empty IntArray with the default initial capacity.
+         */
+        public IntArray() {
+            this(10);
+        }
+
+        /**
+         * Creates an empty IntArray with the specified initial capacity.
+         */
+        public IntArray(@IntRange(from = 0) int initialCapacity) {
+            if (initialCapacity == 0) {
+                mValues = EmptyArray.INT;
+            } else {
+                mValues = ArrayUtils.newUnpaddedIntArray(initialCapacity);
+            }
+            mSize = 0;
+        }
+
+        /**
+         * Changes the size of this IntArray. If this IntArray is shrinked, the backing array
+         * capacity is unchanged.
+         */
+        public void resize(@IntRange(from = 0) int newSize) {
+            if (newSize > mValues.length) {
+                ensureCapacity(newSize - mSize);
+            }
+            mSize = newSize;
+        }
+
+        /**
+         * Appends the specified value to the end of this array.
+         */
+        public void append(int value) {
+            ensureCapacity(1);
+            mValues[mSize++] = value;
+        }
+
+        /**
+         * Ensures capacity to append at least <code>count</code> values.
+         */
+        private void ensureCapacity(@IntRange(from = 0) int count) {
+            final int requestedSize = mSize + count;
+            if (requestedSize >= mValues.length) {
+                final int newCapacity = computeNewCapacity(mSize, requestedSize);
+                final int[] newValues = ArrayUtils.newUnpaddedIntArray(newCapacity);
+                System.arraycopy(mValues, 0, newValues, 0, mSize);
+                mValues = newValues;
+            }
+        }
+
+        /**
+         * Removes all values from this array.
+         */
+        public void clear() {
+            mSize = 0;
+        }
+
+        /**
+         * Removes all values from this array and release the internal array object if it is too
+         * large.
+         */
+        public void clearWithReleasingLargeArray() {
+            clear();
+            if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) {
+                mValues = EmptyArray.INT;
+            }
+        }
+
+        /**
+         * Returns the value at the specified position in this array.
+         */
+        public int get(@IntRange(from = 0) int index) {
+            return mValues[index];
+        }
+
+        /**
+         * Sets the value at the specified position in this array.
+         */
+        public void set(@IntRange(from = 0) int index, int value) {
+            mValues[index] = value;
+        }
+
+        /**
+         * Returns the number of values in this array.
+         */
+        public @IntRange(from = 0) int size() {
+            return mSize;
+        }
+
+        /**
+         * Returns internal raw array.
+         *
+         * Note that this array may have larger size than you requested.
+         * Use size() instead for getting the actual array size.
+         */
+        public @NonNull int[] getRawArray() {
+            return mValues;
+        }
+    }
+
+    /**
+     * An auto growing float array.
+     */
+    public static class FloatArray {
+
+        private @NonNull float[] mValues;
+        private @IntRange(from = 0) int mSize;
+
+        /**
+         * Creates an empty FloatArray with the default initial capacity.
+         */
+        public FloatArray() {
+            this(10);
+        }
+
+        /**
+         * Creates an empty FloatArray with the specified initial capacity.
+         */
+        public FloatArray(@IntRange(from = 0) int initialCapacity) {
+            if (initialCapacity == 0) {
+                mValues = EmptyArray.FLOAT;
+            } else {
+                mValues = ArrayUtils.newUnpaddedFloatArray(initialCapacity);
+            }
+            mSize = 0;
+        }
+
+        /**
+         * Changes the size of this FloatArray. If this FloatArray is shrinked, the backing array
+         * capacity is unchanged.
+         */
+        public void resize(@IntRange(from = 0) int newSize) {
+            if (newSize > mValues.length) {
+                ensureCapacity(newSize - mSize);
+            }
+            mSize = newSize;
+        }
+
+        /**
+         * Appends the specified value to the end of this array.
+         */
+        public void append(float value) {
+            ensureCapacity(1);
+            mValues[mSize++] = value;
+        }
+
+        /**
+         * Ensures capacity to append at least <code>count</code> values.
+         */
+        private void ensureCapacity(int count) {
+            final int requestedSize = mSize + count;
+            if (requestedSize >= mValues.length) {
+                final int newCapacity = computeNewCapacity(mSize, requestedSize);
+                final float[] newValues = ArrayUtils.newUnpaddedFloatArray(newCapacity);
+                System.arraycopy(mValues, 0, newValues, 0, mSize);
+                mValues = newValues;
+            }
+        }
+
+        /**
+         * Removes all values from this array.
+         */
+        public void clear() {
+            mSize = 0;
+        }
+
+        /**
+         * Removes all values from this array and release the internal array object if it is too
+         * large.
+         */
+        public void clearWithReleasingLargeArray() {
+            clear();
+            if (mValues.length > MAX_CAPACITY_TO_BE_KEPT) {
+                mValues = EmptyArray.FLOAT;
+            }
+        }
+
+        /**
+         * Returns the value at the specified position in this array.
+         */
+        public float get(@IntRange(from = 0) int index) {
+            return mValues[index];
+        }
+
+        /**
+         * Sets the value at the specified position in this array.
+         */
+        public void set(@IntRange(from = 0) int index, float value) {
+            mValues[index] = value;
+        }
+
+        /**
+         * Returns the number of values in this array.
+         */
+        public @IntRange(from = 0) int size() {
+            return mSize;
+        }
+
+        /**
+         * Returns internal raw array.
+         *
+         * Note that this array may have larger size than you requested.
+         * Use size() instead for getting the actual array size.
+         */
+        public @NonNull float[] getRawArray() {
+            return mValues;
+        }
+    }
+}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 4d2a962..2a693a1 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1907,22 +1907,14 @@
 
     private static float measurePara(TextPaint paint, CharSequence text, int start, int end,
             TextDirectionHeuristic textDir) {
-        MeasuredText mt = MeasuredText.obtain();
+        MeasuredText mt = null;
         TextLine tl = TextLine.obtain();
         try {
-            mt.setPara(text, start, end, textDir);
-            Directions directions;
-            int dir;
-            if (mt.mEasy) {
-                directions = DIRS_ALL_LEFT_TO_RIGHT;
-                dir = Layout.DIR_LEFT_TO_RIGHT;
-            } else {
-                directions = AndroidBidi.directions(mt.mDir, mt.mLevels,
-                    0, mt.mChars, 0, mt.mLen);
-                dir = mt.mDir;
-            }
-            char[] chars = mt.mChars;
-            int len = mt.mLen;
+            mt = MeasuredText.buildForBidi(text, start, end, textDir, mt);
+            final char[] chars = mt.getChars();
+            final int len = chars.length;
+            final Directions directions = mt.getDirections(0, len);
+            final int dir = mt.getParagraphDir();
             boolean hasTabs = false;
             TabStops tabStops = null;
             // leading margins should be taken into account when measuring a paragraph
@@ -1955,7 +1947,9 @@
             return margin + Math.abs(tl.metrics(null));
         } finally {
             TextLine.recycle(tl);
-            MeasuredText.recycle(mt);
+            if (mt != null) {
+                mt.recycle();
+            }
         }
     }
 
@@ -2272,6 +2266,11 @@
     private SpanSet<LineBackgroundSpan> mLineBackgroundSpans;
     private int mJustificationMode;
 
+    /** @hide */
+    @IntDef({DIR_LEFT_TO_RIGHT, DIR_RIGHT_TO_LEFT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Direction {}
+
     public static final int DIR_LEFT_TO_RIGHT = 1;
     public static final int DIR_RIGHT_TO_LEFT = -1;
 
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index 3d9fba7..14d6f9e 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -16,125 +16,436 @@
 
 package android.text;
 
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.graphics.Paint;
+import android.text.AutoGrowArray.ByteArray;
+import android.text.AutoGrowArray.FloatArray;
+import android.text.AutoGrowArray.IntArray;
+import android.text.Layout.Directions;
 import android.text.style.MetricAffectingSpan;
 import android.text.style.ReplacementSpan;
-import android.util.Log;
+import android.util.Pools.SynchronizedPool;
 
-import com.android.internal.util.ArrayUtils;
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.util.Arrays;
 
 /**
+ * MeasuredText provides text information for rendering purpose.
+ *
+ * The first motivation of this class is identify the text directions and retrieving individual
+ * character widths. However retrieving character widths is slower than identifying text directions.
+ * Thus, this class provides several builder methods for specific purposes.
+ *
+ * - buildForBidi:
+ *   Compute only text directions.
+ * - buildForMeasurement:
+ *   Compute text direction and all character widths.
+ * - buildForStaticLayout:
+ *   This is bit special. StaticLayout also needs to know text direction and character widths for
+ *   line breaking, but all things are done in native code. Similarly, text measurement is done
+ *   in native code. So instead of storing result to Java array, this keeps the result in native
+ *   code since there is no good reason to move the results to Java layer.
+ *
+ * In addition to the character widths, some additional information is computed for each purposes,
+ * e.g. whole text length for measurement or font metrics for static layout.
+ *
+ * MeasuredText is NOT a thread safe object.
  * @hide
  */
-class MeasuredText {
-    private static final boolean localLOGV = false;
-    CharSequence mText;
-    int mTextStart;
-    float[] mWidths;
-    char[] mChars;
-    byte[] mLevels;
-    int mDir;
-    boolean mEasy;
-    int mLen;
+public class MeasuredText {
+    private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC';
 
-    private int mPos;
-    private TextPaint mWorkPaint;
+    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+            MeasuredText.class.getClassLoader(), nGetReleaseFunc(), 1024);
 
-    private MeasuredText() {
-        mWorkPaint = new TextPaint();
+    private MeasuredText() {}  // Use build static functions instead.
+
+    private static final SynchronizedPool<MeasuredText> sPool = new SynchronizedPool<>(1);
+
+    private static @NonNull MeasuredText obtain() { // Use build static functions instead.
+        final MeasuredText mt = sPool.acquire();
+        return mt != null ? mt : new MeasuredText();
     }
 
-    private static final Object[] sLock = new Object[0];
-    private static final MeasuredText[] sCached = new MeasuredText[3];
+    /**
+     * Recycle the MeasuredText.
+     *
+     * Do not call any methods after you call this method.
+     */
+    public void recycle() {
+        release();
+        sPool.release(this);
+    }
 
-    static MeasuredText obtain() {
-        MeasuredText mt;
-        synchronized (sLock) {
-            for (int i = sCached.length; --i >= 0;) {
-                if (sCached[i] != null) {
-                    mt = sCached[i];
-                    sCached[i] = null;
-                    return mt;
-                }
-            }
+    // The casted original text.
+    //
+    // This may be null if the passed text is not a Spanned.
+    private @Nullable Spanned mSpanned;
+
+    // The start offset of the target range in the original text (mSpanned);
+    private @IntRange(from = 0) int mTextStart;
+
+    // The length of the target range in the original text.
+    private @IntRange(from = 0) int mTextLength;
+
+    // The copied character buffer for measuring text.
+    //
+    // The length of this array is mTextLength.
+    private @Nullable char[] mCopiedBuffer;
+
+    // The whole paragraph direction.
+    private @Layout.Direction int mParaDir;
+
+    // True if the text is LTR direction and doesn't contain any bidi characters.
+    private boolean mLtrWithoutBidi;
+
+    // The bidi level for individual characters.
+    //
+    // This is empty if mLtrWithoutBidi is true.
+    private @NonNull ByteArray mLevels = new ByteArray();
+
+    // The whole width of the text.
+    // See getWholeWidth comments.
+    private @FloatRange(from = 0.0f) float mWholeWidth;
+
+    // Individual characters' widths.
+    // See getWidths comments.
+    private @Nullable FloatArray mWidths = new FloatArray();
+
+    // The span end positions.
+    // See getSpanEndCache comments.
+    private @Nullable IntArray mSpanEndCache = new IntArray(4);
+
+    // The font metrics.
+    // See getFontMetrics comments.
+    private @Nullable IntArray mFontMetrics = new IntArray(4 * 4);
+
+    // The native MeasuredText.
+    // See getNativePtr comments.
+    // Do not modify these members directly. Use bindNativeObject/unbindNativeObject instead.
+    private /* Maybe Zero */ long mNativePtr = 0;
+    private @Nullable Runnable mNativeObjectCleaner;
+
+    // Associate the native object to this Java object.
+    private void bindNativeObject(/* Non Zero*/ long nativePtr) {
+        mNativePtr = nativePtr;
+        mNativeObjectCleaner = sRegistry.registerNativeAllocation(this, nativePtr);
+    }
+
+    // Decouple the native object from this Java object and release the native object.
+    private void unbindNativeObject() {
+        if (mNativePtr != 0) {
+            mNativeObjectCleaner.run();
+            mNativePtr = 0;
         }
-        mt = new MeasuredText();
-        if (localLOGV) {
-            Log.v("MEAS", "new: " + mt);
+    }
+
+    // Following two objects are for avoiding object allocation.
+    private @NonNull TextPaint mCachedPaint = new TextPaint();
+    private @Nullable Paint.FontMetricsInt mCachedFm;
+
+    /**
+     * Releases internal buffers.
+     */
+    public void release() {
+        reset();
+        mLevels.clearWithReleasingLargeArray();
+        mWidths.clearWithReleasingLargeArray();
+        mFontMetrics.clearWithReleasingLargeArray();
+        mSpanEndCache.clearWithReleasingLargeArray();
+    }
+
+    /**
+     * Resets the internal state for starting new text.
+     */
+    private void reset() {
+        mSpanned = null;
+        mCopiedBuffer = null;
+        mWholeWidth = 0;
+        mLevels.clear();
+        mWidths.clear();
+        mFontMetrics.clear();
+        mSpanEndCache.clear();
+        unbindNativeObject();
+    }
+
+    /**
+     * Returns the characters to be measured.
+     *
+     * This is always available.
+     */
+    public @NonNull char[] getChars() {
+        return mCopiedBuffer;
+    }
+
+    /**
+     * Returns the paragraph direction.
+     *
+     * This is always available.
+     */
+    public @Layout.Direction int getParagraphDir() {
+        return mParaDir;
+    }
+
+    /**
+     * Returns the directions.
+     *
+     * This is always available.
+     */
+    public Directions getDirections(@IntRange(from = 0) int start,  // inclusive
+                                    @IntRange(from = 0) int end) {  // exclusive
+        if (mLtrWithoutBidi) {
+            return Layout.DIRS_ALL_LEFT_TO_RIGHT;
+        }
+
+        final int length = end - start;
+        return AndroidBidi.directions(mParaDir, mLevels.getRawArray(), start, mCopiedBuffer, start,
+                length);
+    }
+
+    /**
+     * Returns the whole text width.
+     *
+     * This is available only if the MeasureText is computed with computeForMeasurement.
+     * Returns 0 in other cases.
+     */
+    public @FloatRange(from = 0.0f) float getWholeWidth() {
+        return mWholeWidth;
+    }
+
+    /**
+     * Returns the individual character's width.
+     *
+     * This is available only if the MeasureText is computed with computeForMeasurement.
+     * Returns empty array in other cases.
+     */
+    public @NonNull FloatArray getWidths() {
+        return mWidths;
+    }
+
+    /**
+     * Returns the MetricsAffectingSpan end indices.
+     *
+     * If the input text is not a spanned string, this has one value that is the length of the text.
+     *
+     * This is available only if the MeasureText is computed with computeForStaticLayout.
+     * Returns empty array in other cases.
+     */
+    public @NonNull IntArray getSpanEndCache() {
+        return mSpanEndCache;
+    }
+
+    /**
+     * Returns the int array which holds FontMetrics.
+     *
+     * This array holds the repeat of top, bottom, ascent, descent of font metrics value.
+     *
+     * This is available only if the MeasureText is computed with computeForStaticLayout.
+     * Returns empty array in other cases.
+     */
+    public @NonNull IntArray getFontMetrics() {
+        return mFontMetrics;
+    }
+
+    /**
+     * Returns the native ptr of the MeasuredText.
+     *
+     * This is available only if the MeasureText is computed with computeForStaticLayout.
+     * Returns 0 in other cases.
+     */
+    public /* Maybe Zero */ long getNativePtr() {
+        return mNativePtr;
+    }
+
+    /**
+     * Generates new MeasuredText for Bidi computation.
+     *
+     * If recycle is null, this returns new instance. If recycle is not null, this fills computed
+     * result to recycle and returns recycle.
+     *
+     * @param text the character sequence to be measured
+     * @param start the inclusive start offset of the target region in the text
+     * @param end the exclusive end offset of the target region in the text
+     * @param textDir the text direction
+     * @param recycle pass existing MeasuredText if you want to recycle it.
+     *
+     * @return measured text
+     */
+    public static @NonNull MeasuredText buildForBidi(@NonNull CharSequence text,
+                                                     @IntRange(from = 0) int start,
+                                                     @IntRange(from = 0) int end,
+                                                     @NonNull TextDirectionHeuristic textDir,
+                                                     @Nullable MeasuredText recycle) {
+        final MeasuredText mt = recycle == null ? obtain() : recycle;
+        mt.resetAndAnalyzeBidi(text, start, end, textDir);
+        return mt;
+    }
+
+    /**
+     * Generates new MeasuredText for measuring texts.
+     *
+     * If recycle is null, this returns new instance. If recycle is not null, this fills computed
+     * result to recycle and returns recycle.
+     *
+     * @param paint the paint to be used for rendering the text.
+     * @param text the character sequence to be measured
+     * @param start the inclusive start offset of the target region in the text
+     * @param end the exclusive end offset of the target region in the text
+     * @param textDir the text direction
+     * @param recycle pass existing MeasuredText if you want to recycle it.
+     *
+     * @return measured text
+     */
+    public static @NonNull MeasuredText buildForMeasurement(@NonNull TextPaint paint,
+                                                            @NonNull CharSequence text,
+                                                            @IntRange(from = 0) int start,
+                                                            @IntRange(from = 0) int end,
+                                                            @NonNull TextDirectionHeuristic textDir,
+                                                            @Nullable MeasuredText recycle) {
+        final MeasuredText mt = recycle == null ? obtain() : recycle;
+        mt.resetAndAnalyzeBidi(text, start, end, textDir);
+
+        mt.mWidths.resize(mt.mTextLength);
+        if (mt.mTextLength == 0) {
+            return mt;
+        }
+
+        if (mt.mSpanned == null) {
+            // No style change by MetricsAffectingSpan. Just measure all text.
+            mt.applyMetricsAffectingSpan(
+                    paint, null /* spans */, start, end, 0 /* native static layout ptr */);
+        } else {
+            // There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
+            int spanEnd;
+            for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
+                spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end, MetricAffectingSpan.class);
+                MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
+                        MetricAffectingSpan.class);
+                spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
+                mt.applyMetricsAffectingSpan(
+                        paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */);
+            }
         }
         return mt;
     }
 
-    static MeasuredText recycle(MeasuredText mt) {
-        mt.finish();
-        synchronized(sLock) {
-            for (int i = 0; i < sCached.length; ++i) {
-                if (sCached[i] == null) {
-                    sCached[i] = mt;
-                    mt.mText = null;
-                    break;
+    /**
+     * Generates new MeasuredText for StaticLayout.
+     *
+     * If recycle is null, this returns new instance. If recycle is not null, this fills computed
+     * result to recycle and returns recycle.
+     *
+     * @param paint the paint to be used for rendering the text.
+     * @param text the character sequence to be measured
+     * @param start the inclusive start offset of the target region in the text
+     * @param end the exclusive end offset of the target region in the text
+     * @param textDir the text direction
+     * @param recycle pass existing MeasuredText if you want to recycle it.
+     *
+     * @return measured text
+     */
+    public static @NonNull MeasuredText buildForStaticLayout(
+            @NonNull TextPaint paint,
+            @NonNull CharSequence text,
+            @IntRange(from = 0) int start,
+            @IntRange(from = 0) int end,
+            @NonNull TextDirectionHeuristic textDir,
+            @Nullable MeasuredText recycle) {
+        final MeasuredText mt = recycle == null ? obtain() : recycle;
+        mt.resetAndAnalyzeBidi(text, start, end, textDir);
+        if (mt.mTextLength == 0) {
+            // Need to build empty native measured text for StaticLayout.
+            // TODO: Stop creating empty measured text for empty lines.
+            long nativeBuilderPtr = nInitBuilder();
+            try {
+                mt.bindNativeObject(nBuildNativeMeasuredText(nativeBuilderPtr, mt.mCopiedBuffer));
+            } finally {
+                nFreeBuilder(nativeBuilderPtr);
+            }
+            return mt;
+        }
+
+        long nativeBuilderPtr = nInitBuilder();
+        try {
+            if (mt.mSpanned == null) {
+                // No style change by MetricsAffectingSpan. Just measure all text.
+                mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, nativeBuilderPtr);
+                mt.mSpanEndCache.append(end);
+            } else {
+                // There may be a MetricsAffectingSpan. Split into span transitions and apply
+                // styles.
+                int spanEnd;
+                for (int spanStart = start; spanStart < end; spanStart = spanEnd) {
+                    spanEnd = mt.mSpanned.nextSpanTransition(spanStart, end,
+                                                             MetricAffectingSpan.class);
+                    MetricAffectingSpan[] spans = mt.mSpanned.getSpans(spanStart, spanEnd,
+                            MetricAffectingSpan.class);
+                    spans = TextUtils.removeEmptySpans(spans, mt.mSpanned,
+                                                       MetricAffectingSpan.class);
+                    mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd,
+                                                 nativeBuilderPtr);
+                    mt.mSpanEndCache.append(spanEnd);
                 }
             }
+            mt.bindNativeObject(nBuildNativeMeasuredText(nativeBuilderPtr, mt.mCopiedBuffer));
+        } finally {
+            nFreeBuilder(nativeBuilderPtr);
         }
-        return null;
-    }
 
-    void finish() {
-        mText = null;
-        if (mLen > 1000) {
-            mWidths = null;
-            mChars = null;
-            mLevels = null;
-        }
+        return mt;
     }
 
     /**
-     * Analyzes text for bidirectional runs.  Allocates working buffers.
+     * Reset internal state and analyzes text for bidirectional runs.
+     *
+     * @param text the character sequence to be measured
+     * @param start the inclusive start offset of the target region in the text
+     * @param end the exclusive end offset of the target region in the text
+     * @param textDir the text direction
      */
-    void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir) {
-        mText = text;
+    private void resetAndAnalyzeBidi(@NonNull CharSequence text,
+                                     @IntRange(from = 0) int start,  // inclusive
+                                     @IntRange(from = 0) int end,  // exclusive
+                                     @NonNull TextDirectionHeuristic textDir) {
+        reset();
+        mSpanned = text instanceof Spanned ? (Spanned) text : null;
         mTextStart = start;
+        mTextLength = end - start;
 
-        int len = end - start;
-        mLen = len;
-        mPos = 0;
-
-        if (mWidths == null || mWidths.length < len) {
-            mWidths = ArrayUtils.newUnpaddedFloatArray(len);
+        if (mCopiedBuffer == null || mCopiedBuffer.length != mTextLength) {
+            mCopiedBuffer = new char[mTextLength];
         }
-        if (mChars == null || mChars.length != len) {
-            mChars = new char[len];
-        }
-        TextUtils.getChars(text, start, end, mChars, 0);
+        TextUtils.getChars(text, start, end, mCopiedBuffer, 0);
 
-        if (text instanceof Spanned) {
-            Spanned spanned = (Spanned) text;
-            ReplacementSpan[] spans = spanned.getSpans(start, end,
-                    ReplacementSpan.class);
+        // Replace characters associated with ReplacementSpan to U+FFFC.
+        if (mSpanned != null) {
+            ReplacementSpan[] spans = mSpanned.getSpans(start, end, ReplacementSpan.class);
 
             for (int i = 0; i < spans.length; i++) {
-                int startInPara = spanned.getSpanStart(spans[i]) - start;
-                int endInPara = spanned.getSpanEnd(spans[i]) - start;
-                // The span interval may be larger and must be restricted to [start, end[
+                int startInPara = mSpanned.getSpanStart(spans[i]) - start;
+                int endInPara = mSpanned.getSpanEnd(spans[i]) - start;
+                // The span interval may be larger and must be restricted to [start, end)
                 if (startInPara < 0) startInPara = 0;
-                if (endInPara > len) endInPara = len;
-                for (int j = startInPara; j < endInPara; j++) {
-                    mChars[j] = '\uFFFC'; // object replacement character
-                }
+                if (endInPara > mTextLength) endInPara = mTextLength;
+                Arrays.fill(mCopiedBuffer, startInPara, endInPara, OBJECT_REPLACEMENT_CHARACTER);
             }
         }
 
         if ((textDir == TextDirectionHeuristics.LTR ||
                 textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR ||
                 textDir == TextDirectionHeuristics.ANYRTL_LTR) &&
-                TextUtils.doesNotNeedBidi(mChars, 0, len)) {
-            mDir = Layout.DIR_LEFT_TO_RIGHT;
-            mEasy = true;
+                TextUtils.doesNotNeedBidi(mCopiedBuffer, 0, mTextLength)) {
+            mLevels.clear();
+            mParaDir = Layout.DIR_LEFT_TO_RIGHT;
+            mLtrWithoutBidi = true;
         } else {
-            if (mLevels == null || mLevels.length < len) {
-                mLevels = ArrayUtils.newUnpaddedByteArray(len);
-            }
-            int bidiRequest;
+            final int bidiRequest;
             if (textDir == TextDirectionHeuristics.LTR) {
                 bidiRequest = Layout.DIR_REQUEST_LTR;
             } else if (textDir == TextDirectionHeuristics.RTL) {
@@ -144,122 +455,147 @@
             } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
                 bidiRequest = Layout.DIR_REQUEST_DEFAULT_RTL;
             } else {
-                boolean isRtl = textDir.isRtl(mChars, 0, len);
+                final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength);
                 bidiRequest = isRtl ? Layout.DIR_REQUEST_RTL : Layout.DIR_REQUEST_LTR;
             }
-            mDir = AndroidBidi.bidi(bidiRequest, mChars, mLevels);
-            mEasy = false;
+            mLevels.resize(mTextLength);
+            mParaDir = AndroidBidi.bidi(bidiRequest, mCopiedBuffer, mLevels.getRawArray());
+            mLtrWithoutBidi = false;
+        }
+    }
+
+    private void applyReplacementRun(@NonNull ReplacementSpan replacement,
+                                     @IntRange(from = 0) int start,  // inclusive, in copied buffer
+                                     @IntRange(from = 0) int end,  // exclusive, in copied buffer
+                                     /* Maybe Zero */ long nativeBuilderPtr) {
+        // Use original text. Shouldn't matter.
+        // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
+        //       backward compatibility? or Should we initialize them for getFontMetricsInt?
+        final float width = replacement.getSize(
+                mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm);
+        if (nativeBuilderPtr == 0) {
+            // Assigns all width to the first character. This is the same behavior as minikin.
+            mWidths.set(start, width);
+            if (end > start + 1) {
+                Arrays.fill(mWidths.getRawArray(), start + 1, end, 0.0f);
+            }
+            mWholeWidth += width;
+        } else {
+            nAddReplacementRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
+                               width);
+        }
+    }
+
+    private void applyStyleRun(@IntRange(from = 0) int start,  // inclusive, in copied buffer
+                               @IntRange(from = 0) int end,  // exclusive, in copied buffer
+                               /* Maybe Zero */ long nativeBuilderPtr) {
+        if (nativeBuilderPtr != 0) {
+            mCachedPaint.getFontMetricsInt(mCachedFm);
+        }
+
+        if (mLtrWithoutBidi) {
+            // If the whole text is LTR direction, just apply whole region.
+            if (nativeBuilderPtr == 0) {
+                mWholeWidth += mCachedPaint.getTextRunAdvances(
+                        mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
+                        mWidths.getRawArray(), start);
+            } else {
+                nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
+                        false /* isRtl */);
+            }
+        } else {
+            // If there is multiple bidi levels, split into individual bidi level and apply style.
+            byte level = mLevels.get(start);
+            // Note that the empty text or empty range won't reach this method.
+            // Safe to search from start + 1.
+            for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) {
+                if (levelEnd == end || mLevels.get(levelEnd) != level) {  // transition point
+                    final boolean isRtl = (level & 0x1) != 0;
+                    if (nativeBuilderPtr == 0) {
+                        final int levelLength = levelEnd - levelStart;
+                        mWholeWidth += mCachedPaint.getTextRunAdvances(
+                                mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
+                                isRtl, mWidths.getRawArray(), levelStart);
+                    } else {
+                        nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), levelStart,
+                                levelEnd, isRtl);
+                    }
+                    if (levelEnd == end) {
+                        break;
+                    }
+                    levelStart = levelEnd;
+                    level = mLevels.get(levelEnd);
+                }
+            }
+        }
+    }
+
+    private void applyMetricsAffectingSpan(
+            @NonNull TextPaint paint,
+            @Nullable MetricAffectingSpan[] spans,
+            @IntRange(from = 0) int start,  // inclusive, in original text buffer
+            @IntRange(from = 0) int end,  // exclusive, in original text buffer
+            /* Maybe Zero */ long nativeBuilderPtr) {
+        mCachedPaint.set(paint);
+        // XXX paint should not have a baseline shift, but...
+        mCachedPaint.baselineShift = 0;
+
+        final boolean needFontMetrics = nativeBuilderPtr != 0;
+
+        if (needFontMetrics && mCachedFm == null) {
+            mCachedFm = new Paint.FontMetricsInt();
+        }
+
+        ReplacementSpan replacement = null;
+        if (spans != null) {
+            for (int i = 0; i < spans.length; i++) {
+                MetricAffectingSpan span = spans[i];
+                if (span instanceof ReplacementSpan) {
+                    // The last ReplacementSpan is effective for backward compatibility reasons.
+                    replacement = (ReplacementSpan) span;
+                } else {
+                    // TODO: No need to call updateMeasureState for ReplacementSpan as well?
+                    span.updateMeasureState(mCachedPaint);
+                }
+            }
+        }
+
+        final int startInCopiedBuffer = start - mTextStart;
+        final int endInCopiedBuffer = end - mTextStart;
+
+        if (replacement != null) {
+            applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer,
+                                nativeBuilderPtr);
+        } else {
+            applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeBuilderPtr);
+        }
+
+        if (needFontMetrics) {
+            if (mCachedPaint.baselineShift < 0) {
+                mCachedFm.ascent += mCachedPaint.baselineShift;
+                mCachedFm.top += mCachedPaint.baselineShift;
+            } else {
+                mCachedFm.descent += mCachedPaint.baselineShift;
+                mCachedFm.bottom += mCachedPaint.baselineShift;
+            }
+
+            mFontMetrics.append(mCachedFm.top);
+            mFontMetrics.append(mCachedFm.bottom);
+            mFontMetrics.append(mCachedFm.ascent);
+            mFontMetrics.append(mCachedFm.descent);
         }
     }
 
     /**
-     * Apply the style.
+     * Returns the maximum index that the accumulated width not exceeds the width.
      *
-     * If nativeStaticLayoutPtr is 0, this method measures the styled text width.
-     * If nativeStaticLayoutPtr is not 0, this method just passes the style information to native
-     * code by calling StaticLayout.addstyleRun() and returns 0.
+     * If forward=false is passed, returns the minimum index from the end instead.
+     *
+     * This only works if the MeasuredText is computed with computeForMeasurement.
+     * Undefined behavior in other case.
      */
-    float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm,
-            long nativeStaticLayoutPtr) {
-        if (fm != null) {
-            paint.getFontMetricsInt(fm);
-        }
-
-        final int p = mPos;
-        mPos = p + len;
-
-        if (mEasy) {
-            final boolean isRtl = mDir != Layout.DIR_LEFT_TO_RIGHT;
-            if (nativeStaticLayoutPtr == 0) {
-                return paint.getTextRunAdvances(mChars, p, len, p, len, isRtl, mWidths, p);
-            } else {
-                StaticLayout.addStyleRun(nativeStaticLayoutPtr, paint, p, p + len, isRtl);
-                return 0.0f;  // Builder.addStyleRun doesn't return the width.
-            }
-        }
-
-        float totalAdvance = 0;
-        int level = mLevels[p];
-        for (int q = p, i = p + 1, e = p + len;; ++i) {
-            if (i == e || mLevels[i] != level) {
-                final boolean isRtl = (level & 0x1) != 0;
-                if (nativeStaticLayoutPtr == 0) {
-                    totalAdvance +=
-                            paint.getTextRunAdvances(mChars, q, i - q, q, i - q, isRtl, mWidths, q);
-                } else {
-                    // Builder.addStyleRun doesn't return the width.
-                    StaticLayout.addStyleRun(nativeStaticLayoutPtr, paint, q, i, isRtl);
-                }
-                if (i == e) {
-                    break;
-                }
-                q = i;
-                level = mLevels[i];
-            }
-        }
-        return totalAdvance;  // If nativeStaticLayoutPtr is 0, the result is zero.
-    }
-
-    float addStyleRun(TextPaint paint, int len, Paint.FontMetricsInt fm) {
-        return addStyleRun(paint, len, fm, 0 /* native ptr */);
-    }
-
-    float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len,
-            Paint.FontMetricsInt fm, long nativeStaticLayoutPtr) {
-
-        TextPaint workPaint = mWorkPaint;
-        workPaint.set(paint);
-        // XXX paint should not have a baseline shift, but...
-        workPaint.baselineShift = 0;
-
-        ReplacementSpan replacement = null;
-        for (int i = 0; i < spans.length; i++) {
-            MetricAffectingSpan span = spans[i];
-            if (span instanceof ReplacementSpan) {
-                replacement = (ReplacementSpan)span;
-            } else {
-                span.updateMeasureState(workPaint);
-            }
-        }
-
-        float wid;
-        if (replacement == null) {
-            wid = addStyleRun(workPaint, len, fm, nativeStaticLayoutPtr);
-        } else {
-            // Use original text.  Shouldn't matter.
-            wid = replacement.getSize(workPaint, mText, mTextStart + mPos,
-                    mTextStart + mPos + len, fm);
-            if (nativeStaticLayoutPtr == 0) {
-                float[] w = mWidths;
-                w[mPos] = wid;
-                for (int i = mPos + 1, e = mPos + len; i < e; i++)
-                    w[i] = 0;
-            } else {
-                StaticLayout.addReplacementRun(nativeStaticLayoutPtr, paint, mPos, mPos + len, wid);
-            }
-            mPos += len;
-        }
-
-        if (fm != null) {
-            if (workPaint.baselineShift < 0) {
-                fm.ascent += workPaint.baselineShift;
-                fm.top += workPaint.baselineShift;
-            } else {
-                fm.descent += workPaint.baselineShift;
-                fm.bottom += workPaint.baselineShift;
-            }
-        }
-
-        return wid;
-    }
-
-    float addStyleRun(TextPaint paint, MetricAffectingSpan[] spans, int len,
-            Paint.FontMetricsInt fm) {
-        return addStyleRun(paint, spans, len, fm, 0 /* native ptr */);
-    }
-
-    int breakText(int limit, boolean forwards, float width) {
-        float[] w = mWidths;
+    @IntRange(from = 0) int breakText(int limit, boolean forwards, float width) {
+        float[] w = mWidths.getRawArray();
         if (forwards) {
             int i = 0;
             while (i < limit) {
@@ -267,7 +603,7 @@
                 if (width < 0.0f) break;
                 i++;
             }
-            while (i > 0 && mChars[i - 1] == ' ') i--;
+            while (i > 0 && mCopiedBuffer[i - 1] == ' ') i--;
             return i;
         } else {
             int i = limit - 1;
@@ -276,19 +612,65 @@
                 if (width < 0.0f) break;
                 i--;
             }
-            while (i < limit - 1 && (mChars[i + 1] == ' ' || w[i + 1] == 0.0f)) {
+            while (i < limit - 1 && (mCopiedBuffer[i + 1] == ' ' || w[i + 1] == 0.0f)) {
                 i++;
             }
             return limit - i - 1;
         }
     }
 
-    float measure(int start, int limit) {
+    /**
+     * Returns the length of the substring.
+     *
+     * This only works if the MeasuredText is computed with computeForMeasurement.
+     * Undefined behavior in other case.
+     */
+    @FloatRange(from = 0.0f) float measure(int start, int limit) {
         float width = 0;
-        float[] w = mWidths;
+        float[] w = mWidths.getRawArray();
         for (int i = start; i < limit; ++i) {
             width += w[i];
         }
         return width;
     }
+
+    private static native /* Non Zero */ long nInitBuilder();
+
+    /**
+     * Apply style to make native measured text.
+     *
+     * @param nativeBuilderPtr The native MeasuredText builder pointer.
+     * @param paintPtr The native paint pointer to be applied.
+     * @param start The start offset in the copied buffer.
+     * @param end The end offset in the copied buffer.
+     * @param isRtl True if the text is RTL.
+     */
+    private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
+                                            /* Non Zero */ long paintPtr,
+                                            @IntRange(from = 0) int start,
+                                            @IntRange(from = 0) int end,
+                                            boolean isRtl);
+
+    /**
+     * Apply ReplacementRun to make native measured text.
+     *
+     * @param nativeBuilderPtr The native MeasuredText builder pointer.
+     * @param paintPtr The native paint pointer to be applied.
+     * @param start The start offset in the copied buffer.
+     * @param end The end offset in the copied buffer.
+     * @param width The width of the replacement.
+     */
+    private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
+                                                  /* Non Zero */ long paintPtr,
+                                                  @IntRange(from = 0) int start,
+                                                  @IntRange(from = 0) int end,
+                                                  @FloatRange(from = 0) float width);
+
+    private static native long nBuildNativeMeasuredText(/* Non Zero */ long nativeBuilderPtr,
+                                                 @NonNull char[] text);
+
+    private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
+
+    @CriticalNative
+    private static native /* Non Zero */ long nGetReleaseFunc();
 }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index c0fc44f..400b075 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -21,10 +21,10 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Paint;
+import android.text.AutoGrowArray.FloatArray;
 import android.text.style.LeadingMarginSpan;
 import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
 import android.text.style.LineHeightSpan;
-import android.text.style.MetricAffectingSpan;
 import android.text.style.TabStopSpan;
 import android.util.Log;
 import android.util.Pools.SynchronizedPool;
@@ -48,6 +48,18 @@
  * Canvas.drawText()} directly.</p>
  */
 public class StaticLayout extends Layout {
+    /*
+     * The break iteration is done in native code. The protocol for using the native code is as
+     * follows.
+     *
+     * First, call nInit to setup native line breaker object. Then, for each paragraph, do the
+     * following:
+     *
+     *   - Create MeasuredText by MeasuredText.buildForStaticLayout which measures in native.
+     *   - Run nComputeLineBreaks() to obtain line breaks for the paragraph.
+     *
+     * After all paragraphs, call finish() to release expensive buffers.
+     */
 
     static final String TAG = "StaticLayout";
 
@@ -99,8 +111,6 @@
             b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
             b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
             b.mJustificationMode = Layout.JUSTIFICATION_MODE_NONE;
-
-            b.mMeasuredText = MeasuredText.obtain();
             return b;
         }
 
@@ -111,8 +121,6 @@
         private static void recycle(@NonNull Builder b) {
             b.mPaint = null;
             b.mText = null;
-            MeasuredText.recycle(b.mMeasuredText);
-            b.mMeasuredText = null;
             b.mLeftIndents = null;
             b.mRightIndents = null;
             b.mLeftPaddings = null;
@@ -128,7 +136,6 @@
             mRightIndents = null;
             mLeftPaddings = null;
             mRightPaddings = null;
-            mMeasuredText.finish();
         }
 
         public Builder setText(CharSequence source) {
@@ -444,9 +451,6 @@
 
         private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
 
-        // This will go away and be subsumed by native builder code
-        private MeasuredText mMeasuredText;
-
         private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<>(3);
     }
 
@@ -618,11 +622,7 @@
         TextUtils.TruncateAt ellipsize = b.mEllipsize;
         final boolean addLastLineSpacing = b.mAddLastLineLineSpacing;
         LineBreaks lineBreaks = new LineBreaks();  // TODO: move to builder to avoid allocation costs
-        // store span end locations
-        int[] spanEndCache = new int[4];
-        // store fontMetrics per span range
-        // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range)
-        int[] fmCache = new int[4 * 4];
+        FloatArray widths = new FloatArray();
 
         mLineCount = 0;
         mEllipsized = false;
@@ -634,8 +634,6 @@
         Paint.FontMetricsInt fm = b.mFontMetricsInt;
         int[] chooseHtv = null;
 
-        MeasuredText measured = b.mMeasuredText;
-
         Spanned spanned = null;
         if (source instanceof Spanned)
             spanned = (Spanned) source;
@@ -662,6 +660,7 @@
                 b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
                 indents, mLeftPaddings, mRightPaddings);
 
+        MeasuredText measured = null;
         try {
             int paraEnd;
             for (int paraStart = bufStart; paraStart <= bufEnd; paraStart = paraEnd) {
@@ -721,13 +720,6 @@
                     }
                 }
 
-                measured.setPara(source, paraStart, paraEnd, textDir);
-                char[] chs = measured.mChars;
-                float[] widths = measured.mWidths;
-                byte[] chdirs = measured.mLevels;
-                int dir = measured.mDir;
-                boolean easy = measured.mEasy;
-
                 // tab stop locations
                 int[] variableTabStops = null;
                 if (spanned != null) {
@@ -743,56 +735,24 @@
                     }
                 }
 
+                measured = MeasuredText.buildForStaticLayout(
+                        paint, source, paraStart, paraEnd, textDir, measured);
+                final char[] chs = measured.getChars();
+                final int[] spanEndCache = measured.getSpanEndCache().getRawArray();
+                final int[] fmCache = measured.getFontMetrics().getRawArray();
+                // TODO: Stop keeping duplicated width copy in native and Java.
+                widths.resize(chs.length);
+
                 // measurement has to be done before performing line breaking
                 // but we don't want to recompute fontmetrics or span ranges the
                 // second time, so we cache those and then use those stored values
-                int fmCacheCount = 0;
-                int spanEndCacheCount = 0;
-                for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
-                    if (fmCacheCount * 4 >= fmCache.length) {
-                        int[] grow = new int[fmCacheCount * 4 * 2];
-                        System.arraycopy(fmCache, 0, grow, 0, fmCacheCount * 4);
-                        fmCache = grow;
-                    }
-
-                    if (spanEndCacheCount >= spanEndCache.length) {
-                        int[] grow = new int[spanEndCacheCount * 2];
-                        System.arraycopy(spanEndCache, 0, grow, 0, spanEndCacheCount);
-                        spanEndCache = grow;
-                    }
-
-                    if (spanned == null) {
-                        spanEnd = paraEnd;
-                        int spanLen = spanEnd - spanStart;
-                        measured.addStyleRun(paint, spanLen, fm, nativePtr);
-                    } else {
-                        spanEnd = spanned.nextSpanTransition(spanStart, paraEnd,
-                                MetricAffectingSpan.class);
-                        int spanLen = spanEnd - spanStart;
-                        MetricAffectingSpan[] spans =
-                                spanned.getSpans(spanStart, spanEnd, MetricAffectingSpan.class);
-                        spans = TextUtils.removeEmptySpans(spans, spanned,
-                                MetricAffectingSpan.class);
-                        measured.addStyleRun(paint, spans, spanLen, fm, nativePtr);
-                    }
-
-                    // the order of storage here (top, bottom, ascent, descent) has to match the
-                    // code below where these values are retrieved
-                    fmCache[fmCacheCount * 4 + 0] = fm.top;
-                    fmCache[fmCacheCount * 4 + 1] = fm.bottom;
-                    fmCache[fmCacheCount * 4 + 2] = fm.ascent;
-                    fmCache[fmCacheCount * 4 + 3] = fm.descent;
-                    fmCacheCount++;
-
-                    spanEndCache[spanEndCacheCount] = spanEnd;
-                    spanEndCacheCount++;
-                }
 
                 int breakCount = nComputeLineBreaks(
                         nativePtr,
 
                         // Inputs
                         chs,
+                        measured.getNativePtr(),
                         paraEnd - paraStart,
                         firstWidth,
                         firstWidthLineCount,
@@ -809,7 +769,7 @@
                         lineBreaks.ascents,
                         lineBreaks.descents,
                         lineBreaks.flags,
-                        widths);
+                        widths.getRawArray());
 
                 final int[] breaks = lineBreaks.breaks;
                 final float[] lineWidths = lineBreaks.widths;
@@ -832,7 +792,7 @@
                             width += lineWidths[i];
                         } else {
                             for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
-                                width += widths[j];
+                                width += widths.get(j);
                             }
                         }
                         flag |= flags[i] & TAB_MASK;
@@ -896,10 +856,10 @@
                         v = out(source, here, endPos,
                                 ascent, descent, fmTop, fmBottom,
                                 v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
-                                flags[breakIndex], needMultiply, chdirs, dir, easy, bufEnd,
-                                includepad, trackpad, addLastLineSpacing, chs, widths, paraStart,
-                                ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint,
-                                moreChars);
+                                flags[breakIndex], needMultiply, measured, bufEnd,
+                                includepad, trackpad, addLastLineSpacing, chs, widths.getRawArray(),
+                                paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex],
+                                paint, moreChars);
 
                         if (endPos < spanEnd) {
                             // preserve metrics for current span
@@ -927,22 +887,22 @@
 
             if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE)
                     && mLineCount < mMaximumVisibleLineCount) {
-                measured.setPara(source, bufEnd, bufEnd, textDir);
-
                 paint.getFontMetricsInt(fm);
-
                 v = out(source,
                         bufEnd, bufEnd, fm.ascent, fm.descent,
                         fm.top, fm.bottom,
                         v,
                         spacingmult, spacingadd, null,
                         null, fm, 0,
-                        needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd,
+                        needMultiply, null, bufEnd,
                         includepad, trackpad, addLastLineSpacing, null,
                         null, bufStart, ellipsize,
                         ellipsizedWidth, 0, paint, false);
             }
         } finally {
+            if (measured != null) {
+                measured.recycle();
+            }
             nFinish(nativePtr);
         }
     }
@@ -952,8 +912,8 @@
     private int out(final CharSequence text, final int start, final int end, int above, int below,
             int top, int bottom, int v, final float spacingmult, final float spacingadd,
             final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm,
-            final int flags, final boolean needMultiply, final byte[] chdirs, final int dir,
-            final boolean easy, final int bufEnd, final boolean includePad, final boolean trackPad,
+            final int flags, final boolean needMultiply, @Nullable final MeasuredText measured,
+            final int bufEnd, final boolean includePad, final boolean trackPad,
             final boolean addLastLineLineSpacing, final char[] chs, final float[] widths,
             final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth,
             final float textWidth, final TextPaint paint, final boolean moreChars) {
@@ -961,6 +921,7 @@
         final int off = j * mColumns;
         final int want = off + mColumns + TOP;
         int[] lines = mLines;
+        final int dir = (start == end) ? Layout.DIR_LEFT_TO_RIGHT : measured.getParagraphDir();
 
         if (want >= lines.length) {
             final int[] grow = ArrayUtils.newUnpaddedIntArray(GrowingArrayUtils.growSize(want));
@@ -986,16 +947,11 @@
         // one bit for start field
         lines[off + TAB] |= flags & TAB_MASK;
         lines[off + HYPHEN] = flags;
-
         lines[off + DIR] |= dir << DIR_SHIFT;
-        // easy means all chars < the first RTL, so no emoji, no nothing
-        // XXX a run with no text or all spaces is easy but might be an empty
-        // RTL paragraph.  Make sure easy is false if this is the case.
-        if (easy) {
-            mLineDirections[j] = DIRS_ALL_LEFT_TO_RIGHT;
+        if (start == end) {
+            mLineDirections[j] = Layout.DIRS_ALL_LEFT_TO_RIGHT;
         } else {
-            mLineDirections[j] = AndroidBidi.directions(dir, chdirs, start - widthStart, chs,
-                    start - widthStart, end - start);
+            mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart);
         }
 
         final boolean firstLine = (j == 0);
@@ -1473,33 +1429,6 @@
                 mMaxLineHeight : super.getHeight();
     }
 
-    /**
-     * Measurement and break iteration is done in native code. The protocol for using
-     * the native code is as follows.
-     *
-     * First, call nInit to setup native line breaker object. Then, for each paragraph, do the
-     * following:
-     *
-     *   - Call one of the following methods for each run within the paragraph depending on the type
-     *     of run:
-     *     + addStyleRun (a text run, to be measured in native code)
-     *     + addReplacementRun (a replacement run, width is given)
-     *
-     *   - Run nComputeLineBreaks() to obtain line breaks for the paragraph.
-     *
-     * After all paragraphs, call finish() to release expensive buffers.
-     */
-
-    /* package */ static void addStyleRun(long nativePtr, TextPaint paint, int start, int end,
-            boolean isRtl) {
-        nAddStyleRun(nativePtr, paint.getNativeInstance(), start, end, isRtl);
-    }
-
-    /* package */ static void addReplacementRun(long nativePtr, TextPaint paint, int start, int end,
-            float width) {
-        nAddReplacementRun(nativePtr, paint.getNativeInstance(), start, end, width);
-    }
-
     @FastNative
     private static native long nInit(
             @BreakStrategy int breakStrategy,
@@ -1512,17 +1441,6 @@
     @CriticalNative
     private static native void nFinish(long nativePtr);
 
-    @CriticalNative
-    private static native void nAddStyleRun(
-            /* non-zero */ long nativePtr, /* non-zero */ long nativePaint,
-            @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean isRtl);
-
-    @CriticalNative
-    private static native void nAddReplacementRun(
-            /* non-zero */ long nativePtr, /* non-zero */ long nativePaint,
-            @IntRange(from = 0) int start, @IntRange(from = 0) int end,
-            @FloatRange(from = 0.0f) float width);
-
     // populates LineBreaks and returns the number of breaks found
     //
     // the arrays inside the LineBreaks objects are passed in as well
@@ -1535,6 +1453,7 @@
 
             // Inputs
             @NonNull char[] text,
+            /* Non Zero */ long measuredTextPtr,
             @IntRange(from = 0) int length,
             @FloatRange(from = 0.0f) float firstWidth,
             @IntRange(from = 0) int firstWidthLineCount,
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index cbdaa69..9c9fbf2 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -42,7 +42,6 @@
 import android.text.style.ForegroundColorSpan;
 import android.text.style.LeadingMarginSpan;
 import android.text.style.LocaleSpan;
-import android.text.style.MetricAffectingSpan;
 import android.text.style.ParagraphStyle;
 import android.text.style.QuoteSpan;
 import android.text.style.RelativeSizeSpan;
@@ -1251,10 +1250,11 @@
             @NonNull String ellipsis) {
 
         final int len = text.length();
-        final MeasuredText mt = MeasuredText.obtain();
+        MeasuredText mt = null;
         MeasuredText resultMt = null;
         try {
-            float width = setPara(mt, paint, text, 0, text.length(), textDir);
+            mt = MeasuredText.buildForMeasurement(paint, text, 0, text.length(), textDir, mt);
+            float width = mt.getWholeWidth();
 
             if (width <= avail) {
                 if (callback != null) {
@@ -1263,7 +1263,6 @@
                 return text;
             }
 
-            resultMt = MeasuredText.obtain();
             // First estimate of effective width of ellipsis.
             float ellipsisWidth = paint.measureText(ellipsis);
             int numberOfTries = 0;
@@ -1290,7 +1289,7 @@
                     }
                 }
 
-                final char[] buf = mt.mChars;
+                final char[] buf = mt.getChars();
                 final Spanned sp = text instanceof Spanned ? (Spanned) text : null;
 
                 final int removed = end - start;
@@ -1333,7 +1332,9 @@
                 if (remaining == 0) { // All text is gone.
                     textFits = true;
                 } else {
-                    width = setPara(resultMt, paint, result, 0, result.length(), textDir);
+                    resultMt = MeasuredText.buildForMeasurement(
+                            paint, result, 0, result.length(), textDir, resultMt);
+                    width = resultMt.getWholeWidth();
                     if (width <= avail) {
                         textFits = true;
                     } else {
@@ -1357,9 +1358,11 @@
             }
             return result;
         } finally {
-            MeasuredText.recycle(mt);
+            if (mt != null) {
+                mt.recycle();
+            }
             if (resultMt != null) {
-                MeasuredText.recycle(resultMt);
+                resultMt.recycle();
             }
         }
     }
@@ -1476,15 +1479,17 @@
     public static CharSequence commaEllipsize(CharSequence text, TextPaint p,
          float avail, String oneMore, String more, TextDirectionHeuristic textDir) {
 
-        MeasuredText mt = MeasuredText.obtain();
+        MeasuredText mt = null;
+        MeasuredText tempMt = null;
         try {
             int len = text.length();
-            float width = setPara(mt, p, text, 0, len, textDir);
+            mt = MeasuredText.buildForMeasurement(p, text, 0, len, textDir, mt);
+            final float width = mt.getWholeWidth();
             if (width <= avail) {
                 return text;
             }
 
-            char[] buf = mt.mChars;
+            char[] buf = mt.getChars();
 
             int commaCount = 0;
             for (int i = 0; i < len; i++) {
@@ -1500,9 +1505,8 @@
 
             int w = 0;
             int count = 0;
-            float[] widths = mt.mWidths;
+            float[] widths = mt.getWidths().getRawArray();
 
-            MeasuredText tempMt = MeasuredText.obtain();
             for (int i = 0; i < len; i++) {
                 w += widths[i];
 
@@ -1519,8 +1523,9 @@
                     }
 
                     // XXX this is probably ok, but need to look at it more
-                    tempMt.setPara(format, 0, format.length(), textDir);
-                    float moreWid = tempMt.addStyleRun(p, tempMt.mLen, null);
+                    tempMt = MeasuredText.buildForMeasurement(
+                            p, format, 0, format.length(), textDir, tempMt);
+                    float moreWid = tempMt.getWholeWidth();
 
                     if (w + moreWid <= avail) {
                         ok = i + 1;
@@ -1528,40 +1533,18 @@
                     }
                 }
             }
-            MeasuredText.recycle(tempMt);
 
             SpannableStringBuilder out = new SpannableStringBuilder(okFormat);
             out.insert(0, text, 0, ok);
             return out;
         } finally {
-            MeasuredText.recycle(mt);
-        }
-    }
-
-    private static float setPara(MeasuredText mt, TextPaint paint,
-            CharSequence text, int start, int end, TextDirectionHeuristic textDir) {
-
-        mt.setPara(text, start, end, textDir);
-
-        float width;
-        Spanned sp = text instanceof Spanned ? (Spanned) text : null;
-        int len = end - start;
-        if (sp == null) {
-            width = mt.addStyleRun(paint, len, null);
-        } else {
-            width = 0;
-            int spanEnd;
-            for (int spanStart = 0; spanStart < len; spanStart = spanEnd) {
-                spanEnd = sp.nextSpanTransition(spanStart, len,
-                        MetricAffectingSpan.class);
-                MetricAffectingSpan[] spans = sp.getSpans(
-                        spanStart, spanEnd, MetricAffectingSpan.class);
-                spans = TextUtils.removeEmptySpans(spans, sp, MetricAffectingSpan.class);
-                width += mt.addStyleRun(paint, spans, spanEnd - spanStart, null);
+            if (mt != null) {
+                mt.recycle();
+            }
+            if (tempMt != null) {
+                tempMt.recycle();
             }
         }
-
-        return width;
     }
 
     // Returns true if the character's presence could affect RTL layout.
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 29baea1..80785fd 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -21,6 +21,7 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 
+import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -33,6 +34,17 @@
     public static final String FFLAG_PREFIX = "sys.fflag.";
     public static final String FFLAG_OVERRIDE_PREFIX = FFLAG_PREFIX + "override.";
 
+    private static final Map<String, String> DEFAULT_FLAGS;
+    static {
+        DEFAULT_FLAGS = new HashMap<>();
+        DEFAULT_FLAGS.put("device_info_v2", "true");
+        DEFAULT_FLAGS.put("new_settings_suggestion", "true");
+        DEFAULT_FLAGS.put("settings_search_v2", "false");
+        DEFAULT_FLAGS.put("settings_app_info_v2", "false");
+        DEFAULT_FLAGS.put("settings_connected_device_v2", "false");
+        DEFAULT_FLAGS.put("settings_battery_v2", "false");
+    }
+
     /**
      * Whether or not a flag is enabled.
      *
@@ -41,7 +53,7 @@
      */
     public static boolean isEnabled(Context context, String feature) {
         // Override precedence:
-        // Settings.Global -> sys.fflag.override.* -> sys.fflag.*
+        // Settings.Global -> sys.fflag.override.* -> static list
 
         // Step 1: check if feature flag is set in Settings.Global.
         String value;
@@ -57,8 +69,8 @@
         if (!TextUtils.isEmpty(value)) {
             return Boolean.parseBoolean(value);
         }
-        // Step 3: check if feature flag has any default value. Flag name: sys.fflag.<feature>
-        value = SystemProperties.get(FFLAG_PREFIX + feature);
+        // Step 3: check if feature flag has any default value.
+        value = getAllFeatureFlags().get(feature);
         return Boolean.parseBoolean(value);
     }
 
@@ -73,6 +85,6 @@
      * Returns all feature flags in their raw form.
      */
     public static Map<String, String> getAllFeatureFlags() {
-        return null;
+        return DEFAULT_FLAGS;
     }
 }
diff --git a/core/java/android/util/KeyValueListParser.java b/core/java/android/util/KeyValueListParser.java
index d50395e..0a00794 100644
--- a/core/java/android/util/KeyValueListParser.java
+++ b/core/java/android/util/KeyValueListParser.java
@@ -149,6 +149,34 @@
     }
 
     /**
+     * Get the value for key as an integer array.
+     *
+     * The value should be encoded as "0:1:2:3:4"
+     *
+     * @param key The key to lookup.
+     * @param def The value to return if the key was not found.
+     * @return the int[] value associated with the key.
+     */
+    public int[] getIntArray(String key, int[] def) {
+        String value = mValues.get(key);
+        if (value != null) {
+            try {
+                String[] parts = value.split(":");
+                if (parts.length > 0) {
+                    int[] ret = new int[parts.length];
+                    for (int i = 0; i < parts.length; i++) {
+                        ret[i] = Integer.parseInt(parts[i]);
+                    }
+                    return ret;
+                }
+            } catch (NumberFormatException e) {
+                // fallthrough
+            }
+        }
+        return def;
+    }
+
+    /**
      * @return the number of keys.
      */
     public int size() {
diff --git a/core/java/android/util/Pools.java b/core/java/android/util/Pools.java
index 70581be..f0b7e01 100644
--- a/core/java/android/util/Pools.java
+++ b/core/java/android/util/Pools.java
@@ -130,22 +130,29 @@
     }
 
     /**
-     * Synchronized) pool of objects.
+     * Synchronized pool of objects.
      *
      * @param <T> The pooled type.
      */
     public static class SynchronizedPool<T> extends SimplePool<T> {
-        private final Object mLock = new Object();
+        private final Object mLock;
 
         /**
          * Creates a new instance.
          *
          * @param maxPoolSize The max pool size.
+         * @param lock an optional custom object to synchronize on
          *
          * @throws IllegalArgumentException If the max pool size is less than zero.
          */
-        public SynchronizedPool(int maxPoolSize) {
+        public SynchronizedPool(int maxPoolSize, Object lock) {
             super(maxPoolSize);
+            mLock = lock;
+        }
+
+        /** @see #SynchronizedPool(int, Object)  */
+        public SynchronizedPool(int maxPoolSize) {
+            this(maxPoolSize, new Object());
         }
 
         @Override
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 2ffa1c5..ba6b6cf 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -164,6 +164,7 @@
     private long mLastFrameTimeNanos;
     private long mFrameIntervalNanos;
     private boolean mDebugPrintNextFrameTimeDelta;
+    private int mFPSDivisor = 1;
 
     /**
      * Contains information about the current frame for jank-tracking,
@@ -601,6 +602,11 @@
         }
     }
 
+    void setFPSDivisor(int divisor) {
+        if (divisor <= 0) divisor = 1;
+        mFPSDivisor = divisor;
+    }
+
     void doFrame(long frameTimeNanos, int frame) {
         final long startNanos;
         synchronized (mLock) {
@@ -643,6 +649,14 @@
                 return;
             }
 
+            if (mFPSDivisor > 1) {
+                long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
+                if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
+                    scheduleVsyncLocked();
+                    return;
+                }
+            }
+
             mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
             mFrameScheduled = false;
             mLastFrameTimeNanos = frameTimeNanos;
diff --git a/core/java/android/view/DisplayCutout.aidl b/core/java/android/view/DisplayCutout.aidl
new file mode 100644
index 0000000..6d13b99
--- /dev/null
+++ b/core/java/android/view/DisplayCutout.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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;
+
+parcelable DisplayCutout.ParcelableWrapper;
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 611cc63..07a57fb 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -23,6 +23,7 @@
 import android.view.DragEvent;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.DisplayCutout;
 
 import com.android.internal.os.IResultReceiver;
 import android.util.MergedConfiguration;
@@ -50,7 +51,8 @@
     void resized(in Rect frame, in Rect overscanInsets, in Rect contentInsets,
             in Rect visibleInsets, in Rect stableInsets, in Rect outsets, boolean reportDraw,
             in MergedConfiguration newMergedConfiguration, in Rect backDropFrame,
-            boolean forceLayout, boolean alwaysConsumeNavBar, int displayId);
+            boolean forceLayout, boolean alwaysConsumeNavBar, int displayId,
+            in DisplayCutout.ParcelableWrapper displayCutout);
     void moved(int newX, int newY);
     void dispatchAppVisibility(boolean visible);
     void dispatchGetNewSurface();
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 29032a7..9e103a3 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -147,7 +147,6 @@
     void exitKeyguardSecurely(IOnKeyguardExitResult callback);
     boolean isKeyguardLocked();
     boolean isKeyguardSecure();
-    boolean inKeyguardRestrictedInputMode();
     void dismissKeyguard(IKeyguardDismissCallback callback);
 
     // Requires INTERACT_ACROSS_USERS_FULL permission
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 51d6514..ed167c8 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -22,6 +22,7 @@
 import android.graphics.Region;
 import android.os.Bundle;
 import android.util.MergedConfiguration;
+import android.view.DisplayCutout;
 import android.view.InputChannel;
 import android.view.IWindow;
 import android.view.IWindowId;
@@ -40,7 +41,8 @@
             out InputChannel outInputChannel);
     int addToDisplay(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, out Rect outContentInsets,
-            out Rect outStableInsets, out Rect outOutsets, out InputChannel outInputChannel);
+            out Rect outStableInsets, out Rect outOutsets,
+            out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel);
     int addWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             in int viewVisibility, out Rect outContentInsets, out Rect outStableInsets);
     int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs,
@@ -96,6 +98,7 @@
             int flags, out Rect outFrame, out Rect outOverscanInsets,
             out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
             out Rect outOutsets, out Rect outBackdropFrame,
+            out DisplayCutout.ParcelableWrapper displayCutout,
             out MergedConfiguration outMergedConfiguration, out Surface outSurface);
 
     /*
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 37550d8..420a1bb 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -26,6 +26,8 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.Process;
 import android.os.UserHandle;
 import android.util.Log;
@@ -39,12 +41,14 @@
  * SurfaceControl
  *  @hide
  */
-public class SurfaceControl {
+public class SurfaceControl implements Parcelable {
     private static final String TAG = "SurfaceControl";
 
     private static native long nativeCreate(SurfaceSession session, String name,
             int w, int h, int format, int flags, long parentObject, int windowType, int ownerUid)
             throws OutOfResourcesException;
+    private static native long nativeReadFromParcel(Parcel in);
+    private static native void nativeWriteToParcel(long nativeObject, Parcel out);
     private static native void nativeRelease(long nativeObject);
     private static native void nativeDestroy(long nativeObject);
     private static native void nativeDisconnect(long nativeObject);
@@ -577,6 +581,37 @@
         mCloseGuard.open("release");
     }
 
+    private SurfaceControl(Parcel in) {
+        mName = in.readString();
+        mNativeObject = nativeReadFromParcel(in);
+        if (mNativeObject == 0) {
+            throw new IllegalArgumentException("Couldn't read SurfaceControl from parcel=" + in);
+        }
+        mCloseGuard.open("release");
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mName);
+        nativeWriteToParcel(mNativeObject, dest);
+    }
+
+    public static final Creator<SurfaceControl> CREATOR
+            = new Creator<SurfaceControl>() {
+        public SurfaceControl createFromParcel(Parcel in) {
+            return new SurfaceControl(in);
+        }
+
+        public SurfaceControl[] newArray(int size) {
+            return new SurfaceControl[size];
+        }
+    };
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -667,7 +702,7 @@
      */
     @Deprecated
     public static void mergeToGlobalTransaction(Transaction t) {
-        synchronized(sGlobalTransaction) {
+        synchronized(SurfaceControl.class) {
             sGlobalTransaction.merge(t);
         }
     }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 7c76bab..0a69772 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -190,6 +190,17 @@
     public static final String DEBUG_SHOW_NON_RECTANGULAR_CLIP_PROPERTY =
             "debug.hwui.show_non_rect_clip";
 
+    /**
+     * Sets the FPS devisor to lower the FPS.
+     *
+     * Sets a positive integer as a divisor. 1 (the default value) menas the full FPS, and 2
+     * means half the full FPS.
+     *
+     *
+     * @hide
+     */
+    public static final String DEBUG_FPS_DIVISOR = "debug.hwui.fps_divisor";
+
     static {
         // Try to check OpenGL support early if possible.
         isAvailable();
@@ -955,6 +966,9 @@
             if (mInitialized) return;
             mInitialized = true;
             mAppContext = context.getApplicationContext();
+
+            // b/68769804: For low FPS experiments.
+            setFPSDivisor(SystemProperties.getInt(DEBUG_FPS_DIVISOR, 1));
             initSched(renderProxy);
             initGraphicsStats();
         }
@@ -1007,6 +1021,13 @@
         observer.mNative = null;
     }
 
+    /** b/68769804: For low FPS experiments. */
+    public static void setFPSDivisor(int divisor) {
+        if (divisor <= 0) divisor = 1;
+        Choreographer.getInstance().setFPSDivisor(divisor);
+        nHackySetRTAnimationsEnabled(divisor == 1);
+    }
+
     /** Not actually public - internal use only. This doc to make lint happy */
     public static native void disableVsync();
 
@@ -1075,4 +1096,6 @@
 
     private static native Bitmap nCreateHardwareBitmap(long renderNode, int width, int height);
     private static native void nSetHighContrastText(boolean enabled);
+    // For temporary experimentation b/66945974
+    private static native void nHackySetRTAnimationsEnabled(boolean enabled);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0525ab1..623759e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -893,6 +893,15 @@
      */
     private static boolean sAutoFocusableOffUIThreadWontNotifyParents;
 
+    /**
+     * Prior to P things like setScaleX() allowed passing float values that were bogus such as
+     * Float.NaN. If the app is targetting P or later then passing these values will result in an
+     * exception being thrown. If the app is targetting an earlier SDK version, then we will
+     * silently clamp these values to avoid crashes elsewhere when the rendering code hits
+     * these bogus values.
+     */
+    private static boolean sThrowOnInvalidFloatProperties;
+
     /** @hide */
     @IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO})
     @Retention(RetentionPolicy.SOURCE)
@@ -4752,6 +4761,8 @@
             sUseDefaultFocusHighlight = context.getResources().getBoolean(
                     com.android.internal.R.bool.config_useDefaultFocusHighlight);
 
+            sThrowOnInvalidFloatProperties = targetSdkVersion >= Build.VERSION_CODES.P;
+
             sCompatibilityDone = true;
         }
     }
@@ -7208,8 +7219,7 @@
      * @param text The announcement text.
      */
     public void announceForAccessibility(CharSequence text) {
-        if (AccessibilityManager.getInstance(mContext).isObservedEventType(
-                AccessibilityEvent.TYPE_ANNOUNCEMENT) && mParent != null) {
+        if (AccessibilityManager.getInstance(mContext).isEnabled() && mParent != null) {
             AccessibilityEvent event = AccessibilityEvent.obtain(
                     AccessibilityEvent.TYPE_ANNOUNCEMENT);
             onInitializeAccessibilityEvent(event);
@@ -10968,8 +10978,7 @@
         if ((mPrivateFlags2 & PFLAG2_ACCESSIBILITY_FOCUSED) != 0) {
             mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_FOCUSED;
             invalidate();
-            if (AccessibilityManager.getInstance(mContext).isObservedEventType(
-                    AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED)) {
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                 AccessibilityEvent event = AccessibilityEvent.obtain(
                         AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
                 event.setAction(action);
@@ -11794,8 +11803,7 @@
 
     private void sendViewTextTraversedAtGranularityEvent(int action, int granularity,
             int fromIndex, int toIndex) {
-        if (mParent == null || !AccessibilityManager.getInstance(mContext).isObservedEventType(
-                AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY)) {
+        if (mParent == null) {
             return;
         }
         AccessibilityEvent event = AccessibilityEvent.obtain(
@@ -14275,7 +14283,7 @@
      */
     public void setScaleX(float scaleX) {
         if (scaleX != getScaleX()) {
-            requireIsFinite(scaleX, "scaleX");
+            scaleX = sanitizeFloatPropertyValue(scaleX, "scaleX");
             invalidateViewProperty(true, false);
             mRenderNode.setScaleX(scaleX);
             invalidateViewProperty(false, true);
@@ -14312,7 +14320,7 @@
      */
     public void setScaleY(float scaleY) {
         if (scaleY != getScaleY()) {
-            requireIsFinite(scaleY, "scaleY");
+            scaleY = sanitizeFloatPropertyValue(scaleY, "scaleY");
             invalidateViewProperty(true, false);
             mRenderNode.setScaleY(scaleY);
             invalidateViewProperty(false, true);
@@ -14862,13 +14870,41 @@
         }
     }
 
-    private static void requireIsFinite(float transform, String propertyName) {
-        if (Float.isNaN(transform)) {
-            throw new IllegalArgumentException("Cannot set '" + propertyName + "' to Float.NaN");
+    private static float sanitizeFloatPropertyValue(float value, String propertyName) {
+        return sanitizeFloatPropertyValue(value, propertyName, -Float.MAX_VALUE, Float.MAX_VALUE);
+    }
+
+    private static float sanitizeFloatPropertyValue(float value, String propertyName,
+            float min, float max) {
+        // The expected "nothing bad happened" path
+        if (value >= min && value <= max) return value;
+
+        if (value < min || value == Float.NEGATIVE_INFINITY) {
+            if (sThrowOnInvalidFloatProperties) {
+                throw new IllegalArgumentException("Cannot set '" + propertyName + "' to "
+                        + value + ", the value must be >= " + min);
+            }
+            return min;
         }
-        if (Float.isInfinite(transform)) {
-            throw new IllegalArgumentException("Cannot set '" + propertyName + "' to infinity");
+
+        if (value > max || value == Float.POSITIVE_INFINITY) {
+            if (sThrowOnInvalidFloatProperties) {
+                throw new IllegalArgumentException("Cannot set '" + propertyName + "' to "
+                        + value + ", the value must be <= " + max);
+            }
+            return max;
         }
+
+        if (Float.isNaN(value)) {
+            if (sThrowOnInvalidFloatProperties) {
+                throw new IllegalArgumentException(
+                        "Cannot set '" + propertyName + "' to Float.NaN");
+            }
+            return 0; // Unclear which direction this NaN went so... 0?
+        }
+
+        // Shouldn't be possible to reach this.
+        throw new IllegalStateException("How do you get here?? " + value);
     }
 
     /**
@@ -14957,7 +14993,7 @@
      */
     public void setElevation(float elevation) {
         if (elevation != getElevation()) {
-            requireIsFinite(elevation, "elevation");
+            elevation = sanitizeFloatPropertyValue(elevation, "elevation");
             invalidateViewProperty(true, false);
             mRenderNode.setElevation(elevation);
             invalidateViewProperty(false, true);
@@ -15050,7 +15086,7 @@
      */
     public void setTranslationZ(float translationZ) {
         if (translationZ != getTranslationZ()) {
-            requireIsFinite(translationZ, "translationZ");
+            translationZ = sanitizeFloatPropertyValue(translationZ, "translationZ");
             invalidateViewProperty(true, false);
             mRenderNode.setTranslationZ(translationZ);
             invalidateViewProperty(false, true);
@@ -25721,6 +25757,9 @@
          */
         final Rect mStableInsets = new Rect();
 
+        final DisplayCutout.ParcelableWrapper mDisplayCutout =
+                new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
+
         /**
          * For windows that include areas that are not covered by real surface these are the outsets
          * for real surface.
@@ -26185,8 +26224,7 @@
 
         @Override
         public void run() {
-            if (AccessibilityManager.getInstance(mContext).isObservedEventType(
-                    AccessibilityEvent.TYPE_VIEW_SCROLLED)) {
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                 AccessibilityEvent event = AccessibilityEvent.obtain(
                         AccessibilityEvent.TYPE_VIEW_SCROLLED);
                 event.setScrollDeltaX(mDeltaX);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1c9d863..0bae36b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -384,12 +384,15 @@
     final Rect mPendingContentInsets = new Rect();
     final Rect mPendingOutsets = new Rect();
     final Rect mPendingBackDropFrame = new Rect();
+    final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
+            new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
     boolean mPendingAlwaysConsumeNavBar;
     final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
             = new ViewTreeObserver.InternalInsetsInfo();
 
     final Rect mDispatchContentInsets = new Rect();
     final Rect mDispatchStableInsets = new Rect();
+    DisplayCutout mDispatchDisplayCutout = DisplayCutout.NO_CUTOUT;
 
     private WindowInsets mLastWindowInsets;
 
@@ -730,7 +733,7 @@
                     res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                             getHostVisibility(), mDisplay.getDisplayId(),
                             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
-                            mAttachInfo.mOutsets, mInputChannel);
+                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                 } catch (RemoteException e) {
                     mAdded = false;
                     mView = null;
@@ -752,6 +755,7 @@
                 mPendingOverscanInsets.set(0, 0, 0, 0);
                 mPendingContentInsets.set(mAttachInfo.mContentInsets);
                 mPendingStableInsets.set(mAttachInfo.mStableInsets);
+                mPendingDisplayCutout.set(mAttachInfo.mDisplayCutout);
                 mPendingVisibleInsets.set(0, 0, 0, 0);
                 mAttachInfo.mAlwaysConsumeNavBar =
                         (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR) != 0;
@@ -1544,15 +1548,20 @@
         if (mLastWindowInsets == null || forceConstruct) {
             mDispatchContentInsets.set(mAttachInfo.mContentInsets);
             mDispatchStableInsets.set(mAttachInfo.mStableInsets);
+            mDispatchDisplayCutout = mAttachInfo.mDisplayCutout.get();
+
             Rect contentInsets = mDispatchContentInsets;
             Rect stableInsets = mDispatchStableInsets;
+            DisplayCutout displayCutout = mDispatchDisplayCutout;
             // For dispatch we preserve old logic, but for direct requests from Views we allow to
             // immediately use pending insets.
             if (!forceConstruct
                     && (!mPendingContentInsets.equals(contentInsets) ||
-                        !mPendingStableInsets.equals(stableInsets))) {
+                        !mPendingStableInsets.equals(stableInsets) ||
+                        !mPendingDisplayCutout.get().equals(displayCutout))) {
                 contentInsets = mPendingContentInsets;
                 stableInsets = mPendingStableInsets;
+                displayCutout = mPendingDisplayCutout.get();
             }
             Rect outsets = mAttachInfo.mOutsets;
             if (outsets.left > 0 || outsets.top > 0 || outsets.right > 0 || outsets.bottom > 0) {
@@ -1563,7 +1572,7 @@
             mLastWindowInsets = new WindowInsets(contentInsets,
                     null /* windowDecorInsets */, stableInsets,
                     mContext.getResources().getConfiguration().isScreenRound(),
-                    mAttachInfo.mAlwaysConsumeNavBar, null /* displayCutout */);
+                    mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
         }
         return mLastWindowInsets;
     }
@@ -1730,6 +1739,9 @@
                 if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {
                     insetsChanged = true;
                 }
+                if (!mPendingDisplayCutout.equals(mAttachInfo.mDisplayCutout)) {
+                    insetsChanged = true;
+                }
                 if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
                     mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
                     if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
@@ -1906,7 +1918,8 @@
                         + " overscan=" + mPendingOverscanInsets.toShortString()
                         + " content=" + mPendingContentInsets.toShortString()
                         + " visible=" + mPendingVisibleInsets.toShortString()
-                        + " visible=" + mPendingStableInsets.toShortString()
+                        + " stable=" + mPendingStableInsets.toShortString()
+                        + " cutout=" + mPendingDisplayCutout.get().toString()
                         + " outsets=" + mPendingOutsets.toShortString()
                         + " surface=" + mSurface);
 
@@ -1931,6 +1944,8 @@
                         mAttachInfo.mVisibleInsets);
                 final boolean stableInsetsChanged = !mPendingStableInsets.equals(
                         mAttachInfo.mStableInsets);
+                final boolean cutoutChanged = !mPendingDisplayCutout.equals(
+                        mAttachInfo.mDisplayCutout);
                 final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets);
                 final boolean surfaceSizeChanged = (relayoutResult
                         & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
@@ -1955,6 +1970,14 @@
                     // Need to relayout with content insets.
                     contentInsetsChanged = true;
                 }
+                if (cutoutChanged) {
+                    mAttachInfo.mDisplayCutout.set(mPendingDisplayCutout);
+                    if (DEBUG_LAYOUT) {
+                        Log.v(mTag, "DisplayCutout changing to: " + mAttachInfo.mDisplayCutout);
+                    }
+                    // Need to relayout with content insets.
+                    contentInsetsChanged = true;
+                }
                 if (alwaysConsumeNavBarChanged) {
                     mAttachInfo.mAlwaysConsumeNavBar = mPendingAlwaysConsumeNavBar;
                     contentInsetsChanged = true;
@@ -2056,6 +2079,7 @@
                         mResizeMode = freeformResizing
                                 ? RESIZE_MODE_FREEFORM
                                 : RESIZE_MODE_DOCKED_DIVIDER;
+                        // TODO: Need cutout?
                         startDragResizing(mPendingBackDropFrame,
                                 mWinFrame.equals(mPendingBackDropFrame), mPendingVisibleInsets,
                                 mPendingStableInsets, mResizeMode);
@@ -3776,6 +3800,7 @@
                             && mPendingOverscanInsets.equals(args.arg5)
                             && mPendingContentInsets.equals(args.arg2)
                             && mPendingStableInsets.equals(args.arg6)
+                            && mPendingDisplayCutout.get().equals(args.arg9)
                             && mPendingVisibleInsets.equals(args.arg3)
                             && mPendingOutsets.equals(args.arg7)
                             && mPendingBackDropFrame.equals(args.arg8)
@@ -3808,6 +3833,7 @@
                                 || !mPendingOverscanInsets.equals(args.arg5)
                                 || !mPendingContentInsets.equals(args.arg2)
                                 || !mPendingStableInsets.equals(args.arg6)
+                                || !mPendingDisplayCutout.get().equals(args.arg9)
                                 || !mPendingVisibleInsets.equals(args.arg3)
                                 || !mPendingOutsets.equals(args.arg7);
 
@@ -3815,6 +3841,7 @@
                         mPendingOverscanInsets.set((Rect) args.arg5);
                         mPendingContentInsets.set((Rect) args.arg2);
                         mPendingStableInsets.set((Rect) args.arg6);
+                        mPendingDisplayCutout.set((DisplayCutout) args.arg9);
                         mPendingVisibleInsets.set((Rect) args.arg3);
                         mPendingOutsets.set((Rect) args.arg7);
                         mPendingBackDropFrame.set((Rect) args.arg8);
@@ -6258,7 +6285,7 @@
                 (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                 viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                 mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
-                mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame,
+                mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
                 mPendingMergedConfiguration, mSurface);
 
         mPendingAlwaysConsumeNavBar =
@@ -6541,7 +6568,8 @@
     private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
             Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
             MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
-            boolean alwaysConsumeNavBar, int displayId) {
+            boolean alwaysConsumeNavBar, int displayId,
+            DisplayCutout.ParcelableWrapper displayCutout) {
         if (DEBUG_LAYOUT) Log.v(mTag, "Resizing " + this + ": frame=" + frame.toShortString()
                 + " contentInsets=" + contentInsets.toShortString()
                 + " visibleInsets=" + visibleInsets.toShortString()
@@ -6578,6 +6606,7 @@
         args.arg6 = sameProcessCall ? new Rect(stableInsets) : stableInsets;
         args.arg7 = sameProcessCall ? new Rect(outsets) : outsets;
         args.arg8 = sameProcessCall ? new Rect(backDropFrame) : backDropFrame;
+        args.arg9 = displayCutout.get(); // DisplayCutout is immutable.
         args.argi1 = forceLayout ? 1 : 0;
         args.argi2 = alwaysConsumeNavBar ? 1 : 0;
         args.argi3 = displayId;
@@ -7610,12 +7639,13 @@
         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
                 Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
                 MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
-                boolean alwaysConsumeNavBar, int displayId) {
+                boolean alwaysConsumeNavBar, int displayId,
+                DisplayCutout.ParcelableWrapper displayCutout) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
                 viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
                         visibleInsets, stableInsets, outsets, reportDraw, mergedConfiguration,
-                        backDropFrame, forceLayout, alwaysConsumeNavBar, displayId);
+                        backDropFrame, forceLayout, alwaysConsumeNavBar, displayId, displayCutout);
             }
         }
 
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 0375635..35f6acb 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -24,7 +24,6 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -188,7 +187,6 @@
      *
      * @hide
      */
-    @TestApi
     public interface AccessibilityServicesStateChangeListener {
 
         /**
@@ -454,18 +452,6 @@
     }
 
     /**
-     * Returns whether there are observers registered for this event type. If
-     * this method returns false you shuold not generate events of this type
-     * to conserve resources.
-     *
-     * @param type The event type.
-     * @return Whether the event is being observed.
-     */
-    public boolean isObservedEventType(@AccessibilityEvent.EventType int type) {
-        return mIsEnabled && (mRelevantEventTypes & type) != 0;
-    }
-
-    /**
      * Requests feedback interruption from all accessibility services.
      */
     public void interrupt() {
@@ -697,7 +683,6 @@
      *                for a callback on the process's main handler.
      * @hide
      */
-    @TestApi
     public void addAccessibilityServicesStateChangeListener(
             @NonNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler) {
         synchronized (mLock) {
@@ -713,7 +698,6 @@
      *
      * @hide
      */
-    @TestApi
     public void removeAccessibilityServicesStateChangeListener(
             @NonNull AccessibilityServicesStateChangeListener listener) {
         // Final CopyOnWriteArrayList - no lock needed.
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 9a99e53..419aeb3 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -47,6 +47,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.Preconditions;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -428,7 +429,7 @@
      * @hide
      */
     public AutofillManager(Context context, IAutoFillManager service) {
-        mContext = context;
+        mContext = Preconditions.checkNotNull(context, "context cannot be null");
         mService = service;
     }
 
@@ -457,7 +458,7 @@
             if (mSessionId != NO_SESSION) {
                 ensureServiceClientAddedIfNeededLocked();
 
-                final AutofillClient client = getClientLocked();
+                final AutofillClient client = getClient();
                 if (client != null) {
                     try {
                         final boolean sessionWasRestored = mService.restoreSession(mSessionId,
@@ -1057,6 +1058,23 @@
     }
 
     /**
+     * TODO(b/67867469):
+     * - proper javadoc
+     * - mention this method in other places
+     * - unhide / remove testApi
+     * @hide
+     */
+    @TestApi
+    public boolean isFieldClassificationEnabled() {
+        try {
+            return mService.isFieldClassificationEnabled();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
+
+    /**
      * Returns {@code true} if autofill is supported by the current device and
      * is supported for this user.
      *
@@ -1076,7 +1094,8 @@
         }
     }
 
-    private AutofillClient getClientLocked() {
+    // Note: don't need to use locked suffix because mContext is final.
+    private AutofillClient getClient() {
         final AutofillClient client = mContext.getAutofillClient();
         if (client == null && sDebug) {
             Log.d(TAG, "No AutofillClient for " + mContext.getPackageName() + " on context "
@@ -1139,16 +1158,16 @@
             return;
         }
         try {
-            final AutofillClient client = getClientLocked();
+            final AutofillClient client = getClient();
+            if (client == null) return; // NOTE: getClient() already logd it..
+
             mSessionId = mService.startSession(mContext.getActivityToken(),
                     mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
                     mCallback != null, flags, client.getComponentName());
             if (mSessionId != NO_SESSION) {
                 mState = STATE_ACTIVE;
             }
-            if (client != null) {
-                client.autofillCallbackResetableStateAvailable();
-            }
+            client.autofillCallbackResetableStateAvailable();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1200,7 +1219,9 @@
 
         try {
             if (restartIfNecessary) {
-                final AutofillClient client = getClientLocked();
+                final AutofillClient client = getClient();
+                if (client == null) return; // NOTE: getClient() already logd it..
+
                 final int newId = mService.updateOrRestartSession(mContext.getActivityToken(),
                         mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
                         mCallback != null, flags, client.getComponentName(), mSessionId, action);
@@ -1208,9 +1229,7 @@
                     if (sDebug) Log.d(TAG, "Session restarted: " + mSessionId + "=>" + newId);
                     mSessionId = newId;
                     mState = (mSessionId == NO_SESSION) ? STATE_UNKNOWN : STATE_ACTIVE;
-                    if (client != null) {
-                        client.autofillCallbackResetableStateAvailable();
-                    }
+                    client.autofillCallbackResetableStateAvailable();
                 }
             } else {
                 mService.updateSession(mSessionId, id, bounds, value, action, flags,
@@ -1223,7 +1242,7 @@
     }
 
     private void ensureServiceClientAddedIfNeededLocked() {
-        if (getClientLocked() == null) {
+        if (getClient() == null) {
             return;
         }
 
@@ -1306,7 +1325,7 @@
         AutofillCallback callback = null;
         synchronized (mLock) {
             if (mSessionId == sessionId) {
-                AutofillClient client = getClientLocked();
+                AutofillClient client = getClient();
 
                 if (client != null) {
                     if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
@@ -1331,7 +1350,7 @@
             Intent fillInIntent) {
         synchronized (mLock) {
             if (sessionId == mSessionId) {
-                AutofillClient client = getClientLocked();
+                final AutofillClient client = getClient();
                 if (client != null) {
                     client.autofillCallbackAuthenticate(authenticationId, intent, fillInIntent);
                 }
@@ -1396,7 +1415,7 @@
                 return;
             }
 
-            final AutofillClient client = getClientLocked();
+            final AutofillClient client = getClient();
             if (client == null) {
                 return;
             }
@@ -1573,7 +1592,7 @@
             // 1. If local and remote session id are off sync the UI would be stuck shown
             // 2. There is a race between the user state being destroyed due the fill
             //    service being uninstalled and the UI being dismissed.
-            AutofillClient client = getClientLocked();
+            AutofillClient client = getClient();
             if (client != null) {
                 if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
                     callback = mCallback;
@@ -1603,7 +1622,7 @@
 
         AutofillCallback callback = null;
         synchronized (mLock) {
-            if (mSessionId == sessionId && getClientLocked() != null) {
+            if (mSessionId == sessionId && getClient() != null) {
                 callback = mCallback;
             }
         }
@@ -1660,7 +1679,7 @@
      * @return The view or {@code null} if view was not found
      */
     private View findView(@NonNull AutofillId autofillId) {
-        final AutofillClient client = getClientLocked();
+        final AutofillClient client = getClient();
 
         if (client == null) {
             return null;
@@ -1694,7 +1713,7 @@
         pw.print(pfx); pw.print("sessionId: "); pw.println(mSessionId);
         pw.print(pfx); pw.print("state: "); pw.println(getStateAsStringLocked());
         pw.print(pfx); pw.print("context: "); pw.println(mContext);
-        pw.print(pfx); pw.print("client: "); pw.println(getClientLocked());
+        pw.print(pfx); pw.print("client: "); pw.println(getClient());
         pw.print(pfx); pw.print("enabled: "); pw.println(mEnabled);
         pw.print(pfx); pw.print("hasService: "); pw.println(mService != null);
         pw.print(pfx); pw.print("hasCallback: "); pw.println(mCallback != null);
@@ -1741,7 +1760,7 @@
     }
 
     private void post(Runnable runnable) {
-        final AutofillClient client = getClientLocked();
+        final AutofillClient client = getClient();
         if (client == null) {
             if (sVerbose) Log.v(TAG, "ignoring post() because client is null");
             return;
@@ -1824,7 +1843,7 @@
          * @param trackedIds The views to be tracked
          */
         TrackedViews(@Nullable AutofillId[] trackedIds) {
-            final AutofillClient client = getClientLocked();
+            final AutofillClient client = getClient();
             if (trackedIds != null && client != null) {
                 final boolean[] isVisible;
 
@@ -1865,7 +1884,7 @@
          * @param isVisible visible if the view is visible in the view hierarchy.
          */
         void notifyViewVisibilityChanged(@NonNull AutofillId id, boolean isVisible) {
-            AutofillClient client = getClientLocked();
+            AutofillClient client = getClient();
 
             if (sDebug) {
                 Log.d(TAG, "notifyViewVisibilityChanged(): id=" + id + " isVisible="
@@ -1902,7 +1921,7 @@
         void onVisibleForAutofillLocked() {
             // The visibility of the views might have changed while the client was not be visible,
             // hence update the visibility state for all views.
-            AutofillClient client = getClientLocked();
+            AutofillClient client = getClient();
             ArraySet<AutofillId> updatedVisibleTrackedIds = null;
             ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
             if (client != null) {
diff --git a/core/java/android/view/autofill/Helper.java b/core/java/android/view/autofill/Helper.java
index b95704a..4b2c53c 100644
--- a/core/java/android/view/autofill/Helper.java
+++ b/core/java/android/view/autofill/Helper.java
@@ -18,11 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.Bundle;
-
-import java.util.Arrays;
-import java.util.Objects;
-import java.util.Set;
 
 /** @hide */
 public final class Helper {
@@ -31,37 +26,20 @@
     public static boolean sDebug = false;
     public static boolean sVerbose = false;
 
-    public static final String REDACTED = "[REDACTED]";
-
-    static StringBuilder append(StringBuilder builder, Bundle bundle) {
-        if (bundle == null || !sDebug) {
-            builder.append("N/A");
-        } else if (!sVerbose) {
-            builder.append(REDACTED);
-        } else {
-            final Set<String> keySet = bundle.keySet();
-            builder.append("[Bundle with ").append(keySet.size()).append(" extras:");
-            for (String key : keySet) {
-                final Object value = bundle.get(key);
-                builder.append(' ').append(key).append('=');
-                builder.append((value instanceof Object[])
-                        ? Arrays.toString((Objects[]) value) : value);
-            }
-            builder.append(']');
-        }
-        return builder;
-    }
-
     /**
      * Appends {@code value} to the {@code builder} redacting its contents.
      */
     public static void appendRedacted(@NonNull StringBuilder builder,
             @Nullable CharSequence value) {
-        if (value == null) {
-            builder.append("null");
-        } else {
-            builder.append(value.length()).append("_chars");
-        }
+        builder.append(getRedacted(value));
+    }
+
+    /**
+     * Gets the redacted version of a value.
+     */
+    @NonNull
+    public static String getRedacted(@Nullable CharSequence value) {
+        return (value == null) ? "null" : value.length() + "_chars";
     }
 
     /**
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 7d6a19f..f49aa5b 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -56,4 +56,5 @@
     void onPendingSaveUi(int operation, IBinder token);
     UserData getUserData();
     void setUserData(in UserData userData);
+    boolean isFieldClassificationEnabled();
 }
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index f0645b8..c69543f 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -67,6 +67,11 @@
     final ResolveInfo mService;
 
     /**
+     * IME only supports VR mode.
+     */
+    final boolean mIsVrOnly;
+
+    /**
      * The unique string Id to identify the input method.  This is generated
      * from the input method component.
      */
@@ -149,6 +154,7 @@
 
         PackageManager pm = context.getPackageManager();
         String settingsActivityComponent = null;
+        boolean isVrOnly;
         int isDefaultResId = 0;
 
         XmlResourceParser parser = null;
@@ -179,6 +185,7 @@
                     com.android.internal.R.styleable.InputMethod);
             settingsActivityComponent = sa.getString(
                     com.android.internal.R.styleable.InputMethod_settingsActivity);
+            isVrOnly = sa.getBoolean(com.android.internal.R.styleable.InputMethod_isVrOnly, false);
             isDefaultResId = sa.getResourceId(
                     com.android.internal.R.styleable.InputMethod_isDefault, 0);
             supportsSwitchingToNextInputMethod = sa.getBoolean(
@@ -254,6 +261,8 @@
         mIsDefaultResId = isDefaultResId;
         mIsAuxIme = isAuxIme;
         mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
+        // TODO(b/68948291): remove this meta-data before release.
+        mIsVrOnly = isVrOnly || service.serviceInfo.metaData.getBoolean("isVrOnly", false);
     }
 
     InputMethodInfo(Parcel source) {
@@ -262,6 +271,7 @@
         mIsDefaultResId = source.readInt();
         mIsAuxIme = source.readInt() == 1;
         mSupportsSwitchingToNextInputMethod = source.readInt() == 1;
+        mIsVrOnly = source.readBoolean();
         mService = ResolveInfo.CREATOR.createFromParcel(source);
         mSubtypes = new InputMethodSubtypeArray(source);
         mForceDefault = false;
@@ -274,7 +284,8 @@
             CharSequence label, String settingsActivity) {
         this(buildDummyResolveInfo(packageName, className, label), false /* isAuxIme */,
                 settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
-                false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */);
+                false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
+                false /* isVrOnly */);
     }
 
     /**
@@ -285,7 +296,7 @@
             String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
             boolean forceDefault) {
         this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
-                true /* supportsSwitchingToNextInputMethod */);
+                true /* supportsSwitchingToNextInputMethod */, false /* isVrOnly */);
     }
 
     /**
@@ -294,7 +305,7 @@
      */
     public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
             List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
-            boolean supportsSwitchingToNextInputMethod) {
+            boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) {
         final ServiceInfo si = ri.serviceInfo;
         mService = ri;
         mId = new ComponentName(si.packageName, si.name).flattenToShortString();
@@ -304,6 +315,7 @@
         mSubtypes = new InputMethodSubtypeArray(subtypes);
         mForceDefault = forceDefault;
         mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
+        mIsVrOnly = isVrOnly;
     }
 
     private static ResolveInfo buildDummyResolveInfo(String packageName, String className,
@@ -398,6 +410,14 @@
     }
 
     /**
+     * Returns true if IME supports VR mode only.
+     * @hide
+     */
+    public boolean isVrOnly() {
+        return mIsVrOnly;
+    }
+
+    /**
      * Return the count of the subtypes of Input Method.
      */
     public int getSubtypeCount() {
@@ -444,6 +464,7 @@
     public void dump(Printer pw, String prefix) {
         pw.println(prefix + "mId=" + mId
                 + " mSettingsActivityName=" + mSettingsActivityName
+                + " mIsVrOnly=" + mIsVrOnly
                 + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod);
         pw.println(prefix + "mIsDefaultResId=0x"
                 + Integer.toHexString(mIsDefaultResId));
@@ -509,6 +530,7 @@
         dest.writeInt(mIsDefaultResId);
         dest.writeInt(mIsAuxIme ? 1 : 0);
         dest.writeInt(mSupportsSwitchingToNextInputMethod ? 1 : 0);
+        dest.writeBoolean(mIsVrOnly);
         mService.writeToParcel(dest, flags);
         mSubtypes.writeToParcel(dest);
     }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 92d1de8..4d96733 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -697,6 +697,19 @@
         }
     }
 
+    /**
+     * Returns a list of VR InputMethod currently installed.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
+    public List<InputMethodInfo> getVrInputMethodList() {
+        try {
+            return mService.getVrInputMethodList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     public List<InputMethodInfo> getEnabledInputMethodList() {
         try {
             return mService.getEnabledInputMethodList();
diff --git a/core/java/android/view/inputmethod/InputMethodManagerInternal.java b/core/java/android/view/inputmethod/InputMethodManagerInternal.java
index 77df4e3..e13813e 100644
--- a/core/java/android/view/inputmethod/InputMethodManagerInternal.java
+++ b/core/java/android/view/inputmethod/InputMethodManagerInternal.java
@@ -16,6 +16,8 @@
 
 package android.view.inputmethod;
 
+import android.content.ComponentName;
+
 /**
  * Input method manager local system service interface.
  *
@@ -37,4 +39,9 @@
      * Hides the current input method, if visible.
      */
     void hideCurrentInputMethod();
+
+    /**
+     * Switches to VR InputMethod defined in the packageName of {@param componentName}.
+     */
+    void startVrInputMethodNoCheck(ComponentName componentName);
 }
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index b2cab5b..7ffbf63 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -109,8 +109,7 @@
     @NonNull private final List<Intent> mSecondaryIntents;
     @NonNull private final List<OnClickListener> mSecondaryOnClickListeners;
     @NonNull private final EntityConfidence<String> mEntityConfidence;
-    private int mLogType;
-    @NonNull private final String mVersionInfo;
+    @NonNull private final String mSignature;
 
     private TextClassification(
             @Nullable String text,
@@ -123,8 +122,7 @@
             @NonNull List<Intent> secondaryIntents,
             @NonNull List<OnClickListener> secondaryOnClickListeners,
             @NonNull Map<String, Float> entityConfidence,
-            int logType,
-            @NonNull String versionInfo) {
+            @NonNull String signature) {
         Preconditions.checkArgument(secondaryLabels.size() == secondaryIntents.size());
         Preconditions.checkArgument(secondaryIcons.size() == secondaryIntents.size());
         Preconditions.checkArgument(secondaryOnClickListeners.size() == secondaryIntents.size());
@@ -138,8 +136,7 @@
         mSecondaryIntents = secondaryIntents;
         mSecondaryOnClickListeners = secondaryOnClickListeners;
         mEntityConfidence = new EntityConfidence<>(entityConfidence);
-        mLogType = logType;
-        mVersionInfo = versionInfo;
+        mSignature = signature;
     }
 
     /**
@@ -315,30 +312,26 @@
     }
 
     /**
-     * Returns the MetricsLogger subtype for the action that is performed for this result.
-     * @hide
-     */
-    public int getLogType() {
-        return mLogType;
-    }
-
-    /**
-     * Returns information about the classifier model used to generate this TextClassification.
-     * @hide
+     * Returns the signature for this object.
+     * The TextClassifier that generates this object may use it as a way to internally identify
+     * this object.
      */
     @NonNull
-    public String getVersionInfo() {
-        return mVersionInfo;
+    public String getSignature() {
+        return mSignature;
     }
 
     @Override
     public String toString() {
-        return String.format("TextClassification {"
+        return String.format(Locale.US, "TextClassification {"
                         + "text=%s, entities=%s, "
                         + "primaryLabel=%s, secondaryLabels=%s, "
-                        + "primaryIntent=%s, secondaryIntents=%s}",
+                        + "primaryIntent=%s, secondaryIntents=%s, "
+                        + "signature=%s}",
                 mText, mEntityConfidence,
-                mPrimaryLabel, mSecondaryLabels, mPrimaryIntent, mSecondaryIntents);
+                mPrimaryLabel, mSecondaryLabels,
+                mPrimaryIntent, mSecondaryIntents,
+                mSignature);
     }
 
     /**
@@ -383,8 +376,7 @@
         @Nullable String mPrimaryLabel;
         @Nullable Intent mPrimaryIntent;
         @Nullable OnClickListener mPrimaryOnClickListener;
-        private int mLogType;
-        @NonNull private String mVersionInfo = "";
+        @NonNull private String mSignature = "";
 
         /**
          * Sets the classified text.
@@ -508,20 +500,12 @@
         }
 
         /**
-         * Sets the MetricsLogger subtype for the action that is performed for this result.
-         * @hide
+         * Sets a signature for the TextClassification object.
+         * The TextClassifier that generates the TextClassification object may use it as a way to
+         * internally identify the TextClassification object.
          */
-        public Builder setLogType(int type) {
-            mLogType = type;
-            return this;
-        }
-
-        /**
-         * Sets information about the classifier model used to generate this TextClassification.
-         * @hide
-         */
-        Builder setVersionInfo(@NonNull String versionInfo) {
-            mVersionInfo = Preconditions.checkNotNull(versionInfo);
+        public Builder setSignature(@NonNull String signature) {
+            mSignature = Preconditions.checkNotNull(signature);
             return this;
         }
 
@@ -535,7 +519,7 @@
                     mPrimaryIntent, mPrimaryOnClickListener,
                     mSecondaryIcons, mSecondaryLabels,
                     mSecondaryIntents, mSecondaryOnClickListeners,
-                    mEntityConfidence, mLogType, mVersionInfo);
+                    mEntityConfidence, mSignature);
         }
     }
 
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 5aaa5ad..f4cbc54 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -37,7 +37,7 @@
 public interface TextClassifier {
 
     /** @hide */
-    String DEFAULT_LOG_TAG = "TextClassifierImpl";
+    String DEFAULT_LOG_TAG = "androidtc";
 
     String TYPE_UNKNOWN = "";
     String TYPE_OTHER = "other";
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index d8ea89a..6cf6b69 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -33,7 +33,6 @@
 import android.text.util.Linkify;
 import android.util.Patterns;
 import android.view.View.OnClickListener;
-import android.widget.TextViewMetrics;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
@@ -122,8 +121,8 @@
                         tsBuilder.setEntityType(results[i].mCollection, results[i].mScore);
                     }
                     return tsBuilder
-                            .setLogSource(LOG_TAG)
-                            .setVersionInfo(getVersionInfo())
+                            .setSignature(
+                                    getSignature(string, selectionStartIndex, selectionEndIndex))
                             .build();
                 } else {
                     // We can not trust the result. Log the issue and ignore the result.
@@ -155,8 +154,7 @@
                                 getHintFlags(string, startIndex, endIndex));
                 if (results.length > 0) {
                     final TextClassification classificationResult =
-                            createClassificationResult(
-                                    results, string.subSequence(startIndex, endIndex));
+                            createClassificationResult(results, string, startIndex, endIndex);
                     return classificationResult;
                 }
             }
@@ -230,13 +228,13 @@
         }
     }
 
-    @NonNull
-    private String getVersionInfo() {
+    private String getSignature(String text, int start, int end) {
         synchronized (mSmartSelectionLock) {
-            if (mLocale != null) {
-                return String.format("%s_v%d", mLocale.toLanguageTag(), mVersion);
-            }
-            return "";
+            final String versionInfo = (mLocale != null)
+                    ? String.format(Locale.US, "%s_v%d", mLocale.toLanguageTag(), mVersion)
+                    : "";
+            final int hash = Objects.hash(text, start, end, mContext.getPackageName());
+            return String.format(Locale.US, "%s|%s|%d", LOG_TAG, versionInfo, hash);
         }
     }
 
@@ -372,9 +370,11 @@
     }
 
     private TextClassification createClassificationResult(
-            SmartSelection.ClassificationResult[] classifications, CharSequence text) {
+            SmartSelection.ClassificationResult[] classifications,
+            String text, int start, int end) {
+        final String classifiedText = text.substring(start, end);
         final TextClassification.Builder builder = new TextClassification.Builder()
-                .setText(text.toString());
+                .setText(classifiedText);
 
         final int size = classifications.length;
         for (int i = 0; i < size; i++) {
@@ -382,11 +382,9 @@
         }
 
         final String type = getHighestScoringType(classifications);
-        builder.setLogType(IntentFactory.getLogType(type));
+        addActions(builder, IntentFactory.create(mContext, type, text));
 
-        addActions(builder, IntentFactory.create(mContext, type, text.toString()));
-
-        return builder.setVersionInfo(getVersionInfo()).build();
+        return builder.setSignature(getSignature(text, start, end)).build();
     }
 
     /** Extends the classification with the intents that can be resolved. */
@@ -564,22 +562,5 @@
                     return null;
             }
         }
-
-        @Nullable
-        public static int getLogType(String type) {
-            type = type.trim().toLowerCase(Locale.ENGLISH);
-            switch (type) {
-                case TextClassifier.TYPE_EMAIL:
-                    return TextViewMetrics.SUBTYPE_ASSIST_MENU_ITEM_EMAIL;
-                case TextClassifier.TYPE_PHONE:
-                    return TextViewMetrics.SUBTYPE_ASSIST_MENU_ITEM_PHONE;
-                case TextClassifier.TYPE_ADDRESS:
-                    return TextViewMetrics.SUBTYPE_ASSIST_MENU_ITEM_ADDRESS;
-                case TextClassifier.TYPE_URL:
-                    return TextViewMetrics.SUBTYPE_ASSIST_MENU_ITEM_URL;
-                default:
-                    return TextViewMetrics.SUBTYPE_ASSIST_MENU_ITEM_OTHER;
-            }
-        }
     }
 }
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index ced4018..25e9e7e 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -37,17 +37,15 @@
     private final int mStartIndex;
     private final int mEndIndex;
     @NonNull private final EntityConfidence<String> mEntityConfidence;
-    @NonNull private final String mLogSource;
-    @NonNull private final String mVersionInfo;
+    @NonNull private final String mSignature;
 
     private TextSelection(
-            int startIndex, int endIndex, @NonNull EntityConfidence<String> entityConfidence,
-            @NonNull String logSource, @NonNull String versionInfo) {
+            int startIndex, int endIndex, @NonNull Map<String, Float> entityConfidence,
+            @NonNull String signature) {
         mStartIndex = startIndex;
         mEndIndex = endIndex;
         mEntityConfidence = new EntityConfidence<>(entityConfidence);
-        mLogSource = logSource;
-        mVersionInfo = versionInfo;
+        mSignature = signature;
     }
 
     /**
@@ -95,27 +93,21 @@
     }
 
     /**
-     * Returns a tag for the source classifier used to generate this result.
-     * @hide
+     * Returns the signature for this object.
+     * The TextClassifier that generates this object may use it as a way to internally identify
+     * this object.
      */
     @NonNull
-    public String getSourceClassifier() {
-        return mLogSource;
-    }
-
-    /**
-     * Returns information about the classifier model used to generate this TextSelection.
-     * @hide
-     */
-    @NonNull
-    public String getVersionInfo() {
-        return mVersionInfo;
+    public String getSignature() {
+        return mSignature;
     }
 
     @Override
     public String toString() {
-        return String.format(Locale.US,
-                "TextSelection {%d, %d, %s}", mStartIndex, mEndIndex, mEntityConfidence);
+        return String.format(
+                Locale.US,
+                "TextSelection {startIndex=%d, endIndex=%d, entities=%s, signature=%s}",
+                mStartIndex, mEndIndex, mEntityConfidence, mSignature);
     }
 
     /**
@@ -126,8 +118,7 @@
         private final int mStartIndex;
         private final int mEndIndex;
         @NonNull private final Map<String, Float> mEntityConfidence = new ArrayMap<>();
-        @NonNull private String mLogSource = "";
-        @NonNull private String mVersionInfo = "";
+        @NonNull private String mSignature = "";
 
         /**
          * Creates a builder used to build {@link TextSelection} objects.
@@ -157,20 +148,13 @@
         }
 
         /**
-         * Sets a tag for the source classifier used to generate this result.
-         * @hide
+         * Sets a signature for the TextSelection object.
+         *
+         * The TextClassifier that generates the TextSelection object may use it as a way to
+         * internally identify the TextSelection object.
          */
-        Builder setLogSource(@NonNull String logSource) {
-            mLogSource = Preconditions.checkNotNull(logSource);
-            return this;
-        }
-
-        /**
-         * Sets information about the classifier model used to generate this TextSelection.
-         * @hide
-         */
-        Builder setVersionInfo(@NonNull String versionInfo) {
-            mVersionInfo = Preconditions.checkNotNull(versionInfo);
+        public Builder setSignature(@NonNull String signature) {
+            mSignature = Preconditions.checkNotNull(signature);
             return this;
         }
 
@@ -179,8 +163,7 @@
          */
         public TextSelection build() {
             return new TextSelection(
-                    mStartIndex, mEndIndex, new EntityConfidence<>(mEntityConfidence),  mLogSource,
-                    mVersionInfo);
+                    mStartIndex, mEndIndex, mEntityConfidence, mSignature);
         }
     }
 
diff --git a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
index 2833564..157b3d8 100644
--- a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
+++ b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
@@ -473,7 +473,7 @@
             final String entityType = classification.getEntityCount() > 0
                     ? classification.getEntity(0)
                     : TextClassifier.TYPE_UNKNOWN;
-            final String versionTag = classification.getVersionInfo();
+            final String versionTag = getVersionInfo(classification.getSignature());
             return new SelectionEvent(
                     start, end, EventType.SELECTION_MODIFIED, entityType, versionTag);
         }
@@ -489,7 +489,7 @@
          */
         public static SelectionEvent selectionModified(
                 int start, int end, @NonNull TextSelection selection) {
-            final boolean smartSelection = selection.getSourceClassifier()
+            final boolean smartSelection = getSourceClassifier(selection.getSignature())
                     .equals(TextClassifier.DEFAULT_LOG_TAG);
             final int eventType;
             if (smartSelection) {
@@ -503,7 +503,7 @@
             final String entityType = selection.getEntityCount() > 0
                     ? selection.getEntity(0)
                     : TextClassifier.TYPE_UNKNOWN;
-            final String versionTag = selection.getVersionInfo();
+            final String versionTag = getVersionInfo(selection.getSignature());
             return new SelectionEvent(start, end, eventType, entityType, versionTag);
         }
 
@@ -538,26 +538,25 @@
             final String entityType = classification.getEntityCount() > 0
                     ? classification.getEntity(0)
                     : TextClassifier.TYPE_UNKNOWN;
-            final String versionTag = classification.getVersionInfo();
+            final String versionTag = getVersionInfo(classification.getSignature());
             return new SelectionEvent(start, end, actionType, entityType, versionTag);
         }
 
-        private boolean isActionType() {
-            switch (mEventType) {
-                case ActionType.OVERTYPE:  // fall through
-                case ActionType.COPY:  // fall through
-                case ActionType.PASTE:  // fall through
-                case ActionType.CUT:  // fall through
-                case ActionType.SHARE:  // fall through
-                case ActionType.SMART_SHARE:  // fall through
-                case ActionType.DRAG:  // fall through
-                case ActionType.ABANDON:  // fall through
-                case ActionType.SELECT_ALL:  // fall through
-                case ActionType.RESET:  // fall through
-                    return true;
-                default:
-                    return false;
+        private static String getVersionInfo(String signature) {
+            final int start = signature.indexOf("|");
+            final int end = signature.indexOf("|", start);
+            if (start >= 0 && end >= start) {
+                return signature.substring(start, end);
             }
+            return "";
+        }
+
+        private static String getSourceClassifier(String signature) {
+            final int end = signature.indexOf("|");
+            if (end >= 0) {
+                return signature.substring(0, end);
+            }
+            return "";
         }
 
         private boolean isTerminal() {
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 797bdfb..9db0e8d 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -265,10 +265,10 @@
                     + "packageName mismatch, expected: "
                     + chosen.packageName + " actual: " + toUse.packageName);
         }
-        if (chosen.versionCode > toUse.versionCode) {
+        if (chosen.getLongVersionCode() > toUse.getLongVersionCode()) {
             throw new MissingWebViewPackageException("Failed to verify WebView provider, "
-                    + "version code is lower than expected: " + chosen.versionCode
-                    + " actual: " + toUse.versionCode);
+                    + "version code is lower than expected: " + chosen.getLongVersionCode()
+                    + " actual: " + toUse.getLongVersionCode());
         }
         if (getWebViewLibrary(toUse.applicationInfo) == null) {
             throw new MissingWebViewPackageException("Tried to load an invalid WebView provider: "
@@ -401,7 +401,7 @@
                 Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
             }
             Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
-                    sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
+                    sPackageInfo.versionName + " (code " + sPackageInfo.getLongVersionCode() + ")");
 
             Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
             try {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index d4bac98..df97112 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -41,7 +41,6 @@
 import android.graphics.RectF;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.metrics.LogMaker;
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
@@ -3997,10 +3996,6 @@
                         .setIntent(textClassification.getIntent());
                 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
                 mAssistClickHandlers.put(item, textClassification.getOnClickListener());
-                mMetricsLogger.write(
-                        new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST)
-                                .setType(MetricsEvent.TYPE_OPEN)
-                                .setSubtype(textClassification.getLogType()));
             }
             final int count = textClassification.getSecondaryActionsCount();
             for (int i = 0; i < count; i++) {
@@ -4082,11 +4077,6 @@
             if (onClickListener != null) {
                 onClickListener.onClick(mTextView);
                 stopTextActionMode();
-                if (assistMenuItem.getItemId() == TextView.ID_ASSIST) {
-                    mMetricsLogger.action(
-                            MetricsEvent.ACTION_TEXT_SELECTION_MENU_ITEM_ASSIST,
-                            textClassification.getLogType());
-                }
             }
             // We tried our best.
             return true;
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index b379280..4d3189e 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1952,8 +1952,7 @@
             CharSequence beforeText = mInputText.getText();
             if (!text.equals(beforeText.toString())) {
                 mInputText.setText(text);
-                if (AccessibilityManager.getInstance(mContext).isObservedEventType(
-                        AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)) {
+                if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
                     mInputText.onInitializeAccessibilityEvent(event);
@@ -2613,7 +2612,7 @@
         }
 
         private void sendAccessibilityEventForVirtualText(int eventType) {
-            if (AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                 AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
                 mInputText.onInitializeAccessibilityEvent(event);
                 mInputText.onPopulateAccessibilityEvent(event);
@@ -2624,7 +2623,7 @@
 
         private void sendAccessibilityEventForVirtualButton(int virtualViewId, int eventType,
                 String text) {
-            if (AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                 AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
                 event.setClassName(Button.class.getName());
                 event.setPackageName(mContext.getPackageName());
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 1279040..7156300 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -570,11 +570,12 @@
                 mFormatChangeObserver = new FormatChangeObserver(getHandler());
             }
             final ContentResolver resolver = getContext().getContentResolver();
+            Uri uri = Settings.System.getUriFor(Settings.System.TIME_12_24);
             if (mShowCurrentUserTime) {
-                resolver.registerContentObserver(Settings.System.CONTENT_URI, true,
+                resolver.registerContentObserver(uri, true,
                         mFormatChangeObserver, UserHandle.USER_ALL);
             } else {
-                resolver.registerContentObserver(Settings.System.CONTENT_URI, true,
+                resolver.registerContentObserver(uri, true,
                         mFormatChangeObserver);
             }
         }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 71532a7..d9bc51f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10836,10 +10836,6 @@
 
     void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText,
             int fromIndex, int removedCount, int addedCount) {
-        if (!AccessibilityManager.getInstance(mContext).isObservedEventType(
-                AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)) {
-            return;
-        }
         AccessibilityEvent event =
                 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
         event.setFromIndex(fromIndex);
diff --git a/core/java/android/widget/TextViewMetrics.java b/core/java/android/widget/TextViewMetrics.java
index 96d1794..738a574 100644
--- a/core/java/android/widget/TextViewMetrics.java
+++ b/core/java/android/widget/TextViewMetrics.java
@@ -37,29 +37,4 @@
      * Long press on TextView - drag and drop started.
      */
     public static final int SUBTYPE_LONG_PRESS_DRAG_AND_DROP = 2;
-
-    /**
-     * Assist menu item (shown or clicked) - classification: other.
-     */
-    public static final int SUBTYPE_ASSIST_MENU_ITEM_OTHER = 0;
-
-    /**
-     * Assist menu item (shown or clicked) - classification: email.
-     */
-    public static final int SUBTYPE_ASSIST_MENU_ITEM_EMAIL = 1;
-
-    /**
-     * Assist menu item (shown or clicked) - classification: phone.
-     */
-    public static final int SUBTYPE_ASSIST_MENU_ITEM_PHONE = 2;
-
-    /**
-     * Assist menu item (shown or clicked) - classification: address.
-     */
-    public static final int SUBTYPE_ASSIST_MENU_ITEM_ADDRESS = 3;
-
-    /**
-     * Assist menu item (shown or clicked) - classification: url.
-     */
-    public static final int SUBTYPE_ASSIST_MENU_ITEM_URL = 4;
 }
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index bfde6ac..d807120 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -504,8 +504,7 @@
         private void trySendAccessibilityEvent() {
             AccessibilityManager accessibilityManager =
                     AccessibilityManager.getInstance(mView.getContext());
-            if (!accessibilityManager.isObservedEventType(
-                    AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)) {
+            if (!accessibilityManager.isEnabled()) {
                 return;
             }
             // treat toasts as notifications since they are used to
diff --git a/services/core/java/com/android/server/policy/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
similarity index 62%
rename from services/core/java/com/android/server/policy/AccessibilityShortcutController.java
rename to core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 55c582e..293471c 100644
--- a/services/core/java/com/android/server/policy/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Google Inc.
+ * Copyright 2017 Google Inc.
  *
  * 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
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.server.policy;
+package com.android.internal.accessibility;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.ActivityManager;
@@ -35,6 +35,7 @@
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Slog;
 import android.view.Window;
 import android.view.WindowManager;
@@ -43,20 +44,30 @@
 import android.widget.Toast;
 import com.android.internal.R;
 
-import java.util.List;
+import java.util.Collections;
+import java.util.Map;
 
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
 
+import static com.android.internal.util.ArrayUtils.convertToLongArray;
+
 /**
  * Class to help manage the accessibility shortcut
  */
 public class AccessibilityShortcutController {
     private static final String TAG = "AccessibilityShortcutController";
+
+    // Dummy component names for framework features
+    public static final ComponentName COLOR_INVERSION_COMPONENT_NAME =
+            new ComponentName("com.android.server.accessibility", "ColorInversion");
+    public static final ComponentName DALTONIZER_COMPONENT_NAME =
+            new ComponentName("com.android.server.accessibility", "Daltonizer");
+
     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
             .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY)
             .build();
-
+    private static Map<ComponentName, ToggleableFrameworkFeatureInfo> sFrameworkShortcutFeaturesMap;
 
     private final Context mContext;
     private AlertDialog mAlertDialog;
@@ -67,6 +78,15 @@
     // Visible for testing
     public FrameworkObjectProvider mFrameworkObjectProvider = new FrameworkObjectProvider();
 
+    /**
+     * Get the component name string for the service or feature currently assigned to the
+     * accessiblity shortcut
+     *
+     * @param context A valid context
+     * @param userId The user ID of interest
+     * @return The flattened component name string of the service selected by the user, or the
+     *         string for the default service if the user has not made a selection
+     */
     public static String getTargetServiceComponentNameString(
             Context context, int userId) {
         final String currentShortcutServiceId = Settings.Secure.getStringForUser(
@@ -78,6 +98,29 @@
         return context.getString(R.string.config_defaultAccessibilityService);
     }
 
+    /**
+     * @return An immutable map from dummy component names to feature info for toggling a framework
+     *         feature
+     */
+    public static Map<ComponentName, ToggleableFrameworkFeatureInfo>
+        getFrameworkShortcutFeaturesMap() {
+        if (sFrameworkShortcutFeaturesMap == null) {
+            Map<ComponentName, ToggleableFrameworkFeatureInfo> featuresMap = new ArrayMap<>(2);
+            featuresMap.put(COLOR_INVERSION_COMPONENT_NAME,
+                    new ToggleableFrameworkFeatureInfo(
+                            Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
+                            "1" /* Value to enable */, "0" /* Value to disable */,
+                            R.string.color_inversion_feature_name));
+            featuresMap.put(DALTONIZER_COMPONENT_NAME,
+                    new ToggleableFrameworkFeatureInfo(
+                            Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
+                            "1" /* Value to enable */, "0" /* Value to disable */,
+                            R.string.color_correction_feature_name));
+            sFrameworkShortcutFeaturesMap = Collections.unmodifiableMap(featuresMap);
+        }
+        return sFrameworkShortcutFeaturesMap;
+    }
+
     public AccessibilityShortcutController(Context context, Handler handler, int initialUserId) {
         mContext = context;
         mUserId = initialUserId;
@@ -160,8 +203,8 @@
         if ((vibrator != null) && vibrator.hasVibrator()) {
             // Don't check if haptics are disabled, as we need to alert the user that their
             // way of interacting with the phone may change if they activate the shortcut
-            long[] vibePattern = PhoneWindowManager.getLongIntArray(mContext.getResources(),
-                    R.array.config_longPressVibePattern);
+            long[] vibePattern = convertToLongArray(
+                    mContext.getResources().getIntArray(R.array.config_longPressVibePattern));
             vibrator.vibrate(vibePattern, -1, VIBRATION_ATTRIBUTES);
         }
 
@@ -187,22 +230,24 @@
             }
 
             // Show a toast alerting the user to what's happening
-            final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
-            if (serviceInfo == null) {
+            final String serviceName = getShortcutFeatureDescription(false /* no summary */);
+            if (serviceName == null) {
                 Slog.e(TAG, "Accessibility shortcut set to invalid service");
                 return;
             }
-            String toastMessageFormatString = mContext.getString(isServiceEnabled(serviceInfo)
-                    ? R.string.accessibility_shortcut_disabling_service
-                    : R.string.accessibility_shortcut_enabling_service);
-            String toastMessage = String.format(toastMessageFormatString,
-                    serviceInfo.getResolveInfo()
-                            .loadLabel(mContext.getPackageManager()).toString());
-            Toast warningToast = mFrameworkObjectProvider.makeToastFromText(
-                    mContext, toastMessage, Toast.LENGTH_LONG);
-            warningToast.getWindowParams().privateFlags |=
-                    WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
-            warningToast.show();
+            // For accessibility services, show a toast explaining what we're doing.
+            final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
+            if (serviceInfo != null) {
+                String toastMessageFormatString = mContext.getString(isServiceEnabled(serviceInfo)
+                        ? R.string.accessibility_shortcut_disabling_service
+                        : R.string.accessibility_shortcut_enabling_service);
+                String toastMessage = String.format(toastMessageFormatString, serviceName);
+                Toast warningToast = mFrameworkObjectProvider.makeToastFromText(
+                        mContext, toastMessage, Toast.LENGTH_LONG);
+                warningToast.getWindowParams().privateFlags |=
+                        WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                warningToast.show();
+            }
 
             mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext)
                     .performAccessibilityShortcut();
@@ -210,18 +255,18 @@
     }
 
     private AlertDialog createShortcutWarningDialog(int userId) {
-        final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
+        final String serviceDescription = getShortcutFeatureDescription(true /* Include summary */);
 
-        if (serviceInfo == null) {
+        if (serviceDescription == null) {
             return null;
         }
 
         final String warningMessage = String.format(
                 mContext.getString(R.string.accessibility_shortcut_toogle_warning),
-                serviceInfo.getResolveInfo().loadLabel(mContext.getPackageManager()).toString());
+                serviceDescription);
         final AlertDialog alertDialog = mFrameworkObjectProvider.getAlertDialogBuilder(
                 // Use SystemUI context so we pick up any theme set in a vendor overlay
-                ActivityThread.currentActivityThread().getSystemUiContext())
+                mFrameworkObjectProvider.getSystemUiContext())
                 .setTitle(R.string.accessibility_shortcut_warning_dialog_title)
                 .setMessage(warningMessage)
                 .setCancelable(false)
@@ -253,6 +298,34 @@
                         ComponentName.unflattenFromString(currentShortcutServiceString));
     }
 
+    private String getShortcutFeatureDescription(boolean includeSummary) {
+        final String currentShortcutServiceString = getTargetServiceComponentNameString(
+                mContext, UserHandle.USER_CURRENT);
+        if (currentShortcutServiceString == null) {
+            return null;
+        }
+        final ComponentName targetComponentName =
+                ComponentName.unflattenFromString(currentShortcutServiceString);
+        final ToggleableFrameworkFeatureInfo frameworkFeatureInfo =
+                getFrameworkShortcutFeaturesMap().get(targetComponentName);
+        if (frameworkFeatureInfo != null) {
+            return frameworkFeatureInfo.getLabel(mContext);
+        }
+        final AccessibilityServiceInfo serviceInfo = mFrameworkObjectProvider
+                .getAccessibilityManagerInstance(mContext).getInstalledServiceInfoWithComponentName(
+                        targetComponentName);
+        if (serviceInfo == null) {
+            return null;
+        }
+        final PackageManager pm = mContext.getPackageManager();
+        String label = serviceInfo.getResolveInfo().loadLabel(pm).toString();
+        String summary = serviceInfo.loadSummary(pm).toString();
+        if (!includeSummary || TextUtils.isEmpty(summary)) {
+            return label;
+        }
+        return String.format("%s\n%s", label, summary);
+    }
+
     private boolean isServiceEnabled(AccessibilityServiceInfo serviceInfo) {
         AccessibilityManager accessibilityManager =
                 mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext);
@@ -264,6 +337,51 @@
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
     }
 
+    /**
+     * Immutable class to hold info about framework features that can be controlled by shortcut
+     */
+    public static class ToggleableFrameworkFeatureInfo {
+        private final String mSettingKey;
+        private final String mSettingOnValue;
+        private final String mSettingOffValue;
+        private final int mLabelStringResourceId;
+        // These go to the settings wrapper
+        private int mIconDrawableId;
+
+        ToggleableFrameworkFeatureInfo(String settingKey, String settingOnValue,
+                String settingOffValue, int labelStringResourceId) {
+            mSettingKey = settingKey;
+            mSettingOnValue = settingOnValue;
+            mSettingOffValue = settingOffValue;
+            mLabelStringResourceId = labelStringResourceId;
+        }
+
+        /**
+         * @return The settings key to toggle between two values
+         */
+        public String getSettingKey() {
+            return mSettingKey;
+        }
+
+        /**
+         * @return The value to write to settings to turn the feature on
+         */
+        public String getSettingOnValue() {
+            return mSettingOnValue;
+        }
+
+        /**
+         * @return The value to write to settings to turn the feature off
+         */
+        public String getSettingOffValue() {
+            return mSettingOffValue;
+        }
+
+        public String getLabel(Context context) {
+            return context.getString(mLabelStringResourceId);
+        }
+    }
+
     // Class to allow mocking of static framework calls
     public static class FrameworkObjectProvider {
         public AccessibilityManager getAccessibilityManagerInstance(Context context) {
@@ -277,5 +395,9 @@
         public Toast makeToastFromText(Context context, CharSequence charSequence, int duration) {
             return Toast.makeText(context, charSequence, duration);
         }
+
+        public Context getSystemUiContext() {
+            return ActivityThread.currentActivityThread().getSystemUiContext();
+        }
     }
 }
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index 0a539f1..8016a65 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -111,14 +111,7 @@
     @Override
     public void onClick(DialogInterface dialog, int which) {
         if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE && which == DialogInterface.BUTTON_POSITIVE) {
-            if (UserManager.get(this).trySetQuietModeDisabled(mUserId, mTarget)
-                    && mTarget != null) {
-                try {
-                    startIntentSenderForResult(mTarget, -1, null, 0, 0, 0);
-                } catch (IntentSender.SendIntentException e) {
-                    /* ignore */
-                }
-            }
+            UserManager.get(this).trySetQuietModeDisabled(mUserId, mTarget);
         }
     }
 
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index fbdf17d..6fb02b1 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -28,6 +28,7 @@
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.Log;
+import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -137,7 +138,7 @@
     private final String mName;
     private final String mPackage;
     private final int mUid;
-    private final int mVersion;
+    private final long mVersion;
     private final DurationsTable mDurations;
     private final PssTable mPssTable;
 
@@ -170,7 +171,7 @@
      * Create a new top-level process state, for the initial case where there is only
      * a single package running in a process.  The initial state is not running.
      */
-    public ProcessState(ProcessStats processStats, String pkg, int uid, int vers, String name) {
+    public ProcessState(ProcessStats processStats, String pkg, int uid, long vers, String name) {
         mStats = processStats;
         mName = name;
         mCommonProcess = this;
@@ -186,7 +187,7 @@
      * state.  The current running state of the top-level process is also copied,
      * marked as started running at 'now'.
      */
-    public ProcessState(ProcessState commonProcess, String pkg, int uid, int vers, String name,
+    public ProcessState(ProcessState commonProcess, String pkg, int uid, long vers, String name,
             long now) {
         mStats = commonProcess.mStats;
         mName = name;
@@ -238,7 +239,7 @@
         return mUid;
     }
 
-    public int getVersion() {
+    public long getVersion() {
         return mVersion;
     }
 
@@ -546,7 +547,7 @@
             // The array map is still pointing to a common process state
             // that is now shared across packages.  Update it to point to
             // the new per-package state.
-            SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgName, mUid);
+            LongSparseArray<PackageState> vpkg = mStats.mPackages.get(pkgName, mUid);
             if (vpkg == null) {
                 throw new IllegalStateException("Didn't find package " + pkgName
                         + " / " + mUid);
@@ -584,7 +585,7 @@
             // The array map is still pointing to a common process state
             // that is now shared across packages.  Update it to point to
             // the new per-package state.
-            SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgList.keyAt(index),
+            LongSparseArray<PackageState> vpkg = mStats.mPackages.get(pkgList.keyAt(index),
                     proc.mUid);
             if (vpkg == null) {
                 throw new IllegalStateException("No existing package "
@@ -1037,7 +1038,7 @@
         }
     }
 
-    public void dumpPackageProcCheckin(PrintWriter pw, String pkgName, int uid, int vers,
+    public void dumpPackageProcCheckin(PrintWriter pw, String pkgName, int uid, long vers,
             String itemName, long now) {
         pw.print("pkgproc,");
         pw.print(pkgName);
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 14f5e5b..2ce7936 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -28,6 +28,7 @@
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.Log;
+import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -157,7 +158,7 @@
     };
 
     // Current version of the parcel format.
-    private static final int PARCEL_VERSION = 21;
+    private static final int PARCEL_VERSION = 22;
     // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
     private static final int MAGIC = 0x50535454;
 
@@ -165,9 +166,8 @@
     public String mTimePeriodStartClockStr;
     public int mFlags;
 
-    public final ProcessMap<SparseArray<PackageState>> mPackages
-            = new ProcessMap<SparseArray<PackageState>>();
-    public final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>();
+    public final ProcessMap<LongSparseArray<PackageState>> mPackages = new ProcessMap<>();
+    public final ProcessMap<ProcessState> mProcesses = new ProcessMap<>();
 
     public final long[] mMemFactorDurations = new long[ADJ_COUNT];
     public int mMemFactor = STATE_NOTHING;
@@ -218,15 +218,16 @@
     }
 
     public void add(ProcessStats other) {
-        ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = other.mPackages.getMap();
+        ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+                other.mPackages.getMap();
         for (int ip=0; ip<pkgMap.size(); ip++) {
             final String pkgName = pkgMap.keyAt(ip);
-            final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
             for (int iu=0; iu<uids.size(); iu++) {
                 final int uid = uids.keyAt(iu);
-                final SparseArray<PackageState> versions = uids.valueAt(iu);
+                final LongSparseArray<PackageState> versions = uids.valueAt(iu);
                 for (int iv=0; iv<versions.size(); iv++) {
-                    final int vers = versions.keyAt(iv);
+                    final long vers = versions.keyAt(iv);
                     final PackageState otherState = versions.valueAt(iv);
                     final int NPROCS = otherState.mProcesses.size();
                     final int NSRVS = otherState.mServices.size();
@@ -269,7 +270,7 @@
                 ProcessState otherProc = uids.valueAt(iu);
                 final String name = otherProc.getName();
                 final String pkg = otherProc.getPackage();
-                final int vers = otherProc.getVersion();
+                final long vers = otherProc.getVersion();
                 ProcessState thisProc = mProcesses.get(name, uid);
                 if (DEBUG) Slog.d(TAG, "Adding uid " + uid + " proc " + name);
                 if (thisProc == null) {
@@ -420,11 +421,12 @@
 
         // Next reset or prune all per-package processes, and for the ones that are reset
         // track this back to the common processes.
-        final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+        final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+                mPackages.getMap();
         for (int ip=pkgMap.size()-1; ip>=0; ip--) {
-            final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
             for (int iu=uids.size()-1; iu>=0; iu--) {
-                final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+                final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
                 for (int iv=vpkgs.size()-1; iv>=0; iv--) {
                     final PackageState pkgState = vpkgs.valueAt(iv);
                     for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) {
@@ -727,13 +729,14 @@
                 uids.valueAt(iu).commitStateTime(now);
             }
         }
-        final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+        final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+                mPackages.getMap();
         final int NPKG = pkgMap.size();
         for (int ip=0; ip<NPKG; ip++) {
-            final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
             final int NUID = uids.size();
             for (int iu=0; iu<NUID; iu++) {
-                final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+                final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
                 final int NVERS = vpkgs.size();
                 for (int iv=0; iv<NVERS; iv++) {
                     PackageState pkgState = vpkgs.valueAt(iv);
@@ -781,23 +784,23 @@
                 out.writeInt(uids.keyAt(iu));
                 final ProcessState proc = uids.valueAt(iu);
                 writeCommonString(out, proc.getPackage());
-                out.writeInt(proc.getVersion());
+                out.writeLong(proc.getVersion());
                 proc.writeToParcel(out, now);
             }
         }
         out.writeInt(NPKG);
         for (int ip=0; ip<NPKG; ip++) {
             writeCommonString(out, pkgMap.keyAt(ip));
-            final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
             final int NUID = uids.size();
             out.writeInt(NUID);
             for (int iu=0; iu<NUID; iu++) {
                 out.writeInt(uids.keyAt(iu));
-                final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+                final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
                 final int NVERS = vpkgs.size();
                 out.writeInt(NVERS);
                 for (int iv=0; iv<NVERS; iv++) {
-                    out.writeInt(vpkgs.keyAt(iv));
+                    out.writeLong(vpkgs.keyAt(iv));
                     final PackageState pkgState = vpkgs.valueAt(iv);
                     final int NPROCS = pkgState.mProcesses.size();
                     out.writeInt(NPROCS);
@@ -963,7 +966,7 @@
                     mReadError = "bad process package name";
                     return;
                 }
-                final int vers = in.readInt();
+                final long vers = in.readLong();
                 ProcessState proc = hadData ? mProcesses.get(procName, uid) : null;
                 if (proc != null) {
                     if (!proc.readFromParcel(in, false)) {
@@ -1014,11 +1017,11 @@
                 }
                 while (NVERS > 0) {
                     NVERS--;
-                    final int vers = in.readInt();
+                    final long vers = in.readLong();
                     PackageState pkgState = new PackageState(pkgName, uid);
-                    SparseArray<PackageState> vpkg = mPackages.get(pkgName, uid);
+                    LongSparseArray<PackageState> vpkg = mPackages.get(pkgName, uid);
                     if (vpkg == null) {
-                        vpkg = new SparseArray<PackageState>();
+                        vpkg = new LongSparseArray<>();
                         mPackages.put(pkgName, uid, vpkg);
                     }
                     vpkg.put(vers, pkgState);
@@ -1117,10 +1120,10 @@
         if (DEBUG_PARCEL) Slog.d(TAG, "Successfully read procstats!");
     }
 
-    public PackageState getPackageStateLocked(String packageName, int uid, int vers) {
-        SparseArray<PackageState> vpkg = mPackages.get(packageName, uid);
+    public PackageState getPackageStateLocked(String packageName, int uid, long vers) {
+        LongSparseArray<PackageState> vpkg = mPackages.get(packageName, uid);
         if (vpkg == null) {
-            vpkg = new SparseArray<PackageState>();
+            vpkg = new LongSparseArray<PackageState>();
             mPackages.put(packageName, uid, vpkg);
         }
         PackageState as = vpkg.get(vers);
@@ -1132,7 +1135,7 @@
         return as;
     }
 
-    public ProcessState getProcessStateLocked(String packageName, int uid, int vers,
+    public ProcessState getProcessStateLocked(String packageName, int uid, long vers,
             String processName) {
         final PackageState pkgState = getPackageStateLocked(packageName, uid, vers);
         ProcessState ps = pkgState.mProcesses.get(processName);
@@ -1202,7 +1205,7 @@
         return ps;
     }
 
-    public ServiceState getServiceStateLocked(String packageName, int uid, int vers,
+    public ServiceState getServiceStateLocked(String packageName, int uid, long vers,
             String processName, String className) {
         final ProcessStats.PackageState as = getPackageStateLocked(packageName, uid, vers);
         ServiceState ss = as.mServices.get(className);
@@ -1228,16 +1231,16 @@
             mSysMemUsage.dump(pw, "  ", ALL_SCREEN_ADJ, ALL_MEM_ADJ);
             sepNeeded = true;
         }
-        ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+        ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap = mPackages.getMap();
         boolean printedHeader = false;
         for (int ip=0; ip<pkgMap.size(); ip++) {
             final String pkgName = pkgMap.keyAt(ip);
-            final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
             for (int iu=0; iu<uids.size(); iu++) {
                 final int uid = uids.keyAt(iu);
-                final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+                final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
                 for (int iv=0; iv<vpkgs.size(); iv++) {
-                    final int vers = vpkgs.keyAt(iv);
+                    final long vers = vpkgs.keyAt(iv);
                     final PackageState pkgState = vpkgs.valueAt(iv);
                     final int NPROCS = pkgState.mProcesses.size();
                     final int NSRVS = pkgState.mServices.size();
@@ -1531,12 +1534,13 @@
             int[] procStates, int sortProcStates[], long now, String reqPackage,
             boolean activeOnly) {
         final ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>();
-        final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+        final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+                mPackages.getMap();
         for (int ip=0; ip<pkgMap.size(); ip++) {
             final String pkgName = pkgMap.keyAt(ip);
-            final SparseArray<SparseArray<PackageState>> procs = pkgMap.valueAt(ip);
+            final SparseArray<LongSparseArray<PackageState>> procs = pkgMap.valueAt(ip);
             for (int iu=0; iu<procs.size(); iu++) {
-                final SparseArray<PackageState> vpkgs = procs.valueAt(iu);
+                final LongSparseArray<PackageState> vpkgs = procs.valueAt(iu);
                 final int NVERS = vpkgs.size();
                 for (int iv=0; iv<NVERS; iv++) {
                     final PackageState state = vpkgs.valueAt(iv);
@@ -1571,7 +1575,8 @@
 
     public void dumpCheckinLocked(PrintWriter pw, String reqPackage) {
         final long now = SystemClock.uptimeMillis();
-        final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+        final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+                mPackages.getMap();
         pw.println("vers,5");
         pw.print("period,"); pw.print(mTimePeriodStartClockStr);
         pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(",");
@@ -1602,12 +1607,12 @@
             if (reqPackage != null && !reqPackage.equals(pkgName)) {
                 continue;
             }
-            final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip);
+            final SparseArray<LongSparseArray<PackageState>> uids = pkgMap.valueAt(ip);
             for (int iu=0; iu<uids.size(); iu++) {
                 final int uid = uids.keyAt(iu);
-                final SparseArray<PackageState> vpkgs = uids.valueAt(iu);
+                final LongSparseArray<PackageState> vpkgs = uids.valueAt(iu);
                 for (int iv=0; iv<vpkgs.size(); iv++) {
-                    final int vers = vpkgs.keyAt(iv);
+                    final long vers = vpkgs.keyAt(iv);
                     final PackageState pkgState = vpkgs.valueAt(iv);
                     final int NPROCS = pkgState.mProcesses.size();
                     final int NSRVS = pkgState.mServices.size();
@@ -1709,7 +1714,8 @@
     }
 
     public void toProto(ProtoOutputStream proto, long now) {
-        final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
+        final ArrayMap<String, SparseArray<LongSparseArray<PackageState>>> pkgMap =
+                mPackages.getMap();
 
         proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime);
         proto.write(ProcessStatsSectionProto.END_REALTIME_MS,
@@ -1750,10 +1756,10 @@
     }
 
     final public static class ProcessStateHolder {
-        public final int appVersion;
+        public final long appVersion;
         public ProcessState state;
 
-        public ProcessStateHolder(int _appVersion) {
+        public ProcessStateHolder(long _appVersion) {
             appVersion = _appVersion;
         }
     }
diff --git a/core/java/com/android/internal/app/procstats/ServiceState.java b/core/java/com/android/internal/app/procstats/ServiceState.java
index 2e11c43..650de2ea 100644
--- a/core/java/com/android/internal/app/procstats/ServiceState.java
+++ b/core/java/com/android/internal/app/procstats/ServiceState.java
@@ -441,7 +441,7 @@
         return totalTime;
     }
 
-    public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, int vers,
+    public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, long vers,
             String serviceName, long now) {
         dumpTimeCheckin(pw, "pkgsvc-run", pkgName, uid, vers, serviceName,
                 ServiceState.SERVICE_RUN, mRunCount, mRunState, mRunStartTime, now);
@@ -454,7 +454,7 @@
     }
 
     private void dumpTimeCheckin(PrintWriter pw, String label, String packageName,
-            int uid, int vers, String serviceName, int serviceType, int opCount,
+            int uid, long vers, String serviceName, int serviceType, int opCount,
             int curState, long curStartTime, long now) {
         if (opCount <= 0) {
             return;
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 3e231d0..57efae6 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -836,7 +836,6 @@
         private final Resources mRes;
         private final ContentResolver mResolver;
         private final HashMap<String, InputMethodInfo> mMethodMap;
-        private final ArrayList<InputMethodInfo> mMethodList;
 
         /**
          * On-memory data store to emulate when {@link #mCopyOnWrite} is {@code true}.
@@ -906,7 +905,6 @@
             mRes = res;
             mResolver = resolver;
             mMethodMap = methodMap;
-            mMethodList = methodList;
             switchCurrentUser(userId, copyOnWrite);
         }
 
@@ -1087,7 +1085,7 @@
             final ArrayList<InputMethodInfo> res = new ArrayList<>();
             for (Pair<String, ArrayList<String>> ims: imsList) {
                 InputMethodInfo info = mMethodMap.get(ims.first);
-                if (info != null) {
+                if (info != null && !info.isVrOnly()) {
                     res.add(info);
                 }
             }
diff --git a/core/java/com/android/internal/logging/EventLogTags.logtags b/core/java/com/android/internal/logging/EventLogTags.logtags
index 93d5a03..a440ee4 100644
--- a/core/java/com/android/internal/logging/EventLogTags.logtags
+++ b/core/java/com/android/internal/logging/EventLogTags.logtags
@@ -8,3 +8,8 @@
 524292 sysui_multi_action (content|4)
 524290 sysui_count (name|3),(increment|1)
 524291 sysui_histogram (name|3),(bucket|1)
+
+# ---------------------------
+# LatencyTracker.java
+# ---------------------------
+36070 sysui_latency (action|1|6),(latency|1|3)
diff --git a/core/java/com/android/internal/os/BackgroundThread.java b/core/java/com/android/internal/os/BackgroundThread.java
index cffba01..7558f8c 100644
--- a/core/java/com/android/internal/os/BackgroundThread.java
+++ b/core/java/com/android/internal/os/BackgroundThread.java
@@ -35,7 +35,7 @@
         if (sInstance == null) {
             sInstance = new BackgroundThread();
             sInstance.start();
-            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
             sHandler = new Handler(sInstance.getLooper());
         }
     }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 3d49072..a050a3c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -120,7 +120,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 169 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 170 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS;
@@ -4501,11 +4501,12 @@
         }
     }
 
-    public void notePackageInstalledLocked(String pkgName, int versionCode) {
+    public void notePackageInstalledLocked(String pkgName, long versionCode) {
         final long elapsedRealtime = mClocks.elapsedRealtime();
         final long uptime = mClocks.uptimeMillis();
+        // XXX need to figure out what to do with long version codes.
         addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PACKAGE_INSTALLED,
-                pkgName, versionCode);
+                pkgName, (int)versionCode);
         PackageChange pc = new PackageChange();
         pc.mPackageName = pkgName;
         pc.mUpdate = true;
@@ -9283,7 +9284,7 @@
                     if (pc.mUpdate) {
                         out.startTag(null, "upd");
                         out.attribute(null, "pkg", pc.mPackageName);
-                        out.attribute(null, "ver", Integer.toString(pc.mVersionCode));
+                        out.attribute(null, "ver", Long.toString(pc.mVersionCode));
                         out.endTag(null, "upd");
                     } else {
                         out.startTag(null, "rem");
@@ -9412,7 +9413,7 @@
                 pc.mUpdate = true;
                 pc.mPackageName = parser.getAttributeValue(null, "pkg");
                 String verStr = parser.getAttributeValue(null, "ver");
-                pc.mVersionCode = verStr != null ? Integer.parseInt(verStr) : 0;
+                pc.mVersionCode = verStr != null ? Long.parseLong(verStr) : 0;
                 dit.mPackageChanges.add(pc);
                 XmlUtils.skipCurrentTag(parser);
             } else if (tagName.equals("rem")) {
@@ -12113,7 +12114,7 @@
                 PackageChange pc = new PackageChange();
                 pc.mPackageName = in.readString();
                 pc.mUpdate = in.readInt() != 0;
-                pc.mVersionCode = in.readInt();
+                pc.mVersionCode = in.readLong();
                 mDailyPackageChanges.add(pc);
             }
         } else {
@@ -12538,7 +12539,7 @@
                 PackageChange pc = mDailyPackageChanges.get(i);
                 out.writeString(pc.mPackageName);
                 out.writeInt(pc.mUpdate ? 1 : 0);
-                out.writeInt(pc.mVersionCode);
+                out.writeLong(pc.mVersionCode);
             }
         } else {
             out.writeInt(0);
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index 8fb56d4..d9aa325 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -48,6 +48,7 @@
     public Object arg6;
     public Object arg7;
     public Object arg8;
+    public Object arg9;
     public int argi1;
     public int argi2;
     public int argi3;
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 22bfcc3..aa85668 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -292,6 +292,15 @@
         return array;
     }
 
+    public static @Nullable long[] convertToLongArray(@Nullable int[] intArray) {
+        if (intArray == null) return null;
+        long[] array = new long[intArray.length];
+        for (int i = 0; i < intArray.length; i++) {
+            array[i] = (long) intArray[i];
+        }
+        return array;
+    }
+
     /**
      * Adds value to given array if not already present, providing set-like
      * behavior.
@@ -425,14 +434,17 @@
      * Adds value to given array if not already present, providing set-like
      * behavior.
      */
-    public static @NonNull long[] appendLong(@Nullable long[] cur, long val) {
+    public static @NonNull long[] appendLong(@Nullable long[] cur, long val,
+            boolean allowDuplicates) {
         if (cur == null) {
             return new long[] { val };
         }
         final int N = cur.length;
-        for (int i = 0; i < N; i++) {
-            if (cur[i] == val) {
-                return cur;
+        if (!allowDuplicates) {
+            for (int i = 0; i < N; i++) {
+                if (cur[i] == val) {
+                    return cur;
+                }
             }
         }
         long[] ret = new long[N + 1];
@@ -442,6 +454,14 @@
     }
 
     /**
+     * Adds value to given array if not already present, providing set-like
+     * behavior.
+     */
+    public static @NonNull long[] appendLong(@Nullable long[] cur, long val) {
+        return appendLong(cur, val, false);
+    }
+
+    /**
      * Removes value from given array if present, providing set-like behavior.
      */
     public static @Nullable long[] removeLong(@Nullable long[] cur, long val) {
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index f0b47de..f983de1 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -30,7 +30,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
-import java.util.function.*;
+import java.util.function.Function;
 import java.util.stream.Stream;
 
 /**
diff --git a/core/java/com/android/internal/util/FunctionalUtils.java b/core/java/com/android/internal/util/FunctionalUtils.java
index cdef97e..eb92c1c 100644
--- a/core/java/com/android/internal/util/FunctionalUtils.java
+++ b/core/java/com/android/internal/util/FunctionalUtils.java
@@ -32,7 +32,7 @@
      */
     @FunctionalInterface
     public interface ThrowingRunnable {
-        void run() throws Exception;
+        void runOrThrow() throws Exception;
     }
 
     /**
@@ -43,7 +43,7 @@
      */
     @FunctionalInterface
     public interface ThrowingSupplier<T> {
-        T get() throws Exception;
+        T getOrThrow() throws Exception;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
similarity index 86%
rename from packages/SystemUI/src/com/android/keyguard/LatencyTracker.java
rename to core/java/com/android/internal/util/LatencyTracker.java
index cee0afc..72cd248 100644
--- a/packages/SystemUI/src/com/android/keyguard/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -1,20 +1,18 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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
+ * 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
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
  */
 
-package com.android.keyguard;
+package com.android.internal.util;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -28,7 +26,7 @@
 import android.util.Log;
 import android.util.SparseLongArray;
 
-import com.android.systemui.EventLogTags;
+import com.android.internal.logging.EventLogTags;
 
 /**
  * Class to track various latencies in SystemUI. It then outputs the latency to logcat so these
@@ -76,13 +74,19 @@
      */
     public static final int ACTION_TURN_ON_SCREEN = 5;
 
+    /**
+     * Time it takes to rotate the screen.
+     */
+    public static final int ACTION_ROTATE_SCREEN = 6;
+
     private static final String[] NAMES = new String[] {
             "expand panel",
             "toggle recents",
             "fingerprint wake-and-unlock",
             "check credential",
             "check credential unlocked",
-            "turn on screen" };
+            "turn on screen",
+            "rotate the screen"};
 
     private static LatencyTracker sLatencyTracker;
 
diff --git a/core/java/com/android/internal/util/function/QuadConsumer.java b/core/java/com/android/internal/util/function/QuadConsumer.java
new file mode 100644
index 0000000..d899c01
--- /dev/null
+++ b/core/java/com/android/internal/util/function/QuadConsumer.java
@@ -0,0 +1,29 @@
+/*
+ * 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.internal.util.function;
+
+
+import java.util.function.Consumer;
+
+/**
+ * A 4-argument {@link Consumer}
+ *
+ * @hide
+ */
+public interface QuadConsumer<A, B, C, D> {
+    void accept(A a, B b, C c, D d);
+}
diff --git a/core/java/com/android/internal/util/function/QuadFunction.java b/core/java/com/android/internal/util/function/QuadFunction.java
new file mode 100644
index 0000000..700d953
--- /dev/null
+++ b/core/java/com/android/internal/util/function/QuadFunction.java
@@ -0,0 +1,29 @@
+/*
+ * 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.internal.util.function;
+
+
+import java.util.function.Function;
+
+/**
+ * A 4-argument {@link Function}
+ *
+ * @hide
+ */
+public interface QuadFunction<A, B, C, D, R> {
+    R apply(A a, B b, C c, D d);
+}
diff --git a/core/java/com/android/internal/util/function/QuadPredicate.java b/core/java/com/android/internal/util/function/QuadPredicate.java
new file mode 100644
index 0000000..512c98b
--- /dev/null
+++ b/core/java/com/android/internal/util/function/QuadPredicate.java
@@ -0,0 +1,29 @@
+/*
+ * 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.internal.util.function;
+
+
+import java.util.function.Predicate;
+
+/**
+ * A 4-argument {@link Predicate}
+ *
+ * @hide
+ */
+public interface QuadPredicate<A, B, C, D> {
+    boolean test(A a, B b, C c, D d);
+}
diff --git a/core/java/com/android/internal/util/function/TriConsumer.java b/core/java/com/android/internal/util/function/TriConsumer.java
new file mode 100644
index 0000000..40d614e
--- /dev/null
+++ b/core/java/com/android/internal/util/function/TriConsumer.java
@@ -0,0 +1,29 @@
+/*
+ * 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.internal.util.function;
+
+
+import java.util.function.Consumer;
+
+/**
+ * A 3-argument {@link Consumer}
+ *
+ * @hide
+ */
+public interface TriConsumer<A, B, C> {
+    void accept(A a, B b, C c);
+}
diff --git a/core/java/com/android/internal/util/function/TriFunction.java b/core/java/com/android/internal/util/function/TriFunction.java
new file mode 100644
index 0000000..2b1df86
--- /dev/null
+++ b/core/java/com/android/internal/util/function/TriFunction.java
@@ -0,0 +1,29 @@
+/*
+ * 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.internal.util.function;
+
+
+import java.util.function.Function;
+
+/**
+ * A 3-argument {@link Function}
+ *
+ * @hide
+ */
+public interface TriFunction<A, B, C, R> {
+    R apply(A a, B b, C c);
+}
diff --git a/core/java/com/android/internal/util/function/TriPredicate.java b/core/java/com/android/internal/util/function/TriPredicate.java
new file mode 100644
index 0000000..d9cd968
--- /dev/null
+++ b/core/java/com/android/internal/util/function/TriPredicate.java
@@ -0,0 +1,29 @@
+/*
+ * 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.internal.util.function;
+
+
+import java.util.function.Predicate;
+
+/**
+ * A 3-argument {@link Predicate}
+ *
+ * @hide
+ */
+public interface TriPredicate<A, B, C> {
+    boolean test(A a, B b, C c);
+}
diff --git a/core/java/com/android/internal/util/function/pooled/ArgumentPlaceholder.java b/core/java/com/android/internal/util/function/pooled/ArgumentPlaceholder.java
new file mode 100644
index 0000000..cf86b71
--- /dev/null
+++ b/core/java/com/android/internal/util/function/pooled/ArgumentPlaceholder.java
@@ -0,0 +1,33 @@
+/*
+ * 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.internal.util.function.pooled;
+
+/**
+ * A placeholder for an argument of type {@code R}
+ *
+ * @see PooledLambda
+ * @hide
+ */
+public final class ArgumentPlaceholder<R> {
+    private ArgumentPlaceholder() {}
+    static final ArgumentPlaceholder<?> INSTANCE = new ArgumentPlaceholder<>();
+
+    @Override
+    public String toString() {
+        return "_";
+    }
+}
diff --git a/core/java/com/android/internal/util/function/pooled/OmniFunction.java b/core/java/com/android/internal/util/function/pooled/OmniFunction.java
new file mode 100755
index 0000000..c0f506e
--- /dev/null
+++ b/core/java/com/android/internal/util/function/pooled/OmniFunction.java
@@ -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.
+ */
+
+package com.android.internal.util.function.pooled;
+
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
+import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
+import com.android.internal.util.function.QuadConsumer;
+import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.TriConsumer;
+import com.android.internal.util.function.TriFunction;
+
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
+import java.util.function.Function;
+
+/**
+ * An interface implementing all supported function interfaces, delegating each to {@link #invoke}
+ *
+ * @hide
+ */
+abstract class OmniFunction<A, B, C, D, R> implements
+        PooledFunction<A, R>, BiFunction<A, B, R>, TriFunction<A, B, C, R>,
+        QuadFunction<A, B, C, D, R>,
+        PooledConsumer<A>, BiConsumer<A, B>, TriConsumer<A, B, C>, QuadConsumer<A, B, C, D>,
+        PooledPredicate<A>, BiPredicate<A, B>,
+        PooledSupplier<R>, PooledRunnable,
+        ThrowingRunnable, ThrowingSupplier<R>,
+        PooledSupplier.OfInt, PooledSupplier.OfLong, PooledSupplier.OfDouble {
+
+    abstract R invoke(A a, B b, C c, D d);
+
+    @Override
+    public R apply(A o, B o2) {
+        return invoke(o, o2, null, null);
+    }
+
+    @Override
+    public R apply(A o) {
+        return invoke(o, null, null, null);
+    }
+
+    abstract public <V> OmniFunction<A, B, C, D, V> andThen(Function<? super R, ? extends V> after);
+    abstract public OmniFunction<A, B, C, D, R> negate();
+
+    @Override
+    public void accept(A o, B o2) {
+        invoke(o, o2, null, null);
+    }
+
+    @Override
+    public void accept(A o) {
+        invoke(o, null, null, null);
+    }
+
+    @Override
+    public void run() {
+        invoke(null, null, null, null);
+    }
+
+    @Override
+    public R get() {
+        return invoke(null, null, null, null);
+    }
+
+    @Override
+    public boolean test(A o, B o2) {
+        return (Boolean) invoke(o, o2, null, null);
+    }
+
+    @Override
+    public boolean test(A o) {
+        return (Boolean) invoke(o, null, null, null);
+    }
+
+    @Override
+    public PooledRunnable asRunnable() {
+        return this;
+    }
+
+    @Override
+    public PooledConsumer<A> asConsumer() {
+        return this;
+    }
+
+    @Override
+    public R apply(A a, B b, C c) {
+        return invoke(a, b, c, null);
+    }
+
+    @Override
+    public void accept(A a, B b, C c) {
+        invoke(a, b, c, null);
+    }
+
+    @Override
+    public R apply(A a, B b, C c, D d) {
+        return invoke(a, b, c, d);
+    }
+
+    @Override
+    public void accept(A a, B b, C c, D d) {
+        invoke(a, b, c, d);
+    }
+
+    @Override
+    public void runOrThrow() throws Exception {
+        run();
+    }
+
+    @Override
+    public R getOrThrow() throws Exception {
+        return get();
+    }
+
+    @Override
+    abstract public OmniFunction<A, B, C, D, R> recycleOnUse();
+}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledConsumer.java b/core/java/com/android/internal/util/function/pooled/PooledConsumer.java
new file mode 100644
index 0000000..f66586e
--- /dev/null
+++ b/core/java/com/android/internal/util/function/pooled/PooledConsumer.java
@@ -0,0 +1,31 @@
+/*
+ * 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.internal.util.function.pooled;
+
+import java.util.function.Consumer;
+
+/**
+ * {@link Consumer} + {@link PooledLambda}
+ *
+ * @see PooledLambda
+ * @hide
+ */
+public interface PooledConsumer<T> extends PooledLambda, Consumer<T> {
+
+    /** @inheritDoc */
+    PooledConsumer<T> recycleOnUse();
+}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledFunction.java b/core/java/com/android/internal/util/function/pooled/PooledFunction.java
new file mode 100644
index 0000000..1f166fa
--- /dev/null
+++ b/core/java/com/android/internal/util/function/pooled/PooledFunction.java
@@ -0,0 +1,36 @@
+/*
+ * 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.internal.util.function.pooled;
+
+import java.util.function.Function;
+
+/**
+ * {@link Function} + {@link PooledLambda}
+ *
+ * @see PooledLambda
+ * @hide
+ */
+public interface PooledFunction<A, R> extends PooledLambda, Function<A, R> {
+
+    /**
+     * Ignores the result
+     */
+    PooledConsumer<A> asConsumer();
+
+    /** @inheritDoc */
+    PooledFunction<A, R> recycleOnUse();
+}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
new file mode 100755
index 0000000..17b140d
--- /dev/null
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
@@ -0,0 +1,813 @@
+/*
+ * 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.internal.util.function.pooled;
+
+import static com.android.internal.util.function.pooled.PooledLambdaImpl.acquire;
+import static com.android.internal.util.function.pooled.PooledLambdaImpl.acquireConstSupplier;
+
+import android.os.Message;
+
+import com.android.internal.util.function.QuadConsumer;
+import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.TriConsumer;
+import com.android.internal.util.function.TriFunction;
+import com.android.internal.util.function.pooled.PooledLambdaImpl.LambdaType.ReturnType;
+
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+/**
+ * A recyclable anonymous function.
+ * Allows obtaining {@link Function}s/{@link Runnable}s/{@link Supplier}s/etc. without allocating a
+ * new instance each time
+ *
+ * This exploits the mechanic that stateless lambdas (such as plain/non-bound method references)
+ * get translated into a singleton instance, making it possible to create a recyclable container
+ * ({@link PooledLambdaImpl}) holding a reference to such a singleton function, as well as
+ * (possibly partial) arguments required for its invocation.
+ *
+ * To obtain an instance, use one of the factory methods in this class.
+ *
+ * You can call {@link #recycleOnUse} to make the instance automatically recycled upon invocation,
+ * making if effectively <b>one-time use</b>.
+ * This is often the behavior you want, as it allows to not worry about manual recycling.
+ * Some notable examples: {@link android.os.Handler#post(Runnable)},
+ * {@link android.app.Activity#runOnUiThread(Runnable)}, {@link android.view.View#post(Runnable)}
+ *
+ * For factories of functions that take further arguments, the corresponding 'missing' argument's
+ * position is marked by an argument of type {@link ArgumentPlaceholder} with the type parameter
+ * corresponding to missing argument's type.
+ * You can fill the 'missing argument' spot with {@link #__()}
+ * (which is the factory function for {@link ArgumentPlaceholder})
+ *
+ * @hide
+ */
+@SuppressWarnings({"unchecked", "unused", "WeakerAccess"})
+public interface PooledLambda {
+
+    /**
+     * Recycles this instance. No-op if already recycled.
+     */
+    void recycle();
+
+    /**
+     * Makes this instance automatically {@link #recycle} itself after the first call.
+     *
+     * @return this instance for convenience
+     */
+    PooledLambda recycleOnUse();
+
+
+    // Factories
+
+    /**
+     * @return {@link ArgumentPlaceholder} with the inferred type parameter value
+     */
+    static <R> ArgumentPlaceholder<R> __() {
+        return (ArgumentPlaceholder<R>) ArgumentPlaceholder.INSTANCE;
+    }
+
+    /**
+     * @param typeHint the explicitly specified type of the missing argument
+     * @return {@link ArgumentPlaceholder} with the specified type parameter value
+     */
+    static <R> ArgumentPlaceholder<R> __(Class<R> typeHint) {
+        return __();
+    }
+
+    /**
+     * Wraps the given value into a {@link PooledSupplier}
+     *
+     * @param value a value to wrap
+     * @return a pooled supplier of {@code value}
+     */
+    static <R> PooledSupplier<R> obtainSupplier(R value) {
+        PooledLambdaImpl r = acquireConstSupplier(ReturnType.OBJECT);
+        r.mFunc = value;
+        return r;
+    }
+
+    /**
+     * Wraps the given value into a {@link PooledSupplier}
+     *
+     * @param value a value to wrap
+     * @return a pooled supplier of {@code value}
+     */
+    static PooledSupplier.OfInt obtainSupplier(int value) {
+        PooledLambdaImpl r = acquireConstSupplier(ReturnType.INT);
+        r.mConstValue = value;
+        return r;
+    }
+
+    /**
+     * Wraps the given value into a {@link PooledSupplier}
+     *
+     * @param value a value to wrap
+     * @return a pooled supplier of {@code value}
+     */
+    static PooledSupplier.OfLong obtainSupplier(long value) {
+        PooledLambdaImpl r = acquireConstSupplier(ReturnType.LONG);
+        r.mConstValue = value;
+        return r;
+    }
+
+    /**
+     * Wraps the given value into a {@link PooledSupplier}
+     *
+     * @param value a value to wrap
+     * @return a pooled supplier of {@code value}
+     */
+    static PooledSupplier.OfDouble obtainSupplier(double value) {
+        PooledLambdaImpl r = acquireConstSupplier(ReturnType.DOUBLE);
+        r.mConstValue = Double.doubleToRawLongBits(value);
+        return r;
+    }
+
+    /**
+     * {@link PooledRunnable} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @return a {@link PooledRunnable}, equivalent to lambda:
+     *         {@code () -> function(arg1) }
+     */
+    static <A> PooledRunnable obtainRunnable(
+            Consumer<? super A> function,
+            A arg1) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 1, 0, ReturnType.VOID, arg1, null, null, null);
+    }
+
+    /**
+     * {@link PooledSupplier} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @return a {@link PooledSupplier}, equivalent to lambda:
+     *         {@code () -> function(arg1) }
+     */
+    static <A> PooledSupplier<Boolean> obtainSupplier(
+            Predicate<? super A> function,
+            A arg1) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null);
+    }
+
+    /**
+     * {@link PooledSupplier} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @return a {@link PooledSupplier}, equivalent to lambda:
+     *         {@code () -> function(arg1) }
+     */
+    static <A, R> PooledSupplier<R> obtainSupplier(
+            Function<? super A, ? extends R> function,
+            A arg1) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 1, 0, ReturnType.OBJECT, arg1, null, null, null);
+    }
+
+    /**
+     * Factory of {@link Message}s that contain an
+     * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+     * {@link Message#getCallback internal callback}.
+     *
+     * The callback is equivalent to one obtainable via
+     * {@link #obtainRunnable(Consumer, Object)}
+     *
+     * Note that using this method with {@link android.os.Handler#handleMessage}
+     * is more efficient than the alternative of {@link android.os.Handler#post}
+     * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+     * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+     *
+     * You may optionally set a {@link Message#what} for the message if you want to be
+     * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+     * there's no need to do so
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @return a {@link Message} invoking {@code function(arg1) } when handled
+     */
+    static <A> Message obtainMessage(
+            Consumer<? super A> function,
+            A arg1) {
+        synchronized (Message.sPoolSync) {
+            PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+                    function, 1, 0, ReturnType.VOID, arg1, null, null, null);
+            return Message.obtain().setCallback(callback.recycleOnUse());
+        }
+    }
+
+    /**
+     * {@link PooledRunnable} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @return a {@link PooledRunnable}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2) }
+     */
+    static <A, B> PooledRunnable obtainRunnable(
+            BiConsumer<? super A, ? super B> function,
+            A arg1, B arg2) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 2, 0, ReturnType.VOID, arg1, arg2, null, null);
+    }
+
+    /**
+     * {@link PooledSupplier} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @return a {@link PooledSupplier}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2) }
+     */
+    static <A, B> PooledSupplier<Boolean> obtainSupplier(
+            BiPredicate<? super A, ? super B> function,
+            A arg1, B arg2) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null);
+    }
+
+    /**
+     * {@link PooledSupplier} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @return a {@link PooledSupplier}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2) }
+     */
+    static <A, B, R> PooledSupplier<R> obtainSupplier(
+            BiFunction<? super A, ? super B, ? extends R> function,
+            A arg1, B arg2) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null);
+    }
+
+    /**
+     * {@link PooledConsumer} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 placeholder for a missing argument. Use {@link #__} to get one
+     * @param arg2 parameter supplied to {@code function} on call
+     * @return a {@link PooledConsumer}, equivalent to lambda:
+     *         {@code (arg1) -> function(arg1, arg2) }
+     */
+    static <A, B> PooledConsumer<A> obtainConsumer(
+            BiConsumer<? super A, ? super B> function,
+            ArgumentPlaceholder<A> arg1, B arg2) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 2, 1, ReturnType.VOID, arg1, arg2, null, null);
+    }
+
+    /**
+     * {@link PooledPredicate} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 placeholder for a missing argument. Use {@link #__} to get one
+     * @param arg2 parameter supplied to {@code function} on call
+     * @return a {@link PooledPredicate}, equivalent to lambda:
+     *         {@code (arg1) -> function(arg1, arg2) }
+     */
+    static <A, B> PooledPredicate<A> obtainPredicate(
+            BiPredicate<? super A, ? super B> function,
+            ArgumentPlaceholder<A> arg1, B arg2) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null);
+    }
+
+    /**
+     * {@link PooledFunction} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 placeholder for a missing argument. Use {@link #__} to get one
+     * @param arg2 parameter supplied to {@code function} on call
+     * @return a {@link PooledFunction}, equivalent to lambda:
+     *         {@code (arg1) -> function(arg1, arg2) }
+     */
+    static <A, B, R> PooledFunction<A, R> obtainFunction(
+            BiFunction<? super A, ? super B, ? extends R> function,
+            ArgumentPlaceholder<A> arg1, B arg2) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null);
+    }
+
+    /**
+     * {@link PooledConsumer} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 placeholder for a missing argument. Use {@link #__} to get one
+     * @return a {@link PooledConsumer}, equivalent to lambda:
+     *         {@code (arg2) -> function(arg1, arg2) }
+     */
+    static <A, B> PooledConsumer<B> obtainConsumer(
+            BiConsumer<? super A, ? super B> function,
+            A arg1, ArgumentPlaceholder<B> arg2) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 2, 1, ReturnType.VOID, arg1, arg2, null, null);
+    }
+
+    /**
+     * {@link PooledPredicate} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 placeholder for a missing argument. Use {@link #__} to get one
+     * @return a {@link PooledPredicate}, equivalent to lambda:
+     *         {@code (arg2) -> function(arg1, arg2) }
+     */
+    static <A, B> PooledPredicate<B> obtainPredicate(
+            BiPredicate<? super A, ? super B> function,
+            A arg1, ArgumentPlaceholder<B> arg2) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null);
+    }
+
+    /**
+     * {@link PooledFunction} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 placeholder for a missing argument. Use {@link #__} to get one
+     * @return a {@link PooledFunction}, equivalent to lambda:
+     *         {@code (arg2) -> function(arg1, arg2) }
+     */
+    static <A, B, R> PooledFunction<B, R> obtainFunction(
+            BiFunction<? super A, ? super B, ? extends R> function,
+            A arg1, ArgumentPlaceholder<B> arg2) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null);
+    }
+
+    /**
+     * Factory of {@link Message}s that contain an
+     * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+     * {@link Message#getCallback internal callback}.
+     *
+     * The callback is equivalent to one obtainable via
+     * {@link #obtainRunnable(BiConsumer, Object, Object)}
+     *
+     * Note that using this method with {@link android.os.Handler#handleMessage}
+     * is more efficient than the alternative of {@link android.os.Handler#post}
+     * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+     * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+     *
+     * You may optionally set a {@link Message#what} for the message if you want to be
+     * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+     * there's no need to do so
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @return a {@link Message} invoking {@code function(arg1, arg2) } when handled
+     */
+    static <A, B> Message obtainMessage(
+            BiConsumer<? super A, ? super B> function,
+            A arg1, B arg2) {
+        synchronized (Message.sPoolSync) {
+            PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+                    function, 2, 0, ReturnType.VOID, arg1, arg2, null, null);
+            return Message.obtain().setCallback(callback.recycleOnUse());
+        }
+    }
+
+    /**
+     * {@link PooledRunnable} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @return a {@link PooledRunnable}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2, arg3) }
+     */
+    static <A, B, C> PooledRunnable obtainRunnable(
+            TriConsumer<? super A, ? super B, ? super C> function,
+            A arg1, B arg2, C arg3) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null);
+    }
+
+    /**
+     * {@link PooledSupplier} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @return a {@link PooledSupplier}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2, arg3) }
+     */
+    static <A, B, C, R> PooledSupplier<R> obtainSupplier(
+            TriFunction<? super A, ? super B, ? super C, ? extends R> function,
+            A arg1, B arg2, C arg3) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null);
+    }
+
+    /**
+     * {@link PooledConsumer} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 placeholder for a missing argument. Use {@link #__} to get one
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @return a {@link PooledConsumer}, equivalent to lambda:
+     *         {@code (arg1) -> function(arg1, arg2, arg3) }
+     */
+    static <A, B, C> PooledConsumer<A> obtainConsumer(
+            TriConsumer<? super A, ? super B, ? super C> function,
+            ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null);
+    }
+
+    /**
+     * {@link PooledFunction} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 placeholder for a missing argument. Use {@link #__} to get one
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @return a {@link PooledFunction}, equivalent to lambda:
+     *         {@code (arg1) -> function(arg1, arg2, arg3) }
+     */
+    static <A, B, C, R> PooledFunction<A, R> obtainFunction(
+            TriFunction<? super A, ? super B, ? super C, ? extends R> function,
+            ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null);
+    }
+
+    /**
+     * {@link PooledConsumer} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 placeholder for a missing argument. Use {@link #__} to get one
+     * @param arg3 parameter supplied to {@code function} on call
+     * @return a {@link PooledConsumer}, equivalent to lambda:
+     *         {@code (arg2) -> function(arg1, arg2, arg3) }
+     */
+    static <A, B, C> PooledConsumer<B> obtainConsumer(
+            TriConsumer<? super A, ? super B, ? super C> function,
+            A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null);
+    }
+
+    /**
+     * {@link PooledFunction} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 placeholder for a missing argument. Use {@link #__} to get one
+     * @param arg3 parameter supplied to {@code function} on call
+     * @return a {@link PooledFunction}, equivalent to lambda:
+     *         {@code (arg2) -> function(arg1, arg2, arg3) }
+     */
+    static <A, B, C, R> PooledFunction<B, R> obtainFunction(
+            TriFunction<? super A, ? super B, ? super C, ? extends R> function,
+            A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null);
+    }
+
+    /**
+     * {@link PooledConsumer} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 placeholder for a missing argument. Use {@link #__} to get one
+     * @return a {@link PooledConsumer}, equivalent to lambda:
+     *         {@code (arg3) -> function(arg1, arg2, arg3) }
+     */
+    static <A, B, C> PooledConsumer<C> obtainConsumer(
+            TriConsumer<? super A, ? super B, ? super C> function,
+            A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null);
+    }
+
+    /**
+     * {@link PooledFunction} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 placeholder for a missing argument. Use {@link #__} to get one
+     * @return a {@link PooledFunction}, equivalent to lambda:
+     *         {@code (arg3) -> function(arg1, arg2, arg3) }
+     */
+    static <A, B, C, R> PooledFunction<C, R> obtainFunction(
+            TriFunction<? super A, ? super B, ? super C, ? extends R> function,
+            A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null);
+    }
+
+    /**
+     * Factory of {@link Message}s that contain an
+     * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+     * {@link Message#getCallback internal callback}.
+     *
+     * The callback is equivalent to one obtainable via
+     * {@link #obtainRunnable(TriConsumer, Object, Object, Object)}
+     *
+     * Note that using this method with {@link android.os.Handler#handleMessage}
+     * is more efficient than the alternative of {@link android.os.Handler#post}
+     * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+     * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+     *
+     * You may optionally set a {@link Message#what} for the message if you want to be
+     * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+     * there's no need to do so
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @return a {@link Message} invoking {@code function(arg1, arg2, arg3) } when handled
+     */
+    static <A, B, C> Message obtainMessage(
+            TriConsumer<? super A, ? super B, ? super C> function,
+            A arg1, B arg2, C arg3) {
+        synchronized (Message.sPoolSync) {
+            PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+                    function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null);
+            return Message.obtain().setCallback(callback.recycleOnUse());
+        }
+    }
+
+    /**
+     * {@link PooledRunnable} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @return a {@link PooledRunnable}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2, arg3, arg4) }
+     */
+    static <A, B, C, D> PooledRunnable obtainRunnable(
+            QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
+            A arg1, B arg2, C arg3, D arg4) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4);
+    }
+
+    /**
+     * {@link PooledSupplier} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @return a {@link PooledSupplier}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2, arg3, arg4) }
+     */
+    static <A, B, C, D, R> PooledSupplier<R> obtainSupplier(
+            QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
+            A arg1, B arg2, C arg3, D arg4) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4);
+    }
+
+    /**
+     * {@link PooledConsumer} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 placeholder for a missing argument. Use {@link #__} to get one
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @return a {@link PooledConsumer}, equivalent to lambda:
+     *         {@code (arg1) -> function(arg1, arg2, arg3, arg4) }
+     */
+    static <A, B, C, D> PooledConsumer<A> obtainConsumer(
+            QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
+            ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4);
+    }
+
+    /**
+     * {@link PooledFunction} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 placeholder for a missing argument. Use {@link #__} to get one
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @return a {@link PooledFunction}, equivalent to lambda:
+     *         {@code (arg1) -> function(arg1, arg2, arg3, arg4) }
+     */
+    static <A, B, C, D, R> PooledFunction<A, R> obtainFunction(
+            QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
+            ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4);
+    }
+
+    /**
+     * {@link PooledConsumer} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 placeholder for a missing argument. Use {@link #__} to get one
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @return a {@link PooledConsumer}, equivalent to lambda:
+     *         {@code (arg2) -> function(arg1, arg2, arg3, arg4) }
+     */
+    static <A, B, C, D> PooledConsumer<B> obtainConsumer(
+            QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
+            A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4);
+    }
+
+    /**
+     * {@link PooledFunction} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 placeholder for a missing argument. Use {@link #__} to get one
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @return a {@link PooledFunction}, equivalent to lambda:
+     *         {@code (arg2) -> function(arg1, arg2, arg3, arg4) }
+     */
+    static <A, B, C, D, R> PooledFunction<B, R> obtainFunction(
+            QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
+            A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4);
+    }
+
+    /**
+     * {@link PooledConsumer} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 placeholder for a missing argument. Use {@link #__} to get one
+     * @param arg4 parameter supplied to {@code function} on call
+     * @return a {@link PooledConsumer}, equivalent to lambda:
+     *         {@code (arg3) -> function(arg1, arg2, arg3, arg4) }
+     */
+    static <A, B, C, D> PooledConsumer<C> obtainConsumer(
+            QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
+            A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4);
+    }
+
+    /**
+     * {@link PooledFunction} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 placeholder for a missing argument. Use {@link #__} to get one
+     * @param arg4 parameter supplied to {@code function} on call
+     * @return a {@link PooledFunction}, equivalent to lambda:
+     *         {@code (arg3) -> function(arg1, arg2, arg3, arg4) }
+     */
+    static <A, B, C, D, R> PooledFunction<C, R> obtainFunction(
+            QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
+            A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4);
+    }
+
+    /**
+     * {@link PooledConsumer} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 placeholder for a missing argument. Use {@link #__} to get one
+     * @return a {@link PooledConsumer}, equivalent to lambda:
+     *         {@code (arg4) -> function(arg1, arg2, arg3, arg4) }
+     */
+    static <A, B, C, D> PooledConsumer<D> obtainConsumer(
+            QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
+            A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4);
+    }
+
+    /**
+     * {@link PooledFunction} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 placeholder for a missing argument. Use {@link #__} to get one
+     * @return a {@link PooledFunction}, equivalent to lambda:
+     *         {@code (arg4) -> function(arg1, arg2, arg3, arg4) }
+     */
+    static <A, B, C, D, R> PooledFunction<D, R> obtainFunction(
+            QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
+            A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4);
+    }
+
+    /**
+     * Factory of {@link Message}s that contain an
+     * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+     * {@link Message#getCallback internal callback}.
+     *
+     * The callback is equivalent to one obtainable via
+     * {@link #obtainRunnable(QuadConsumer, Object, Object, Object, Object)}
+     *
+     * Note that using this method with {@link android.os.Handler#handleMessage}
+     * is more efficient than the alternative of {@link android.os.Handler#post}
+     * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+     * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+     *
+     * You may optionally set a {@link Message#what} for the message if you want to be
+     * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+     * there's no need to do so
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4) } when handled
+     */
+    static <A, B, C, D> Message obtainMessage(
+            QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
+            A arg1, B arg2, C arg3, D arg4) {
+        synchronized (Message.sPoolSync) {
+            PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+                    function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4);
+            return Message.obtain().setCallback(callback.recycleOnUse());
+        }
+    }
+}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
new file mode 100755
index 0000000..03e013c
--- /dev/null
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -0,0 +1,557 @@
+/*
+ * 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.internal.util.function.pooled;
+
+import android.annotation.Nullable;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pools;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.BitUtils;
+import com.android.internal.util.function.QuadConsumer;
+import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.QuadPredicate;
+import com.android.internal.util.function.TriConsumer;
+import com.android.internal.util.function.TriFunction;
+import com.android.internal.util.function.TriPredicate;
+
+import java.util.Arrays;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+/**
+ * @see PooledLambda
+ * @hide
+ */
+final class PooledLambdaImpl<R> extends OmniFunction<Object, Object, Object, Object, R> {
+
+    private static final boolean DEBUG = false;
+    private static final String LOG_TAG = "PooledLambdaImpl";
+
+    private static final int MAX_ARGS = 4;
+
+    private static final int MAX_POOL_SIZE = 50;
+
+    static class Pool extends Pools.SynchronizedPool<PooledLambdaImpl> {
+
+        public Pool(Object lock) {
+            super(MAX_POOL_SIZE, lock);
+        }
+    }
+
+    static final Pool sPool = new Pool(new Object());
+    static final Pool sMessageCallbacksPool = new Pool(Message.sPoolSync);
+
+    private PooledLambdaImpl() {}
+
+    /**
+     * The function reference to be invoked
+     *
+     * May be the return value itself in case when an immediate result constant is provided instead
+     */
+    Object mFunc;
+
+    /**
+     * A primitive result value to be immediately returned on invocation instead of calling
+     * {@link #mFunc}
+     */
+    long mConstValue;
+
+    /**
+     * Arguments for {@link #mFunc}
+     */
+    @Nullable Object[] mArgs = null;
+
+    /**
+     * Flag for {@link #mFlags}
+     *
+     * Indicates whether this instance is recycled
+     */
+    private static final int FLAG_RECYCLED = 1 << MAX_ARGS;
+
+    /**
+     * Flag for {@link #mFlags}
+     *
+     * Indicates whether this instance should be immediately recycled on invocation
+     * (as requested via {@link PooledLambda#recycleOnUse()}) or not(default)
+     */
+    private static final int FLAG_RECYCLE_ON_USE = 1 << (MAX_ARGS + 1);
+
+    /**
+     * Flag for {@link #mFlags}
+     *
+     * Indicates that this instance was acquired from {@link #sMessageCallbacksPool} as opposed to
+     * {@link #sPool}
+     */
+    private static final int FLAG_ACQUIRED_FROM_MESSAGE_CALLBACKS_POOL = 1 << (MAX_ARGS + 2);
+
+    /** @see #mFlags */
+    static final int MASK_EXPOSED_AS = LambdaType.MASK << (MAX_ARGS + 3);
+
+    /** @see #mFlags */
+    static final int MASK_FUNC_TYPE = LambdaType.MASK <<
+            (MAX_ARGS + 3 + LambdaType.MASK_BIT_COUNT);
+
+    /**
+     * Bit schema:
+     * AAAABCDEEEEEEFFFFFF
+     *
+     * Where:
+     * A - whether {@link #mArgs arg} at corresponding index was specified at
+     * {@link #acquire creation time} (0) or {@link #invoke invocation time} (1)
+     * B - {@link #FLAG_RECYCLED}
+     * C - {@link #FLAG_RECYCLE_ON_USE}
+     * D - {@link #FLAG_ACQUIRED_FROM_MESSAGE_CALLBACKS_POOL}
+     * E - {@link LambdaType} representing the type of the lambda returned to the caller from a
+     * factory method
+     * F - {@link LambdaType} of {@link #mFunc} as resolved when calling a factory method
+     */
+    int mFlags = 0;
+
+
+    @Override
+    public void recycle() {
+        if (DEBUG) Log.i(LOG_TAG, this + ".recycle()");
+        if (!isRecycled()) doRecycle();
+    }
+
+    private void doRecycle() {
+        if (DEBUG) Log.i(LOG_TAG, this + ".doRecycle()");
+        Pool pool = (mFlags & FLAG_ACQUIRED_FROM_MESSAGE_CALLBACKS_POOL) != 0
+                ? PooledLambdaImpl.sMessageCallbacksPool
+                : PooledLambdaImpl.sPool;
+
+        mFunc = null;
+        if (mArgs != null) Arrays.fill(mArgs, null);
+        mFlags = FLAG_RECYCLED;
+        mConstValue = 0L;
+
+        pool.release(this);
+    }
+
+    @Override
+    R invoke(Object a1, Object a2, Object a3, Object a4) {
+        checkNotRecycled();
+        if (DEBUG) {
+            Log.i(LOG_TAG, this + ".invoke("
+                    + commaSeparateFirstN(
+                            new Object[] { a1, a2, a3, a4 },
+                            LambdaType.decodeArgCount(getFlags(MASK_EXPOSED_AS)))
+                    + ")");
+        }
+        boolean ignored = fillInArg(a1) && fillInArg(a2) && fillInArg(a3) && fillInArg(a4);
+        int argCount = LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE));
+        if (argCount != LambdaType.MASK_ARG_COUNT) {
+            for (int i = 0; i < argCount; i++) {
+                if (mArgs[i] == ArgumentPlaceholder.INSTANCE) {
+                    throw new IllegalStateException("Missing argument #" + i + " among "
+                            + Arrays.toString(mArgs));
+                }
+            }
+        }
+        try {
+            return doInvoke();
+        } finally {
+            if (isRecycleOnUse()) doRecycle();
+            if (!isRecycled()) {
+                int argsSize = ArrayUtils.size(mArgs);
+                for (int i = 0; i < argsSize; i++) {
+                    popArg(i);
+                }
+            }
+        }
+    }
+
+    private boolean fillInArg(Object invocationArg) {
+        int argsSize = ArrayUtils.size(mArgs);
+        for (int i = 0; i < argsSize; i++) {
+            if (mArgs[i] == ArgumentPlaceholder.INSTANCE) {
+                mArgs[i] = invocationArg;
+                mFlags |= BitUtils.bitAt(i);
+                return true;
+            }
+        }
+        if (invocationArg != null && invocationArg != ArgumentPlaceholder.INSTANCE) {
+            throw new IllegalStateException("No more arguments expected for provided arg "
+                    + invocationArg + " among " + Arrays.toString(mArgs));
+        }
+        return false;
+    }
+
+    private void checkNotRecycled() {
+        if (isRecycled()) throw new IllegalStateException("Instance is recycled: " + this);
+    }
+
+    @SuppressWarnings("unchecked")
+    private R doInvoke() {
+        final int funcType = getFlags(MASK_FUNC_TYPE);
+        final int argCount = LambdaType.decodeArgCount(funcType);
+        final int returnType = LambdaType.decodeReturnType(funcType);
+
+        switch (argCount) {
+            case LambdaType.MASK_ARG_COUNT: {
+                switch (returnType) {
+                    case LambdaType.ReturnType.INT: return (R) (Integer) getAsInt();
+                    case LambdaType.ReturnType.LONG: return (R) (Long) getAsLong();
+                    case LambdaType.ReturnType.DOUBLE: return (R) (Double) getAsDouble();
+                    default: return (R) mFunc;
+                }
+            }
+            case 0: {
+                switch (returnType) {
+                    case LambdaType.ReturnType.VOID: {
+                        ((Runnable) mFunc).run();
+                        return null;
+                    }
+                    case LambdaType.ReturnType.BOOLEAN:
+                    case LambdaType.ReturnType.OBJECT: {
+                        return (R) ((Supplier) mFunc).get();
+                    }
+                }
+            } break;
+            case 1: {
+                switch (returnType) {
+                    case LambdaType.ReturnType.VOID: {
+                        ((Consumer) mFunc).accept(popArg(0));
+                        return null;
+                    }
+                    case LambdaType.ReturnType.BOOLEAN: {
+                        return (R) (Object) ((Predicate) mFunc).test(popArg(0));
+                    }
+                    case LambdaType.ReturnType.OBJECT: {
+                        return (R) ((Function) mFunc).apply(popArg(0));
+                    }
+                }
+            } break;
+            case 2: {
+                switch (returnType) {
+                    case LambdaType.ReturnType.VOID: {
+                        ((BiConsumer) mFunc).accept(popArg(0), popArg(1));
+                        return null;
+                    }
+                    case LambdaType.ReturnType.BOOLEAN: {
+                        return (R) (Object) ((BiPredicate) mFunc).test(popArg(0), popArg(1));
+                    }
+                    case LambdaType.ReturnType.OBJECT: {
+                        return (R) ((BiFunction) mFunc).apply(popArg(0), popArg(1));
+                    }
+                }
+            } break;
+            case 3: {
+                switch (returnType) {
+                    case LambdaType.ReturnType.VOID: {
+                        ((TriConsumer) mFunc).accept(popArg(0), popArg(1), popArg(2));
+                        return null;
+                    }
+                    case LambdaType.ReturnType.BOOLEAN: {
+                        return (R) (Object) ((TriPredicate) mFunc).test(
+                                popArg(0), popArg(1), popArg(2));
+                    }
+                    case LambdaType.ReturnType.OBJECT: {
+                        return (R) ((TriFunction) mFunc).apply(popArg(0), popArg(1), popArg(2));
+                    }
+                }
+            } break;
+            case 4: {
+                switch (returnType) {
+                    case LambdaType.ReturnType.VOID: {
+                        ((QuadConsumer) mFunc).accept(popArg(0), popArg(1), popArg(2), popArg(3));
+                        return null;
+                    }
+                    case LambdaType.ReturnType.BOOLEAN: {
+                        return (R) (Object) ((QuadPredicate) mFunc).test(
+                                popArg(0), popArg(1), popArg(2), popArg(3));
+                    }
+                    case LambdaType.ReturnType.OBJECT: {
+                        return (R) ((QuadFunction) mFunc).apply(
+                                popArg(0), popArg(1), popArg(2), popArg(3));
+                    }
+                }
+            } break;
+        }
+        throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType));
+    }
+
+    private boolean isConstSupplier() {
+        return LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE)) == LambdaType.MASK_ARG_COUNT;
+    }
+
+    private Object popArg(int index) {
+        Object result = mArgs[index];
+        if (isInvocationArgAtIndex(index)) {
+            mArgs[index] = ArgumentPlaceholder.INSTANCE;
+            mFlags &= ~BitUtils.bitAt(index);
+        }
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        if (isRecycled()) return "<recycled PooledLambda@" + hashCodeHex(this) + ">";
+
+        StringBuilder sb = new StringBuilder();
+        if (isConstSupplier()) {
+            sb.append(getFuncTypeAsString()).append("(").append(doInvoke()).append(")");
+        } else {
+            if (mFunc instanceof PooledLambdaImpl) {
+                sb.append(mFunc);
+            } else {
+                sb.append(getFuncTypeAsString()).append("@").append(hashCodeHex(mFunc));
+            }
+            sb.append("(");
+            sb.append(commaSeparateFirstN(mArgs, LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE))));
+            sb.append(")");
+        }
+        return sb.toString();
+    }
+
+    private String commaSeparateFirstN(@Nullable Object[] arr, int n) {
+        if (arr == null) return "";
+        return TextUtils.join(",", Arrays.copyOf(arr, n));
+    }
+
+    private static String hashCodeHex(Object o) {
+        return Integer.toHexString(o.hashCode());
+    }
+
+    private String getFuncTypeAsString() {
+        if (isRecycled()) throw new IllegalStateException();
+        if (isConstSupplier()) return "supplier";
+        String name = LambdaType.toString(getFlags(MASK_EXPOSED_AS));
+        if (name.endsWith("Consumer")) return "consumer";
+        if (name.endsWith("Function")) return "function";
+        if (name.endsWith("Predicate")) return "predicate";
+        if (name.endsWith("Supplier")) return "supplier";
+        if (name.endsWith("Runnable")) return "runnable";
+        throw new IllegalStateException("Don't know the string representation of " + name);
+    }
+
+    /**
+     * Internal non-typesafe factory method for {@link PooledLambdaImpl}
+     */
+    static <E extends PooledLambda> E acquire(Pool pool, Object f,
+            int fNumArgs, int numPlaceholders, int fReturnType,
+            Object a, Object b, Object c, Object d) {
+        PooledLambdaImpl r = acquire(pool);
+        if (DEBUG) {
+            Log.i(LOG_TAG,
+                    "acquire(this = @" + hashCodeHex(r)
+                            + ", f = " + f
+                            + ", fNumArgs = " + fNumArgs
+                            + ", numPlaceholders = " + numPlaceholders
+                            + ", fReturnType = " + LambdaType.ReturnType.toString(fReturnType)
+                            + ", a = " + a
+                            + ", b = " + b
+                            + ", c = " + c
+                            + ", d = " + d
+                            + ")");
+        }
+        r.mFunc = f;
+        r.setFlags(MASK_FUNC_TYPE, LambdaType.encode(fNumArgs, fReturnType));
+        r.setFlags(MASK_EXPOSED_AS, LambdaType.encode(numPlaceholders, fReturnType));
+        if (ArrayUtils.size(r.mArgs) < fNumArgs) r.mArgs = new Object[fNumArgs];
+        setIfInBounds(r.mArgs, 0, a);
+        setIfInBounds(r.mArgs, 1, b);
+        setIfInBounds(r.mArgs, 2, c);
+        setIfInBounds(r.mArgs, 3, d);
+        return (E) r;
+    }
+
+    static PooledLambdaImpl acquireConstSupplier(int type) {
+        PooledLambdaImpl r = acquire(PooledLambdaImpl.sPool);
+        int lambdaType = LambdaType.encode(LambdaType.MASK_ARG_COUNT, type);
+        r.setFlags(PooledLambdaImpl.MASK_FUNC_TYPE, lambdaType);
+        r.setFlags(PooledLambdaImpl.MASK_EXPOSED_AS, lambdaType);
+        return r;
+    }
+
+    static PooledLambdaImpl acquire(Pool pool) {
+        PooledLambdaImpl r = pool.acquire();
+        if (r == null) r = new PooledLambdaImpl();
+        r.mFlags &= ~FLAG_RECYCLED;
+        r.setFlags(FLAG_ACQUIRED_FROM_MESSAGE_CALLBACKS_POOL,
+                pool == sMessageCallbacksPool ? 1 : 0);
+        return r;
+    }
+
+    private static void setIfInBounds(Object[] array, int i, Object a) {
+        if (i < ArrayUtils.size(array)) array[i] = a;
+    }
+
+    @Override
+    public OmniFunction<Object, Object, Object, Object, R> negate() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public <V> OmniFunction<Object, Object, Object, Object, V> andThen(
+            Function<? super R, ? extends V> after) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public double getAsDouble() {
+        return Double.longBitsToDouble(mConstValue);
+    }
+
+    @Override
+    public int getAsInt() {
+        return (int) mConstValue;
+    }
+
+    @Override
+    public long getAsLong() {
+        return mConstValue;
+    }
+
+    @Override
+    public OmniFunction<Object, Object, Object, Object, R> recycleOnUse() {
+        if (DEBUG) Log.i(LOG_TAG, this + ".recycleOnUse()");
+        mFlags |= FLAG_RECYCLE_ON_USE;
+        return this;
+    }
+
+    private boolean isRecycled() {
+        return (mFlags & FLAG_RECYCLED) != 0;
+    }
+
+    private boolean isRecycleOnUse() {
+        return (mFlags & FLAG_RECYCLE_ON_USE) != 0;
+    }
+
+    private boolean isInvocationArgAtIndex(int argIndex) {
+        return (mFlags & (1 << argIndex)) != 0;
+    }
+
+    int getFlags(int mask) {
+        return unmask(mask, mFlags);
+    }
+
+    void setFlags(int mask, int value) {
+        mFlags &= ~mask;
+        mFlags |= mask(mask, value);
+    }
+
+    /**
+     * 0xFF000, 0xAB -> 0xAB000
+     */
+    private static int mask(int mask, int value) {
+        return (value << Integer.numberOfTrailingZeros(mask)) & mask;
+    }
+
+    /**
+     * 0xFF000, 0xAB123 -> 0xAB
+     */
+    private static int unmask(int mask, int bits) {
+        return (bits & mask) / (1 << Integer.numberOfTrailingZeros(mask));
+    }
+
+    /**
+     * Contract for encoding a supported lambda type in {@link #MASK_BIT_COUNT} bits
+     */
+    static class LambdaType {
+        public static final int MASK_ARG_COUNT = 0b111;
+        public static final int MASK_RETURN_TYPE = 0b111000;
+        public static final int MASK = MASK_ARG_COUNT | MASK_RETURN_TYPE;
+        public static final int MASK_BIT_COUNT = 6;
+
+        static int encode(int argCount, int returnType) {
+            return mask(MASK_ARG_COUNT, argCount) | mask(MASK_RETURN_TYPE, returnType);
+        }
+
+        static int decodeArgCount(int type) {
+            return type & MASK_ARG_COUNT;
+        }
+
+        static int decodeReturnType(int type) {
+            return unmask(MASK_RETURN_TYPE, type);
+        }
+
+        static String toString(int type) {
+            int argCount = decodeArgCount(type);
+            int returnType = decodeReturnType(type);
+            if (argCount == 0) {
+                if (returnType == ReturnType.VOID) return "Runnable";
+                if (returnType == ReturnType.OBJECT || returnType == ReturnType.BOOLEAN) {
+                    return "Supplier";
+                }
+            }
+            return argCountPrefix(argCount) + ReturnType.lambdaSuffix(returnType);
+        }
+
+        private static String argCountPrefix(int argCount) {
+            switch (argCount) {
+                case MASK_ARG_COUNT: return "";
+                case 1: return "";
+                case 2: return "Bi";
+                case 3: return "Tri";
+                case 4: return "Quad";
+                default: throw new IllegalArgumentException("" + argCount);
+            }
+        }
+
+        static class ReturnType {
+            public static final int VOID = 1;
+            public static final int BOOLEAN = 2;
+            public static final int OBJECT = 3;
+            public static final int INT = 4;
+            public static final int LONG = 5;
+            public static final int DOUBLE = 6;
+
+            static String toString(int returnType) {
+                switch (returnType) {
+                    case VOID: return "VOID";
+                    case BOOLEAN: return "BOOLEAN";
+                    case OBJECT: return "OBJECT";
+                    case INT: return "INT";
+                    case LONG: return "LONG";
+                    case DOUBLE: return "DOUBLE";
+                    default: return "" + returnType;
+                }
+            }
+
+            static String lambdaSuffix(int type) {
+                return prefix(type) + suffix(type);
+            }
+
+            private static String prefix(int type) {
+                switch (type) {
+                    case INT: return "Int";
+                    case LONG: return "Long";
+                    case DOUBLE: return "Double";
+                    default: return "";
+                }
+            }
+
+            private static String suffix(int type) {
+                switch (type) {
+                    case VOID: return "Consumer";
+                    case BOOLEAN: return "Predicate";
+                    case OBJECT: return "Function";
+                    default: return "Supplier";
+                }
+            }
+        }
+    }
+}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledPredicate.java b/core/java/com/android/internal/util/function/pooled/PooledPredicate.java
new file mode 100644
index 0000000..9b14366
--- /dev/null
+++ b/core/java/com/android/internal/util/function/pooled/PooledPredicate.java
@@ -0,0 +1,36 @@
+/*
+ * 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.internal.util.function.pooled;
+
+import java.util.function.Predicate;
+
+/**
+ * {@link Predicate} + {@link PooledLambda}
+ *
+ * @see PooledLambda
+ * @hide
+ */
+public interface PooledPredicate<T> extends PooledLambda, Predicate<T> {
+
+    /**
+     * Ignores the result
+     */
+    PooledConsumer<T> asConsumer();
+
+    /** @inheritDoc */
+    PooledPredicate<T> recycleOnUse();
+}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledRunnable.java b/core/java/com/android/internal/util/function/pooled/PooledRunnable.java
new file mode 100644
index 0000000..89ca82e
--- /dev/null
+++ b/core/java/com/android/internal/util/function/pooled/PooledRunnable.java
@@ -0,0 +1,30 @@
+/*
+ * 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.internal.util.function.pooled;
+
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
+
+/**
+ * {@link Runnable} + {@link PooledLambda}
+ *
+ * @see PooledLambda
+ * @hide
+ */
+public interface PooledRunnable extends PooledLambda, Runnable, ThrowingRunnable {
+    /** @inheritDoc */
+    PooledRunnable recycleOnUse();
+}
diff --git a/core/java/com/android/internal/util/function/pooled/PooledSupplier.java b/core/java/com/android/internal/util/function/pooled/PooledSupplier.java
new file mode 100644
index 0000000..dd7f73e
--- /dev/null
+++ b/core/java/com/android/internal/util/function/pooled/PooledSupplier.java
@@ -0,0 +1,59 @@
+/*
+ * 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.internal.util.function.pooled;
+
+import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
+
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+
+/**
+ * {@link Supplier} + {@link PooledLambda}
+ *
+ * @see PooledLambda
+ * @hide
+ */
+public interface PooledSupplier<T> extends PooledLambda, Supplier<T>, ThrowingSupplier<T> {
+
+    /**
+     * Ignores the result
+     */
+    PooledRunnable asRunnable();
+
+    /** @inheritDoc */
+    PooledSupplier<T> recycleOnUse();
+
+    /** {@link PooledLambda} + {@link IntSupplier} */
+    interface OfInt extends IntSupplier, PooledLambda {
+        /** @inheritDoc */
+        PooledSupplier.OfInt recycleOnUse();
+    }
+
+    /** {@link PooledLambda} + {@link LongSupplier} */
+    interface OfLong extends LongSupplier, PooledLambda {
+        /** @inheritDoc */
+        PooledSupplier.OfLong recycleOnUse();
+    }
+
+    /** {@link PooledLambda} + {@link DoubleSupplier} */
+    interface OfDouble extends DoubleSupplier, PooledLambda {
+        /** @inheritDoc */
+        PooledSupplier.OfDouble recycleOnUse();
+    }
+}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 361fd3da..7178a0d 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -22,6 +22,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.util.MergedConfiguration;
+import android.view.DisplayCutout;
 import android.view.DragEvent;
 import android.view.IWindow;
 import android.view.IWindowSession;
@@ -41,7 +42,8 @@
     public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
             Rect stableInsets, Rect outsets, boolean reportDraw,
             MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
-            boolean alwaysConsumeNavBar, int displayId) {
+            boolean alwaysConsumeNavBar, int displayId,
+            DisplayCutout.ParcelableWrapper displayCutout) {
         if (reportDraw) {
             try {
                 mSession.finishDrawing(this);
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index b979807..ca8624d 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -36,6 +36,7 @@
 interface IInputMethodManager {
     // TODO: Use ParceledListSlice instead
     List<InputMethodInfo> getInputMethodList();
+    List<InputMethodInfo> getVrInputMethodList();
     // TODO: Use ParceledListSlice instead
     List<InputMethodInfo> getEnabledInputMethodList();
     List<InputMethodSubtype> getEnabledInputMethodSubtypeList(in String imiId,
diff --git a/core/java/com/android/internal/view/RotationPolicy.java b/core/java/com/android/internal/view/RotationPolicy.java
index b479cb1..d7b9132 100644
--- a/core/java/com/android/internal/view/RotationPolicy.java
+++ b/core/java/com/android/internal/view/RotationPolicy.java
@@ -108,11 +108,19 @@
      * Enables or disables rotation lock from the system UI toggle.
      */
     public static void setRotationLock(Context context, final boolean enabled) {
+        final int rotation = areAllRotationsAllowed(context) ? CURRENT_ROTATION : NATURAL_ROTATION;
+        setRotationLockAtAngle(context, enabled, rotation);
+    }
+
+    /**
+     * Enables or disables rotation lock at a specific rotation from system UI.
+     */
+    public static void setRotationLockAtAngle(Context context, final boolean enabled,
+            final int rotation) {
         Settings.System.putIntForUser(context.getContentResolver(),
                 Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, 0,
                 UserHandle.USER_CURRENT);
 
-        final int rotation = areAllRotationsAllowed(context) ? CURRENT_ROTATION : NATURAL_ROTATION;
         setRotationLock(enabled, rotation);
     }
 
diff --git a/core/java/com/android/internal/widget/ExploreByTouchHelper.java b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
index 759a41a..50ad547 100644
--- a/core/java/com/android/internal/widget/ExploreByTouchHelper.java
+++ b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
@@ -186,9 +186,6 @@
         }
 
         final AccessibilityEvent event = createEvent(virtualViewId, eventType);
-        if (event == null) {
-            return false;
-        }
         return parent.requestSendAccessibilityEvent(mView, event);
     }
 
@@ -243,9 +240,6 @@
             if (parent != null) {
                 final AccessibilityEvent event = createEvent(virtualViewId,
                         AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
-                if (event == null) {
-                    return;
-                }
                 event.setContentChangeTypes(changeTypes);
                 parent.requestSendAccessibilityEvent(mView, event);
             }
@@ -311,9 +305,6 @@
      *         the specified item.
      */
     private AccessibilityEvent createEventForHost(int eventType) {
-        if (!AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
-            return null;
-        }
         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
         mView.onInitializeAccessibilityEvent(event);
 
@@ -334,9 +325,6 @@
      *         the specified item.
      */
     private AccessibilityEvent createEventForChild(int virtualViewId, int eventType) {
-        if (!AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
-            return null;
-        }
         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
         event.setEnabled(true);
         event.setClassName(DEFAULT_CLASS_NAME);
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index 26023b4..e013553 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -21,6 +21,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
 import android.util.Pair;
 import android.view.Gravity;
@@ -203,6 +204,11 @@
     public void onViewAdded(View child) {
         super.onViewAdded(child);
         clearMeasureOrder();
+        // For some reason ripples + notification actions seem to be an unhappy combination
+        // b/69474443 so just turn them off for now.
+        if (child.getBackground() instanceof RippleDrawable) {
+            ((RippleDrawable)child.getBackground()).setForceSoftware(true);
+        }
     }
 
     @Override
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 961bcb9..8df0fb8 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -84,6 +84,7 @@
         "android_view_VelocityTracker.cpp",
         "android_text_AndroidCharacter.cpp",
         "android_text_Hyphenator.cpp",
+        "android_text_MeasuredText.cpp",
         "android_text_StaticLayout.cpp",
         "android_os_Debug.cpp",
         "android_os_GraphicsEnvironment.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f41aacd..9e907bd 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -177,6 +177,7 @@
 extern int register_android_net_TrafficStats(JNIEnv* env);
 extern int register_android_text_AndroidCharacter(JNIEnv *env);
 extern int register_android_text_Hyphenator(JNIEnv *env);
+extern int register_android_text_MeasuredText(JNIEnv* env);
 extern int register_android_text_StaticLayout(JNIEnv *env);
 extern int register_android_opengl_classes(JNIEnv *env);
 extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
@@ -1333,6 +1334,7 @@
     REG_JNI(register_android_content_XmlBlock),
     REG_JNI(register_android_text_AndroidCharacter),
     REG_JNI(register_android_text_Hyphenator),
+    REG_JNI(register_android_text_MeasuredText),
     REG_JNI(register_android_text_StaticLayout),
     REG_JNI(register_android_view_InputDevice),
     REG_JNI(register_android_view_KeyCharacterMap),
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index e9ea702..6f4a58a 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -96,6 +96,11 @@
     tree->setAllowCaching(allowCaching);
 }
 
+static void setAntiAlias(JNIEnv*, jobject, jlong treePtr, jboolean aa) {
+    VectorDrawable::Tree* tree = reinterpret_cast<VectorDrawable::Tree*>(treePtr);
+    tree->setAntiAlias(aa);
+}
+
 /**
  * Draw
  */
@@ -363,6 +368,7 @@
         {"nSetRendererViewportSize", "(JFF)V", (void*)setTreeViewportSize},
         {"nSetRootAlpha", "(JF)Z", (void*)setRootAlpha},
         {"nGetRootAlpha", "(J)F", (void*)getRootAlpha},
+        {"nSetAntiAlias", "(JZ)V", (void*)setAntiAlias},
         {"nSetAllowCaching", "(JZ)V", (void*)setAllowCaching},
 
         {"nCreateFullPath", "()J", (void*)createEmptyFullPath},
diff --git a/core/jni/android_text_MeasuredText.cpp b/core/jni/android_text_MeasuredText.cpp
new file mode 100644
index 0000000..af9d131
--- /dev/null
+++ b/core/jni/android_text_MeasuredText.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MeasuredText"
+
+#include "ScopedIcuLocale.h"
+#include "unicode/locid.h"
+#include "unicode/brkiter.h"
+#include "utils/misc.h"
+#include "utils/Log.h"
+#include <nativehelper/ScopedStringChars.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/JNIHelp.h>
+#include "core_jni_helpers.h"
+#include <cstdint>
+#include <vector>
+#include <list>
+#include <algorithm>
+
+#include "SkPaint.h"
+#include "SkTypeface.h"
+#include <hwui/MinikinSkia.h>
+#include <hwui/MinikinUtils.h>
+#include <hwui/Paint.h>
+#include <minikin/FontCollection.h>
+#include <minikin/AndroidLineBreakerHelper.h>
+#include <minikin/MinikinFont.h>
+
+namespace android {
+
+static inline minikin::MeasuredTextBuilder* toBuilder(jlong ptr) {
+    return reinterpret_cast<minikin::MeasuredTextBuilder*>(ptr);
+}
+
+static inline Paint* toPaint(jlong ptr) {
+    return reinterpret_cast<Paint*>(ptr);
+}
+
+static inline minikin::MeasuredText* toMeasuredText(jlong ptr) {
+    return reinterpret_cast<minikin::MeasuredText*>(ptr);
+}
+
+template<typename Ptr> static inline jlong toJLong(Ptr ptr) {
+    return reinterpret_cast<jlong>(ptr);
+}
+
+static void releaseMeasuredText(jlong measuredTextPtr) {
+    delete toMeasuredText(measuredTextPtr);
+}
+
+// Regular JNI
+static jlong nInitBuilder() {
+    return toJLong(new minikin::MeasuredTextBuilder());
+}
+
+// Regular JNI
+static void nAddStyleRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr,
+                         jlong paintPtr, jint start, jint end, jboolean isRtl) {
+    Paint* paint = toPaint(paintPtr);
+    const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface());
+    minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface);
+    toBuilder(builderPtr)->addStyleRun(start, end, std::move(minikinPaint),
+                                       typeface->fFontCollection, isRtl);
+}
+
+// Regular JNI
+static void nAddReplacementRun(JNIEnv* /* unused */, jclass /* unused */, jlong builderPtr,
+                               jlong paintPtr, jint start, jint end, jfloat width) {
+    toBuilder(builderPtr)->addReplacementRun(start, end, width,
+                                             toPaint(paintPtr)->getMinikinLocaleListId());
+}
+
+// Regular JNI
+static jlong nBuildNativeMeasuredText(JNIEnv* env, jclass /* unused */, jlong builderPtr,
+                                      jcharArray javaText) {
+    ScopedCharArrayRO text(env, javaText);
+    const minikin::U16StringPiece textBuffer(text.get(), text.size());
+
+    // Pass the ownership to Java.
+    return toJLong(toBuilder(builderPtr)->build(textBuffer).release());
+}
+
+// Regular JNI
+static void nFreeBuilder(JNIEnv* env, jclass /* unused */, jlong builderPtr) {
+    delete toBuilder(builderPtr);
+}
+
+// CriticalNative
+static jlong nGetReleaseFunc() {
+    return toJLong(&releaseMeasuredText);
+}
+
+static const JNINativeMethod gMethods[] = {
+    // MeasuredTextBuilder native functions.
+    {"nInitBuilder", "()J", (void*) nInitBuilder},
+    {"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
+    {"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
+    {"nBuildNativeMeasuredText", "(J[C)J", (void*) nBuildNativeMeasuredText},
+    {"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
+
+    // MeasuredText native functions.
+    {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc},  // Critical Natives
+};
+
+int register_android_text_MeasuredText(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/text/MeasuredText", gMethods, NELEM(gMethods));
+}
+
+}
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 601bd98..b5c23df 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -115,6 +115,7 @@
 static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
         // Inputs
         jcharArray javaText,
+        jlong measuredTextPtr,
         jint length,
         jfloat firstWidth,
         jint firstWidthLineCount,
@@ -139,39 +140,20 @@
     ScopedNullableIntArrayRO tabStops(env, variableTabStops);
 
     minikin::U16StringPiece u16Text(text.get(), length);
-    minikin::MeasuredText measuredText = builder->measureText(u16Text);
+    minikin::MeasuredText* measuredText = reinterpret_cast<minikin::MeasuredText*>(measuredTextPtr);
     minikin::LineBreakResult result = builder->computeBreaks(
-            u16Text, measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset,
+            u16Text, *measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset,
             tabStops.get(), tabStops.size(), defaultTabStop);
 
     recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents,
             recycleFlags, recycleLength, result);
 
-    env->SetFloatArrayRegion(charWidths, 0, measuredText.widths.size(), measuredText.widths.data());
-
-    builder->clearRuns();
+    env->SetFloatArrayRegion(charWidths, 0, measuredText->widths.size(),
+                             measuredText->widths.data());
 
     return static_cast<jint>(result.breakPoints.size());
 }
 
-// Basically similar to Paint.getTextRunAdvances but with C++ interface
-// CriticalNative
-static void nAddStyleRun(jlong nativePtr, jlong nativePaint, jint start, jint end, jboolean isRtl) {
-    minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
-    Paint* paint = reinterpret_cast<Paint*>(nativePaint);
-    const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface());
-    minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(paint, typeface);
-    builder->addStyleRun(start, end, std::move(minikinPaint), typeface->fFontCollection, isRtl);
-}
-
-// CriticalNative
-static void nAddReplacementRun(jlong nativePtr, jlong nativePaint, jint start, jint end,
-        jfloat width) {
-    minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
-    Paint* paint = reinterpret_cast<Paint*>(nativePaint);
-    builder->addReplacementRun(start, end, width, paint->getMinikinLocaleListId());
-}
-
 static const JNINativeMethod gMethods[] = {
     // Fast Natives
     {"nInit", "("
@@ -185,8 +167,6 @@
 
     // Critical Natives
     {"nFinish", "(J)V", (void*) nFinish},
-    {"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
-    {"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
 
     // Regular JNI
     {"nComputeLineBreaks", "("
@@ -194,6 +174,7 @@
 
         // Inputs
         "[C"  // text
+        "J"  // MeasuredText ptr.
         "I"  // length
         "F"  // firstWidth
         "I"  // firstWidthLineCount
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 71742cb..0a90b97 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -124,7 +124,7 @@
 static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
         jclass clazz, jstring nameObj) {
     const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
-    String8 name(nameChars);
+    std::string name = nameChars;
     env->ReleaseStringUTFChars(nameObj, nameChars);
 
     sp<InputChannel> serverChannel;
@@ -166,7 +166,7 @@
     if (nativeInputChannel) {
         if (finalized) {
             ALOGW("Input channel object '%s' was finalized without being disposed!",
-                    nativeInputChannel->getInputChannel()->getName().string());
+                    nativeInputChannel->getInputChannel()->getName().c_str());
         }
 
         nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj);
@@ -212,7 +212,7 @@
                 return;
             }
 
-            InputChannel* inputChannel = new InputChannel(name, dupFd);
+            InputChannel* inputChannel = new InputChannel(name.string(), dupFd);
             NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
 
             android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel);
@@ -230,7 +230,7 @@
             sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
 
             parcel->writeInt32(1);
-            parcel->writeString8(inputChannel->getName());
+            parcel->writeString8(String8(inputChannel->getName().c_str()));
             parcel->writeDupFileDescriptor(inputChannel->getFd());
         } else {
             parcel->writeInt32(0);
@@ -245,7 +245,7 @@
         return NULL;
     }
 
-    jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().string());
+    jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().c_str());
     return name;
 }
 
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index c457ab0..52c84a4 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -79,7 +79,7 @@
     void setFdEvents(int events);
 
     const char* getInputChannelName() {
-        return mInputConsumer.getChannel()->getName().string();
+        return mInputConsumer.getChannel()->getName().c_str();
     }
 
     virtual int handleEvent(int receiveFd, int events, void* data);
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 58ccef18..f87abac 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -71,7 +71,7 @@
     uint32_t mNextPublishedSeq;
 
     const char* getInputChannelName() {
-        return mInputPublisher.getChannel()->getName().string();
+        return mInputPublisher.getChannel()->getName().c_str();
     }
 
     virtual int handleEvent(int receiveFd, int events, void* data);
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 8c968a2..5e5d59b 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -868,11 +868,40 @@
             capabilities.getDesiredMaxAverageLuminance(), capabilities.getDesiredMinLuminance());
 }
 
+static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
+    Parcel* parcel = parcelForJavaObject(env, parcelObj);
+    if (parcel == NULL) {
+        doThrowNPE(env);
+        return 0;
+    }
+    sp<SurfaceControl> surface = SurfaceControl::readFromParcel(parcel);
+    if (surface == nullptr) {
+        return 0;
+    }
+    surface->incStrong((void *)nativeCreate);
+    return reinterpret_cast<jlong>(surface.get());
+}
+
+static void nativeWriteToParcel(JNIEnv* env, jclass clazz,
+        jlong nativeObject, jobject parcelObj) {
+    Parcel* parcel = parcelForJavaObject(env, parcelObj);
+    if (parcel == NULL) {
+        doThrowNPE(env);
+        return;
+    }
+    SurfaceControl* const self = reinterpret_cast<SurfaceControl *>(nativeObject);
+    self->writeToParcel(parcel);
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod sSurfaceControlMethods[] = {
     {"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJII)J",
             (void*)nativeCreate },
+    {"nativeReadFromParcel", "(Landroid/os/Parcel;)J",
+            (void*)nativeReadFromParcel },
+    {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
+            (void*)nativeWriteToParcel },
     {"nativeRelease", "(J)V",
             (void*)nativeRelease },
     {"nativeDestroy", "(J)V",
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 519a885..9f3475a 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -937,6 +937,11 @@
     Properties::enableHighContrastText = enable;
 }
 
+static void android_view_ThreadedRenderer_hackySetRTAnimationsEnabled(JNIEnv*, jclass,
+        jboolean enable) {
+    Properties::enableRTAnimations = enable;
+}
+
 // ----------------------------------------------------------------------------
 // FrameMetricsObserver
 // ----------------------------------------------------------------------------
@@ -1041,6 +1046,8 @@
             (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode },
     { "disableVsync", "()V", (void*)android_view_ThreadedRenderer_disableVsync },
     { "nSetHighContrastText", "(Z)V", (void*)android_view_ThreadedRenderer_setHighContrastText },
+    { "nHackySetRTAnimationsEnabled", "(Z)V",
+            (void*)android_view_ThreadedRenderer_hackySetRTAnimationsEnabled },
 };
 
 static JavaVM* mJvm = nullptr;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 0228edb..915fe9d 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -200,6 +200,7 @@
 message WindowContainerProto {
   optional ConfigurationContainerProto configuration_container = 1;
   optional int32 orientation = 2;
+  optional bool visible = 3;
 }
 
 /* represents ConfigurationContainer */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d9fb230..0920426 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3616,6 +3616,11 @@
     <permission android:name="android.permission.ACCESS_SHORTCUTS"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Allows an application to read the runtime profiles of other apps.
+         @hide <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.READ_RUNTIME_PROFILES"
+                android:protectionLevel="signature|privileged" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/res/res/drawable/ic_logout.xml b/core/res/res/drawable/ic_logout.xml
new file mode 100644
index 0000000..c016ec8
--- /dev/null
+++ b/core/res/res/drawable/ic_logout.xml
@@ -0,0 +1,15 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <group>
+        <clip-path android:pathData="M0,0h24v24H0V0z M 0,0" />
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17.0,7.0l-1.4099998,1.4099998l2.58,2.5900002l-10.17,0.0l0.0,2.0l10.17,0.0l-2.58,2.58l1.4099998,1.4200001l5.0,-5.0z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,5h8V3H4C2.9,3,2,3.9,2,5v14c0,1.1,0.9,2,2,2h8v-2H4V5z"/>
+    </group>
+</vector>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index b4240cd..440b2690 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -79,8 +79,7 @@
     <string name="RestrictedOnAllVoiceTitle" msgid="158800171499150681">"কোনো ভয়েস/জরুরী পরিষেবা নেই"</string>
     <string name="RestrictedStateContent" msgid="4278821484643362350">"সাময়িকভাবে মোবাইল নেটওয়ার্ক আপনার অবস্থানে এই পরিষেবা দিচ্ছে না"</string>
     <string name="NetworkPreferenceSwitchTitle" msgid="4008877505368566980">"নেটওয়ার্কের সিগন্যাল নেই"</string>
-    <!-- no translation found for NetworkPreferenceSwitchSummary (7056776609127756440) -->
-    <skip />
+    <string name="NetworkPreferenceSwitchSummary" msgid="7056776609127756440">"আরও ভাল সিগন্যাল পাওয়ার জন্য সেটিংস &gt; নেটওয়ার্ক এবং ইন্টারনেট &gt; মোবাইল নেটওয়ার্ক &gt; পছন্দের নেটওয়ার্কের ধরন বিকল্পে গিয়ে অন্য ধরনের নেটওয়ার্ক বেছে নিয়ে দেখুন।"</string>
     <string name="EmergencyCallWarningTitle" msgid="4790413876281901612">"ওয়াই-ফাই কলিং সক্রিয় আছে"</string>
     <string name="EmergencyCallWarningSummary" msgid="8973232888021643293">"জরুরি কলের জন্য মোবাইল নেটওয়ার্ক থাকতে হবে।"</string>
     <string name="notification_channel_network_alert" msgid="4427736684338074967">"সতর্কতা"</string>
@@ -213,6 +212,8 @@
     <string name="global_action_power_off" msgid="4471879440839879722">"পাওয়ার বন্ধ করুন"</string>
     <string name="global_action_emergency" msgid="7112311161137421166">"জরুরী"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"ত্রুটির প্রতিবেদন"</string>
+    <!-- no translation found for global_action_logout (935179188218826050) -->
+    <skip />
     <string name="bugreport_title" msgid="2667494803742548533">"ত্রুটির অভিযোগ করুন"</string>
     <string name="bugreport_message" msgid="398447048750350456">"এটি একটি ই-মেল মেসেজ পাঠানোর জন্য আপনার ডিভাইসের বর্তমান অবস্থা সম্পর্কে তথ্য সংগ্রহ করবে৷ ত্রুটির প্রতিবেদন শুরুর সময় থেকে এটি পাঠানোর জন্য প্রস্তুত হতে কিছুটা সময় নেবে; অনুগ্রহ করে ধৈর্য রাখুন৷"</string>
     <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ইন্টারেক্টিভ প্রতিবেদন"</string>
@@ -1052,12 +1053,9 @@
     <string name="screen_compat_mode_hint" msgid="1064524084543304459">"সিস্টেম সেটিংস&gt; অ্যাপ্স&gt; ডাউনলোড করাগুলি এ এটি পুনঃসক্ষম করুন৷"</string>
     <string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g>, বর্তমান প্রদর্শনের আকারের সেটিংস সমর্থন করে না এবং অপ্রত্যাশিত আচরণ করতে পারে৷"</string>
     <string name="unsupported_display_size_show" msgid="7969129195360353041">"সর্বদা দেখান"</string>
-    <!-- no translation found for unsupported_compile_sdk_message (5030433583092006591) -->
-    <skip />
-    <!-- no translation found for unsupported_compile_sdk_show (2681877855260970231) -->
-    <skip />
-    <!-- no translation found for unsupported_compile_sdk_check_update (3312723623323216101) -->
-    <skip />
+    <string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> Android OS এর একটি সামঞ্জস্যহীন ভার্সনের জন্য তৈরি করা হয়েছিল, তাই এখানে সেটি অস্বাভাবিক আচরণ করতে পারে। মনে হয় অ্যাপটির আপডেট করা ভার্সনও আছে।"</string>
+    <string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"সবসময় দেখুন"</string>
+    <string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"আপডেট পাওয়া যাচ্ছে কিনা দেখুন"</string>
     <string name="smv_application" msgid="3307209192155442829">"অ্যাপ্লিকেশানটি <xliff:g id="APPLICATION">%1$s</xliff:g> (প্রক্রিয়া <xliff:g id="PROCESS">%2$s</xliff:g>) তার স্ব-প্রয়োগ করা কঠোর মোড নীতি লঙ্ঘন করেছে৷"</string>
     <string name="smv_process" msgid="5120397012047462446">"প্রক্রিয়াটি <xliff:g id="PROCESS">%1$s</xliff:g> তার স্ব-প্রয়োগ করা কঠোর মোড নীতি লঙ্ঘন করেছে৷"</string>
     <string name="android_upgrading_title" msgid="1584192285441405746">"Android আপগ্রেড করা হচ্ছে..."</string>
@@ -1123,12 +1121,10 @@
     <string name="network_available_sign_in" msgid="1848877297365446605">"নেটওয়ার্কে সাইন-ইন করুন"</string>
     <!-- no translation found for network_available_sign_in_detailed (8000081941447976118) -->
     <skip />
-    <!-- no translation found for wifi_no_internet (8938267198124654938) -->
-    <skip />
+    <string name="wifi_no_internet" msgid="8938267198124654938">"ওয়াই-ফাই এ ইন্টারনেট অ্যাক্সেস নেই"</string>
     <string name="wifi_no_internet_detailed" msgid="8083079241212301741">"বিকল্পগুলির জন্য আলতো চাপুন"</string>
     <string name="network_switch_metered" msgid="4671730921726992671">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> এ পাল্টানো হয়েছে"</string>
-    <!-- no translation found for network_switch_metered_detail (775163331794506615) -->
-    <skip />
+    <string name="network_switch_metered_detail" msgid="775163331794506615">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> এ ইন্টারনেট অ্যাক্সেস না থাকলে <xliff:g id="NEW_NETWORK">%1$s</xliff:g> ব্যবহার করা হয়৷ ডেটা চার্জ প্রযোজ্য৷"</string>
     <string name="network_switch_metered_toast" msgid="5779283181685974304">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> থেকে <xliff:g id="NEW_NETWORK">%2$s</xliff:g> এ পাল্টানো হয়েছে"</string>
   <string-array name="network_switch_type_name">
     <item msgid="3979506840912951943">"মোবাইল ডেটা"</item>
@@ -1505,6 +1501,10 @@
     <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"শর্টকাটটি চালু থাকলে দুটি ভলিউম বোতাম একসাথে ৩ সেকেন্ড টিপে ধরে রাখলে একটি অ্যাকসেসিবিলিটি বৈশিষ্ট্য চালু হবে।\n\n বর্তমান অ্যাকসেসিবিলিটি বৈশিষ্ট্য:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n আপনি এই বৈশিষ্ট্যটি সেটিংস &gt; অ্যাকসেসিবিলিটিতে গিয়ে পরিবর্তন করতে পারবেন।"</string>
     <string name="disable_accessibility_shortcut" msgid="627625354248453445">"শর্টকাট বন্ধ করুন"</string>
     <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"শর্টকাট ব্যবহার করুন"</string>
+    <!-- no translation found for color_inversion_feature_name (4231186527799958644) -->
+    <skip />
+    <!-- no translation found for color_correction_feature_name (6779391426096954933) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"অ্যাক্সেসযোগ্যতা শর্টকাট <xliff:g id="SERVICE_NAME">%1$s</xliff:g> কে চালু করেছে"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"অ্যাক্সেসযোগ্যতা শর্টকাট <xliff:g id="SERVICE_NAME">%1$s</xliff:g> কে বন্ধ করেছে"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"অ্যাক্সেসযোগ্যতা বোতামের সাহায্যে যে বৈশিষ্ট্যটি নিয়ন্ত্রণ করতে চান, সেটি বেছে নিন:"</string>
@@ -1644,7 +1644,7 @@
     <string name="package_installed_device_owner" msgid="6875717669960212648">"আপনার প্রশাসক ইনস্টল করেছেন"</string>
     <string name="package_updated_device_owner" msgid="1847154566357862089">"আপনার প্রশাসক আপডেট করেছেন"</string>
     <string name="package_deleted_device_owner" msgid="2307122077550236438">"আপনার প্রশাসক মুছে দিয়েছেন"</string>
-    <string name="battery_saver_description" msgid="1960431123816253034">"ব্যাটরির লাইফ উন্নত করতে, ব্যাটারি সাশ্রয়কারী আপনার ডিভাইসের কার্যসম্পাদনা হ্রাস করে এবং কম্পন, লোকেশন পরিষেবাগুলি এবং অধিকাংশ ব্যাকগ্রাউন্ড ডেটা সীমিত করে৷ ইমেল, মেসেজিং এবং অন্যান্য অ্যাপ্লিকেশনগুলিকে যেগুলি সিঙ্কের উপর নির্ভর করে সেগুলিকে আপনি না খোলা পর্যন্ত নাও আপডেট হতে পারে৷\n\nআপনার ডিভাইসটিকে যখন চার্জ করা হয় তখন ব্যাটারি সাশ্রয়কারী নিজে থেকে বন্ধ হয়ে যায়৷"</string>
+    <string name="battery_saver_description" msgid="5394663545060026162">"ব্যাটরির লাইফ উন্নত করে সাহায্য করতে, ব্যাটারি সেভার আপনার ডিভাইসের পারফরম্যান্স কমিয়ে দেয় এবং কম্পন, লোকেশন পরিষেবা এবং অধিকাংশ ব্যাকগ্রাউন্ড ডেটা সীমিত করে। ইমেল, মেসেজিং এবং সিঙ্কের উপর নির্ভর করে এমন অন্যান্য অ্যাপগুলিকে আপনি না খোলা পর্যন্ত নাও আপডেট হতে পারে।\n\nআপনার ডিভাইসটিকে যখন চার্জ করা হয় তখন ব্যাটারি সেভার নিজে থেকে বন্ধ হয়ে যায়।"</string>
     <string name="data_saver_description" msgid="6015391409098303235">"ডেটার ব্যবহার কমাতে সহায়তা করার জন্য, ডেটা সেভার পটভূমিতে কিছু অ্যাপ্লিকেশনকে ডেটা পাঠাতে বা গ্রহণ করতে বাধা দেয়৷ আপনি বর্তমানে এমন একটি অ্যাপ্লিকেশন ব্যবহার করছেন যেটি ডেটা অ্যাক্সেস করতে পারে, তবে সেটি কমই করে৷ এর ফলে যা হতে পারে, উদাহরণস্বরূপ, আপনি ছবিগুলিতে আলতো চাপ না দেওয়া পর্যন্ত সেগুলি প্রদর্শিত হবে না৷"</string>
     <string name="data_saver_enable_title" msgid="4674073932722787417">"ডেটা সেভার চালু করবেন?"</string>
     <string name="data_saver_enable_button" msgid="7147735965247211818">"চালু করুন"</string>
@@ -1691,6 +1691,8 @@
     <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"সপ্তাহান্তের রাত্রি"</string>
     <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"সপ্তাহান্ত"</string>
     <string name="zen_mode_default_events_name" msgid="8158334939013085363">"ইভেন্ট"</string>
+    <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) -->
+    <skip />
     <string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> দ্বারা নিঃশব্দ করা হয়েছে"</string>
     <string name="system_error_wipe_data" msgid="6608165524785354962">"আপনার ডিভাইসে একটি অভ্যন্তরীন সমস্যা হয়েছে, এবং আপনি যতক্ষণ না পর্যন্ত এটিকে ফ্যাক্টরি ডেটা রিসেট করছেন ততক্ষণ এটি ঠিকভাবে কাজ নাও করতে পারে৷"</string>
     <string name="system_error_manufacturer" msgid="8086872414744210668">"আপনার ডিভাইসে একটি অভ্যন্তরীন সমস্যা হয়েছে৷ বিস্তারিত জানার জন্য প্রস্তুতকারকের সাথে যোগাযোগ করুন৷"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index ef30b91..c9c98eb 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -214,6 +214,8 @@
     <string name="global_action_power_off" msgid="4471879440839879722">"Isključi telefon"</string>
     <string name="global_action_emergency" msgid="7112311161137421166">"Hitno"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Izvještaj o greškama"</string>
+    <!-- no translation found for global_action_logout (935179188218826050) -->
+    <skip />
     <string name="bugreport_title" msgid="2667494803742548533">"Kreirajte izvještaj o greškama"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Ovim će se prikupljati informacije o trenutnom stanju uređaja, koji će biti poslani kao e-poruka. Može malo potrajati dok se izvještaj o greškama ne kreira i bude spreman za slanje. Budite strpljivi."</string>
     <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Interaktivni izvještaj"</string>
@@ -1026,9 +1028,9 @@
     <string name="whichEditApplication" msgid="144727838241402655">"Uredi koristeći"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"Uredi koristeći %1$s"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Uredi"</string>
-    <string name="whichSendApplication" msgid="6902512414057341668">"Podijeli koristeći"</string>
-    <string name="whichSendApplicationNamed" msgid="2799370240005424391">"Podijeli koristeći %1$s"</string>
-    <string name="whichSendApplicationLabel" msgid="4579076294675975354">"Podijeli"</string>
+    <string name="whichSendApplication" msgid="6902512414057341668">"Dijeli koristeći"</string>
+    <string name="whichSendApplicationNamed" msgid="2799370240005424391">"Dijeli koristeći %1$s"</string>
+    <string name="whichSendApplicationLabel" msgid="4579076294675975354">"Dijeli"</string>
     <string name="whichSendToApplication" msgid="8272422260066642057">"Pošalji koristeći"</string>
     <string name="whichSendToApplicationNamed" msgid="7768387871529295325">"Pošalji koristeći %1$s"</string>
     <string name="whichSendToApplicationLabel" msgid="8878962419005813500">"Pošalji"</string>
@@ -1353,7 +1355,7 @@
     <string name="progress_erasing" product="default" msgid="6596988875507043042">"Brisanje SD kartice..."</string>
     <string name="share" msgid="1778686618230011964">"Podijelite"</string>
     <string name="find" msgid="4808270900322985960">"Pronađi"</string>
-    <string name="websearch" msgid="4337157977400211589">"Internet pretraga"</string>
+    <string name="websearch" msgid="4337157977400211589">"Internet pretraživanje"</string>
     <string name="find_next" msgid="5742124618942193978">"Nađi sljedeći"</string>
     <string name="find_previous" msgid="2196723669388360506">"Nađi prethodni"</string>
     <string name="gpsNotifTicker" msgid="5622683912616496172">"Korisnik <xliff:g id="NAME">%s</xliff:g> je poslao zahtjev za utvrđivanje lokacije"</string>
@@ -1397,7 +1399,7 @@
     <string name="activitychooserview_choose_application" msgid="2125168057199941199">"Odaberite aplikaciju"</string>
     <string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"Aplikacija <xliff:g id="APPLICATION_NAME">%s</xliff:g> se ne može pokrenuti."</string>
     <string name="shareactionprovider_share_with" msgid="806688056141131819">"Podijeliti sa"</string>
-    <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Podijeli koristeći aplikaciju <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Dijeli koristeći aplikaciju <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="content_description_sliding_handle" msgid="415975056159262248">"Klizni regulator. Dodirnite &amp; držite."</string>
     <string name="description_target_unlock_tablet" msgid="3833195335629795055">"Prevucite za otključavanje ekrana."</string>
     <string name="action_bar_home_description" msgid="5293600496601490216">"Vratite se na početnu stranicu"</string>
@@ -1524,6 +1526,10 @@
     <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Kada je prečica uključena, pritiskom na oba dugmeta za podešavanje jačine zvuka u trajanju od 3 sekunde pokrenut će se funkcija za pristupačnost.\n\n Trenutna funkcija za pristupačnost je:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Funkciju možete promijeniti ako odete u Postavke &gt; Pristupačnost."</string>
     <string name="disable_accessibility_shortcut" msgid="627625354248453445">"Isključi prečicu"</string>
     <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Koristi prečicu"</string>
+    <!-- no translation found for color_inversion_feature_name (4231186527799958644) -->
+    <skip />
+    <!-- no translation found for color_correction_feature_name (6779391426096954933) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Prečica za pristupačnost je uključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Prečica za pristupačnost je isključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Odaberite funkciju koja će se koristiti kada dodirnete dugme Pristupačnost:"</string>
@@ -1719,6 +1725,8 @@
     <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Radni dan uvečer"</string>
     <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Vikend"</string>
     <string name="zen_mode_default_events_name" msgid="8158334939013085363">"Događaj"</string>
+    <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) -->
+    <skip />
     <string name="muted_by" msgid="6147073845094180001">"Ton isključila aplikacija <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
     <string name="system_error_wipe_data" msgid="6608165524785354962">"Postoji problem u vašem uređaju i može biti nestabilan dok ga ne vratite na fabričke postavke."</string>
     <string name="system_error_manufacturer" msgid="8086872414744210668">"Postoji problem u vašem uređaju. Za više informacija obratite se proizvođaču."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 7d9e531..e1b4e0d 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -212,6 +212,8 @@
     <string name="global_action_power_off" msgid="4471879440839879722">"خاموش کردن"</string>
     <string name="global_action_emergency" msgid="7112311161137421166">"اضطراری"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"گزارش اشکال"</string>
+    <!-- no translation found for global_action_logout (935179188218826050) -->
+    <skip />
     <string name="bugreport_title" msgid="2667494803742548533">"گرفتن گزارش اشکال"</string>
     <string name="bugreport_message" msgid="398447048750350456">"این گزارش اطلاعات مربوط به وضعیت دستگاه کنونی شما را جمع‌آوری می‌کند تا به صورت یک پیام رایانامه ارسال شود. از زمان شروع گزارش اشکال تا آماده شدن برای ارسال اندکی زمان می‌برد؛ لطفاً شکیبا باشید."</string>
     <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"گزارش تعاملی"</string>
@@ -1498,6 +1500,10 @@
     <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"وقتی میان‌بر روشن است،‌ اگر هر دو دکمه صدا را ۳ ثانیه فشار دهید یکی از قابلیت‌های دسترس‌پذیری شروع می‌شود.\n\n قابلیت دسترس‌پذیری کنونی:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n می‌توانید در «تنظیمات &gt; دسترس‌پذیری»، قابلیت را تغییر دهید."</string>
     <string name="disable_accessibility_shortcut" msgid="627625354248453445">"خاموش کردن میان‌بر"</string>
     <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"استفاده از میان‌بر"</string>
+    <!-- no translation found for color_inversion_feature_name (4231186527799958644) -->
+    <skip />
+    <!-- no translation found for color_correction_feature_name (6779391426096954933) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"«میان‌بر دسترس‌پذیری» <xliff:g id="SERVICE_NAME">%1$s</xliff:g> را روشن کرد"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"«میان‌بر دسترس‌پذیری» <xliff:g id="SERVICE_NAME">%1$s</xliff:g> را خاموش کرد"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"قابلیتی را انتخاب کنید که هنگام ضربه زدن روی دکمه «دسترس‌پذیری» استفاده می‌شود:"</string>
@@ -1684,6 +1690,8 @@
     <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"شب آخر هفته"</string>
     <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"آخر هفته"</string>
     <string name="zen_mode_default_events_name" msgid="8158334939013085363">"رویداد"</string>
+    <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) -->
+    <skip />
     <string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> آن را بی‌صدا کرد"</string>
     <string name="system_error_wipe_data" msgid="6608165524785354962">"دستگاهتان یک مشکل داخلی دارد، و ممکن است تا زمانی که بازنشانی داده‌های کارخانه انجام نگیرد، بی‌ثبات بماند."</string>
     <string name="system_error_manufacturer" msgid="8086872414744210668">"دستگاهتان یک مشکل داخلی دارد. برای جزئیات آن با سازنده‌تان تماس بگیرید."</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 19e7b34..c499ea7 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -212,6 +212,8 @@
     <string name="global_action_power_off" msgid="4471879440839879722">"Matikan perangkat"</string>
     <string name="global_action_emergency" msgid="7112311161137421166">"Darurat"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Laporan bug"</string>
+    <!-- no translation found for global_action_logout (935179188218826050) -->
+    <skip />
     <string name="bugreport_title" msgid="2667494803742548533">"Ambil laporan bug"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Ini akan mengumpulkan informasi status perangkat Anda saat ini, untuk dikirimkan sebagai pesan email. Harap bersabar, mungkin perlu waktu untuk memulai laporan bug hingga siap dikirim."</string>
     <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Laporan interaktif"</string>
@@ -1498,6 +1500,10 @@
     <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Saat pintasan aktif, menekan kedua tombol volume selama 3 detik akan memulai fitur aksesibilitas.\n\n Fitur aksesibilitas saat ini:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Anda dapat mengubah fitur di Setelan &gt; Aksesibilitas."</string>
     <string name="disable_accessibility_shortcut" msgid="627625354248453445">"Nonaktifkan Pintasan"</string>
     <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Gunakan Pintasan"</string>
+    <!-- no translation found for color_inversion_feature_name (4231186527799958644) -->
+    <skip />
+    <!-- no translation found for color_correction_feature_name (6779391426096954933) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Pintasan Aksesibilitas mengaktifkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Pintasan Aksesibilitas menonaktifkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Pilih fitur yang akan digunakan saat menge-tap tombol Aksesibilitas:"</string>
@@ -1684,6 +1690,8 @@
     <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Malam hari kerja"</string>
     <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Akhir pekan"</string>
     <string name="zen_mode_default_events_name" msgid="8158334939013085363">"Acara"</string>
+    <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) -->
+    <skip />
     <string name="muted_by" msgid="6147073845094180001">"Dinonaktifkan oleh <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
     <string name="system_error_wipe_data" msgid="6608165524785354962">"Ada masalah dengan perangkat. Hal ini mungkin membuat perangkat jadi tidak stabil dan perlu dikembalikan ke setelan pabrik."</string>
     <string name="system_error_manufacturer" msgid="8086872414744210668">"Ada masalah dengan perangkat. Hubungi produsen perangkat untuk informasi selengkapnya."</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 00bc9c2..ce07805 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -212,6 +212,8 @@
     <string name="global_action_power_off" msgid="4471879440839879722">"კვების გამორთვა"</string>
     <string name="global_action_emergency" msgid="7112311161137421166">"საგანგებო სამსახურები"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"ხარვეზის შესახებ ანგარიში"</string>
+    <!-- no translation found for global_action_logout (935179188218826050) -->
+    <skip />
     <string name="bugreport_title" msgid="2667494803742548533">"შექმენით შეცდომის ანგარიში"</string>
     <string name="bugreport_message" msgid="398447048750350456">"იგი შეაგროვებს ინფორმაციას თქვენი მოწყობილობის ამჟამინდელი მდგომარეობის შესახებ, რათა ის ელფოსტის შეტყობინების სახით გააგზავნოს. ხარვეზის ანგარიშის მომზადებასა და შეტყობინების გაგზავნას გარკვეული დრო სჭირდება. გთხოვთ, მოითმინოთ."</string>
     <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"ინტერაქტიული ანგარიში"</string>
@@ -1051,7 +1053,7 @@
     <string name="screen_compat_mode_hint" msgid="1064524084543304459">"ხელახალი გააქტიურება განყოფილებაში: სისტემის პარამეტრები &gt; აპები &gt; ჩამოტვირთულები."</string>
     <string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ის მიერ ეკრანის ამჟამინდელი პარამეტრები მხარდაუჭერელია და შეიძლება არასათანადოდ იმუშაოს."</string>
     <string name="unsupported_display_size_show" msgid="7969129195360353041">"ყოველთვის ჩვენება"</string>
-    <string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> შექმნილია Android-ის ოპერატიული სისტემის არათავსებადი ვერსიისთვის და შეიძლება გაუმართავად იმუშაოს. შესაძლოა ხელმისაწვდომი იყოს აპის განახლებული ვერსია."</string>
+    <string name="unsupported_compile_sdk_message" msgid="4253168368781441759">"<xliff:g id="APP_NAME">%1$s</xliff:g> შექმნილია Android-ის ოპერაციული სისტემის არათავსებადი ვერსიისთვის და შეიძლება გაუმართავად იმუშაოს. შესაძლოა ხელმისაწვდომი იყოს აპის განახლებული ვერსია."</string>
     <string name="unsupported_compile_sdk_show" msgid="2681877855260970231">"ყოველთვის ჩვენება"</string>
     <string name="unsupported_compile_sdk_check_update" msgid="3312723623323216101">"განახლების შემოწმება"</string>
     <string name="smv_application" msgid="3307209192155442829">"აპმა <xliff:g id="APPLICATION">%1$s</xliff:g> (პროცესი <xliff:g id="PROCESS">%2$s</xliff:g>) დაარღვია საკუთარი StrictMode დებულება."</string>
@@ -1498,6 +1500,10 @@
     <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"მალსახმობის ჩართვის შემთხვევაში, ხმის ორივე ღილაკზე 3 წამის განმავლობაში დაჭერით მარტივი წვდომის ფუნქცია ჩაირთვება.\n\n მარტივი წვდომის ამჟამინდელი ფუნქციაა:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n ამ ფუნქციის შეცვლა შეგიძლიათ აქ: პარამეტრები &gt; მარტივი წვდომა."</string>
     <string name="disable_accessibility_shortcut" msgid="627625354248453445">"მალსახმობის გამორთვა"</string>
     <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"მალსახმობის გამოყენება"</string>
+    <!-- no translation found for color_inversion_feature_name (4231186527799958644) -->
+    <skip />
+    <!-- no translation found for color_correction_feature_name (6779391426096954933) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"მარტივი წვდომის მალსახმობმა ჩართო <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"მარტივი წვდომის მალსახმობმა გამორთო <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"აირჩიეთ მარტივი წვდომის ღილაკზე შეხებისას გამოსაყენებელი ფუნქცია:"</string>
@@ -1684,6 +1690,8 @@
     <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"სამუშაო კვირის ღამე"</string>
     <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"შაბათ-კვირა"</string>
     <string name="zen_mode_default_events_name" msgid="8158334939013085363">"მოვლენა"</string>
+    <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) -->
+    <skip />
     <string name="muted_by" msgid="6147073845094180001">"დადუმებულია <xliff:g id="THIRD_PARTY">%1$s</xliff:g>-ის მიერ"</string>
     <string name="system_error_wipe_data" msgid="6608165524785354962">"ფიქსირდება თქვენი მ ოწყობილობის შიდა პრობლემა და შეიძლება არასტაბილური იყოს, სანამ ქარხნულ მონაცემების არ განაახლებთ."</string>
     <string name="system_error_manufacturer" msgid="8086872414744210668">"ფიქსირდება თქვენი მოწყობილობის შიდა პრობლემა. დეტალებისათვის, მიმართეთ თქვენს მწარმოებელს."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 8d02283..3329961 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -47,8 +47,8 @@
     <string name="needPuk2" msgid="4526033371987193070">"Introduza o PUK2 para desbloquear o cartão SIM."</string>
     <string name="enablePin" msgid="209412020907207950">"Ação sem êxito. Ative o bloqueio do SIM/RUIM."</string>
     <plurals name="pinpuk_attempts" formatted="false" msgid="1251012001539225582">
-      <item quantity="one">Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de o cartão SIM ficar bloqueado.</item>
       <item quantity="other">Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas antes de o cartão SIM ficar bloqueado.</item>
+      <item quantity="one">Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de o cartão SIM ficar bloqueado.</item>
     </plurals>
     <string name="imei" msgid="2625429890869005782">"IMEI"</string>
     <string name="meid" msgid="4841221237681254195">"MEID"</string>
@@ -162,8 +162,8 @@
     <string name="low_memory" product="tv" msgid="516619861191025923">"O armazenamento da TV está cheio. Elimine alguns ficheiros para libertar espaço."</string>
     <string name="low_memory" product="default" msgid="3475999286680000541">"O armazenamento do telemóvel está cheio. Elimine alguns ficheiros para libertar espaço."</string>
     <plurals name="ssl_ca_cert_warning" formatted="false" msgid="5106721205300213569">
-      <item quantity="one">Autoridade de certificação instalada</item>
       <item quantity="other">Autoridades de certificação instaladas</item>
+      <item quantity="one">Autoridade de certificação instalada</item>
     </plurals>
     <string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Por um terceiro desconhecido"</string>
     <string name="ssl_ca_cert_noti_by_administrator" msgid="3541729986326153557">"Pelo gestor do seu perfil de trabalho"</string>
@@ -212,6 +212,8 @@
     <string name="global_action_power_off" msgid="4471879440839879722">"Desligar"</string>
     <string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de erros"</string>
+    <!-- no translation found for global_action_logout (935179188218826050) -->
+    <skip />
     <string name="bugreport_title" msgid="2667494803742548533">"Criar relatório de erros"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Será recolhida informação sobre o estado atual do seu dispositivo a enviar através de uma mensagem de email. Demorará algum tempo até que o relatório de erro esteja pronto para ser enviado. Aguarde um pouco."</string>
     <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Relatório interativo"</string>
@@ -219,8 +221,8 @@
     <string name="bugreport_option_full_title" msgid="6354382025840076439">"Relatório completo"</string>
     <string name="bugreport_option_full_summary" msgid="7210859858969115745">"Utilize esta opção para uma interferência mínima do sistema quando o dispositivo não responder ou estiver demasiado lento, ou quando precisar de todas as secções de relatório. Não permite introduzir mais detalhes ou tirar capturas de ecrã adicionais."</string>
     <plurals name="bugreport_countdown" formatted="false" msgid="6878900193900090368">
-      <item quantity="one">A tirar uma captura de ecrã do relatório de erro dentro de <xliff:g id="NUMBER_0">%d</xliff:g> segundo…</item>
       <item quantity="other">A tirar uma captura de ecrã do relatório de erro dentro de <xliff:g id="NUMBER_1">%d</xliff:g> segundos.</item>
+      <item quantity="one">A tirar uma captura de ecrã do relatório de erro dentro de <xliff:g id="NUMBER_0">%d</xliff:g> segundo…</item>
     </plurals>
     <string name="global_action_toggle_silent_mode" msgid="8219525344246810925">"Modo silencioso"</string>
     <string name="global_action_silent_mode_on_status" msgid="3289841937003758806">"Som desativado"</string>
@@ -864,8 +866,8 @@
     <string name="oneMonthDurationPast" msgid="7396384508953779925">"Há 1 mês"</string>
     <string name="beforeOneMonthDurationPast" msgid="909134546836499826">"Há mais de 1 mês"</string>
     <plurals name="last_num_days" formatted="false" msgid="5104533550723932025">
-      <item quantity="one">Último <xliff:g id="COUNT_0">%d</xliff:g> dia</item>
       <item quantity="other">Últimos <xliff:g id="COUNT_1">%d</xliff:g> dias</item>
+      <item quantity="one">Último <xliff:g id="COUNT_0">%d</xliff:g> dia</item>
     </plurals>
     <string name="last_month" msgid="3959346739979055432">"Último mês"</string>
     <string name="older" msgid="5211975022815554840">"Mais antiga"</string>
@@ -886,68 +888,68 @@
     <string name="years" msgid="6881577717993213522">"anos"</string>
     <string name="now_string_shortest" msgid="8912796667087856402">"agora"</string>
     <plurals name="duration_minutes_shortest" formatted="false" msgid="3957499975064245495">
-      <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> m</item>
       <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> m</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> m</item>
     </plurals>
     <plurals name="duration_hours_shortest" formatted="false" msgid="3552182110578602356">
-      <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> h</item>
       <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> h</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> h</item>
     </plurals>
     <plurals name="duration_days_shortest" formatted="false" msgid="5213655532597081640">
-      <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> d</item>
       <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> d</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> d</item>
     </plurals>
     <plurals name="duration_years_shortest" formatted="false" msgid="7848711145196397042">
-      <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> a</item>
       <item quantity="other"><xliff:g id="COUNT_1">%d</xliff:g> a</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%d</xliff:g> a</item>
     </plurals>
     <plurals name="duration_minutes_shortest_future" formatted="false" msgid="3277614521231489951">
-      <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> min</item>
       <item quantity="other">dentro de <xliff:g id="COUNT_1">%d</xliff:g> min</item>
+      <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> min</item>
     </plurals>
     <plurals name="duration_hours_shortest_future" formatted="false" msgid="2152452368397489370">
-      <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> h</item>
       <item quantity="other">dentro de <xliff:g id="COUNT_1">%d</xliff:g> h</item>
+      <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> h</item>
     </plurals>
     <plurals name="duration_days_shortest_future" formatted="false" msgid="8088331502820295701">
-      <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> d</item>
       <item quantity="other">dentro de <xliff:g id="COUNT_1">%d</xliff:g> d</item>
+      <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> d</item>
     </plurals>
     <plurals name="duration_years_shortest_future" formatted="false" msgid="2317006667145250301">
-      <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> a</item>
       <item quantity="other">dentro de <xliff:g id="COUNT_1">%d</xliff:g> a</item>
+      <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> a</item>
     </plurals>
     <plurals name="duration_minutes_relative" formatted="false" msgid="3178131706192980192">
-      <item quantity="one">há <xliff:g id="COUNT_0">%d</xliff:g> minuto</item>
       <item quantity="other">há <xliff:g id="COUNT_1">%d</xliff:g> minutos</item>
+      <item quantity="one">há <xliff:g id="COUNT_0">%d</xliff:g> minuto</item>
     </plurals>
     <plurals name="duration_hours_relative" formatted="false" msgid="676894109982008411">
-      <item quantity="one">há <xliff:g id="COUNT_0">%d</xliff:g> hora</item>
       <item quantity="other">há <xliff:g id="COUNT_1">%d</xliff:g> horas</item>
+      <item quantity="one">há <xliff:g id="COUNT_0">%d</xliff:g> hora</item>
     </plurals>
     <plurals name="duration_days_relative" formatted="false" msgid="2203515825765397130">
-      <item quantity="one">há <xliff:g id="COUNT_0">%d</xliff:g> dia</item>
       <item quantity="other">há <xliff:g id="COUNT_1">%d</xliff:g> dias</item>
+      <item quantity="one">há <xliff:g id="COUNT_0">%d</xliff:g> dia</item>
     </plurals>
     <plurals name="duration_years_relative" formatted="false" msgid="4820062134188885734">
-      <item quantity="one">há <xliff:g id="COUNT_0">%d</xliff:g> ano</item>
       <item quantity="other">há <xliff:g id="COUNT_1">%d</xliff:g> anos</item>
+      <item quantity="one">há <xliff:g id="COUNT_0">%d</xliff:g> ano</item>
     </plurals>
     <plurals name="duration_minutes_relative_future" formatted="false" msgid="4655043589817680966">
-      <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> minuto</item>
       <item quantity="other">dentro de <xliff:g id="COUNT_1">%d</xliff:g> minutos</item>
+      <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> minuto</item>
     </plurals>
     <plurals name="duration_hours_relative_future" formatted="false" msgid="8084579714205223891">
-      <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> hora</item>
       <item quantity="other">dentro de <xliff:g id="COUNT_1">%d</xliff:g> horas</item>
+      <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> hora</item>
     </plurals>
     <plurals name="duration_days_relative_future" formatted="false" msgid="333215369363433992">
-      <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> dia</item>
       <item quantity="other">dentro de <xliff:g id="COUNT_1">%d</xliff:g> dias</item>
+      <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> dia</item>
     </plurals>
     <plurals name="duration_years_relative_future" formatted="false" msgid="8644862986413104011">
-      <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> ano</item>
       <item quantity="other">dentro de <xliff:g id="COUNT_1">%d</xliff:g> anos</item>
+      <item quantity="one">dentro de <xliff:g id="COUNT_0">%d</xliff:g> ano</item>
     </plurals>
     <string name="VideoView_error_title" msgid="3534509135438353077">"Problema com o vídeo"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"Este vídeo não é válido para transmissão em fluxo contínuo neste aparelho."</string>
@@ -1101,12 +1103,12 @@
     <string name="ringtone_picker_title_notification" msgid="4837740874822788802">"Sons de notificação"</string>
     <string name="ringtone_unknown" msgid="3914515995813061520">"Desconhecido"</string>
     <plurals name="wifi_available" formatted="false" msgid="7900333017752027322">
-      <item quantity="one">Rede Wi-Fi disponível</item>
       <item quantity="other">Redes Wi-Fi disponíveis</item>
+      <item quantity="one">Rede Wi-Fi disponível</item>
     </plurals>
     <plurals name="wifi_available_detailed" formatted="false" msgid="1140699367193975606">
-      <item quantity="one">Rede Wi-Fi aberta disponível</item>
       <item quantity="other">Redes Wi-Fi abertas disponíveis</item>
+      <item quantity="one">Rede Wi-Fi aberta disponível</item>
     </plurals>
     <string name="wifi_available_title" msgid="3817100557900599505">"Ligar à rede Wi-Fi aberta"</string>
     <string name="wifi_available_title_connecting" msgid="1557292688310330032">"A ligar à rede Wi-Fi aberta…"</string>
@@ -1320,8 +1322,8 @@
     <string name="no_matches" msgid="8129421908915840737">"Sem correspondências"</string>
     <string name="find_on_page" msgid="1946799233822820384">"Localizar na página"</string>
     <plurals name="matches_found" formatted="false" msgid="1210884353962081884">
-      <item quantity="one">1 correspondência</item>
       <item quantity="other"><xliff:g id="INDEX">%d</xliff:g> de <xliff:g id="TOTAL">%d</xliff:g></item>
+      <item quantity="one">1 correspondência</item>
     </plurals>
     <string name="action_mode_done" msgid="7217581640461922289">"Concluído"</string>
     <string name="progress_erasing" product="nosdcard" msgid="4521573321524340058">"A apagar memória de armazenamento USB..."</string>
@@ -1455,8 +1457,8 @@
     <string name="kg_wrong_password" msgid="2333281762128113157">"Palavra-passe Incorreta"</string>
     <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN Incorreto"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="8790651267324125694">
-      <item quantity="one">Tente novamente dentro de 1 segundo.</item>
       <item quantity="other">Tente novamente dentro de <xliff:g id="NUMBER">%d</xliff:g> segundos.</item>
+      <item quantity="one">Tente novamente dentro de 1 segundo.</item>
     </plurals>
     <string name="kg_pattern_instructions" msgid="398978611683075868">"Desenhe a sua sequência"</string>
     <string name="kg_sim_pin_instructions" msgid="2319508550934557331">"Introduzir PIN do cartão SIM"</string>
@@ -1498,6 +1500,10 @@
     <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Quando o atalho está ativado, premir ambos os botões de volume durante 3 segundos inicia uma funcionalidade de acessibilidade.\n\n Funcionalidade de acessibilidade atual:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Pode alterar a funcionalidade em Definições &gt; Acessibilidade."</string>
     <string name="disable_accessibility_shortcut" msgid="627625354248453445">"Desativar atalho"</string>
     <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Utilizar atalho"</string>
+    <!-- no translation found for color_inversion_feature_name (4231186527799958644) -->
+    <skip />
+    <!-- no translation found for color_correction_feature_name (6779391426096954933) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"O Atalho de acessibilidade ativou o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"O Atalho de acessibilidade desativou o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Escolha uma funcionalidade para utilizar quando tocar no botão Acessibilidade:"</string>
@@ -1610,8 +1616,8 @@
     <string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"Os PINs não correspondem. Tente novamente."</string>
     <string name="restr_pin_error_too_short" msgid="8173982756265777792">"O PIN é demasiado pequeno. Deve ter, no mínimo, 4 dígitos."</string>
     <plurals name="restr_pin_countdown" formatted="false" msgid="9061246974881224688">
-      <item quantity="one">Tente novamente dentro de 1 segundo</item>
       <item quantity="other">Tente novamente dentro de <xliff:g id="COUNT">%d</xliff:g> segundos</item>
+      <item quantity="one">Tente novamente dentro de 1 segundo</item>
     </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"Tente novamente mais tarde"</string>
     <string name="immersive_cling_title" msgid="8394201622932303336">"Visualização de ecrã inteiro"</string>
@@ -1642,36 +1648,36 @@
     <string name="data_saver_enable_title" msgid="4674073932722787417">"Ativar a Poupança de dados?"</string>
     <string name="data_saver_enable_button" msgid="7147735965247211818">"Ativar"</string>
     <plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
-      <item quantity="one">Durante um minuto (até à(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
       <item quantity="other">Durante %1$d minutos (até à(s) <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
+      <item quantity="one">Durante um minuto (até à(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
     </plurals>
     <plurals name="zen_mode_duration_minutes_summary_short" formatted="false" msgid="6830154222366042597">
-      <item quantity="one">Durante 1 min (até à(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
       <item quantity="other">Durante %1$d min (até à(s) <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
+      <item quantity="one">Durante 1 min (até à(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
     </plurals>
     <plurals name="zen_mode_duration_hours_summary" formatted="false" msgid="8152974162096743862">
-      <item quantity="one">Durante uma hora (até à(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
       <item quantity="other">Durante %1$d horas (até à(s) <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
+      <item quantity="one">Durante uma hora (até à(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
     </plurals>
     <plurals name="zen_mode_duration_hours_summary_short" formatted="false" msgid="4787552595253082371">
-      <item quantity="one">Durante 1 h (até à(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
       <item quantity="other">Durante %1$d h (até à(s) <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
+      <item quantity="one">Durante 1 h (até à(s) <xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
     </plurals>
     <plurals name="zen_mode_duration_minutes" formatted="false" msgid="5127407202506485571">
-      <item quantity="one">Durante um minuto</item>
       <item quantity="other">Durante %d minutos</item>
+      <item quantity="one">Durante um minuto</item>
     </plurals>
     <plurals name="zen_mode_duration_minutes_short" formatted="false" msgid="2199350154433426128">
-      <item quantity="one">Durante 1 min</item>
       <item quantity="other">Durante %d min</item>
+      <item quantity="one">Durante 1 min</item>
     </plurals>
     <plurals name="zen_mode_duration_hours" formatted="false" msgid="3938821308277433854">
-      <item quantity="one">Durante uma hora</item>
       <item quantity="other">Durante %d horas</item>
+      <item quantity="one">Durante uma hora</item>
     </plurals>
     <plurals name="zen_mode_duration_hours_short" formatted="false" msgid="6748277774662434217">
-      <item quantity="one">Durante 1 h</item>
       <item quantity="other">Durante %d h</item>
+      <item quantity="one">Durante 1 h</item>
     </plurals>
     <string name="zen_mode_until" msgid="7336308492289875088">"Até às <xliff:g id="FORMATTEDTIME">%1$s</xliff:g>"</string>
     <string name="zen_mode_alarm" msgid="9128205721301330797">"Até <xliff:g id="FORMATTEDTIME">%1$s</xliff:g> (próximo alarme)"</string>
@@ -1684,6 +1690,8 @@
     <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Dias da semana à noite"</string>
     <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"Fim de semana"</string>
     <string name="zen_mode_default_events_name" msgid="8158334939013085363">"Evento"</string>
+    <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) -->
+    <skip />
     <string name="muted_by" msgid="6147073845094180001">"Som desativado por <xliff:g id="THIRD_PARTY">%1$s</xliff:g>"</string>
     <string name="system_error_wipe_data" msgid="6608165524785354962">"Existe um problema interno no seu dispositivo e pode ficar instável até efetuar uma reposição de dados de fábrica."</string>
     <string name="system_error_manufacturer" msgid="8086872414744210668">"Existe um problema interno no seu dispositivo. Contacte o fabricante para obter mais informações."</string>
@@ -1706,8 +1714,8 @@
     <string name="close_button_text" msgid="3937902162644062866">"Fechar"</string>
     <string name="notification_messaging_title_template" msgid="3452480118762691020">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
     <plurals name="selected_count" formatted="false" msgid="7187339492915744615">
-      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selecionado</item>
       <item quantity="other"><xliff:g id="COUNT_1">%1$d</xliff:g> selecionados</item>
+      <item quantity="one"><xliff:g id="COUNT_0">%1$d</xliff:g> selecionado</item>
     </plurals>
     <string name="default_notification_channel_label" msgid="5929663562028088222">"Sem categoria"</string>
     <string name="importance_from_user" msgid="7318955817386549931">"Definiu a importância destas notificações."</string>
@@ -1764,8 +1772,8 @@
     <string name="autofill_error_cannot_autofill" msgid="7402758580060110371">"Não é possível preencher automaticamente o conteúdo"</string>
     <string name="autofill_picker_no_suggestions" msgid="3908514303773350735">"Sem sugestões do preenchimento automático"</string>
     <plurals name="autofill_picker_some_suggestions" formatted="false" msgid="5506565809835815274">
-      <item quantity="one">Uma sugestão do preenchimento automático</item>
       <item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> sugestões do preenchimento automático</item>
+      <item quantity="one">Uma sugestão do preenchimento automático</item>
     </plurals>
     <string name="autofill_save_title" msgid="3345527308992082601">"Pretende guardar no &lt;b&gt;<xliff:g id="LABEL">%1$s</xliff:g>&lt;/b&gt;?"</string>
     <string name="autofill_save_title_with_type" msgid="8637809388029313305">"Pretende guardar <xliff:g id="TYPE">%1$s</xliff:g> no &lt;b&gt;<xliff:g id="LABEL">%2$s</xliff:g>&lt;/b&gt;?"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 20b1bbe..ad6c6bc 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -216,6 +216,8 @@
     <string name="global_action_power_off" msgid="4471879440839879722">"Вимкнути"</string>
     <string name="global_action_emergency" msgid="7112311161137421166">"Екстрений виклик"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Звіт про помилки"</string>
+    <!-- no translation found for global_action_logout (935179188218826050) -->
+    <skip />
     <string name="bugreport_title" msgid="2667494803742548533">"Звіт про помилку"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Інформація про поточний стан вашого пристрою буде зібрана й надіслана електронною поштою. Підготовка звіту триватиме певний час."</string>
     <string name="bugreport_option_interactive_title" msgid="8635056131768862479">"Інтерактивний звіт"</string>
@@ -1546,6 +1548,10 @@
     <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Коли ярлик увімкнено, після натискання обох клавіш гучності й утримування їх протягом 3 секунд увімкнеться функція спеціальних можливостей.\n\n Поточна функція спеціальних можливостей:\n <xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\n Цю функцію можна змінити в меню \"Налаштування\" &gt; \"Спеціальні можливості\"."</string>
     <string name="disable_accessibility_shortcut" msgid="627625354248453445">"Вимкнути ярлик"</string>
     <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Використовувати ярлик"</string>
+    <!-- no translation found for color_inversion_feature_name (4231186527799958644) -->
+    <skip />
+    <!-- no translation found for color_correction_feature_name (6779391426096954933) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Ярлик спеціальних можливостей увімкнув <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Ярлик спеціальних можливостей вимкнув <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Виберіть функцію для кнопки спеціальних можливостей:"</string>
@@ -1750,6 +1756,8 @@
     <string name="zen_mode_default_weeknights_name" msgid="3081318299464998143">"Увечері в будні"</string>
     <string name="zen_mode_default_weekends_name" msgid="2786495801019345244">"На вихідних"</string>
     <string name="zen_mode_default_events_name" msgid="8158334939013085363">"Подія"</string>
+    <!-- no translation found for zen_mode_default_every_night_name (3012363838882944175) -->
+    <skip />
     <string name="muted_by" msgid="6147073845094180001">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> вимикає звук"</string>
     <string name="system_error_wipe_data" msgid="6608165524785354962">"Через внутрішню помилку ваш пристрій може працювати нестабільно. Відновіть заводські налаштування."</string>
     <string name="system_error_manufacturer" msgid="8086872414744210668">"На пристрої сталася внутрішня помилка. Зв’яжіться з виробником пристрою, щоб дізнатися більше."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3b49d9d..51ffa60 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3294,6 +3294,9 @@
              and subtype in order to provide the consistent user experience in switching
              between IMEs and subtypes. -->
         <attr name="supportsSwitchingToNextInputMethod" format="boolean" />
+        <!-- Specifies if an IME can only be used while a device is in VR mode or on a dedicated
+             device -->
+        <attr name="isVrOnly" format="boolean"/>
         <attr name="__removed2" format="boolean" />
     </declare-styleable>
 
@@ -7568,6 +7571,15 @@
             <flag name="keyguard" value="0x2" />
             <flag name="searchbox" value="0x4" />
         </attr>
+        <!-- Flags indicating various features supported by the widget. These are hints to the
+         widget host, and do not actually change the behavior of the widget. -->
+        <attr name="widgetFeatures" format="integer">
+            <!-- The widget can be reconfigured anytime after it is bound -->
+            <flag name="reconfigurable" value="0x1" />
+            <!-- The widget is added directly by the app, and does not need to appear in
+                 the global list of available widgets -->
+            <flag name="hide_from_picker" value="0x2" />
+        </attr>
     </declare-styleable>
 
     <!-- =============================== -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 4d67494..e87b9d8 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -320,6 +320,13 @@
          as multiple split APKs, each APK must have the exact same versionCode. -->
     <attr name="versionCode" format="integer" />
 
+    <!-- Internal major version code.  This is essentially additional high bits
+         for the base version code; it has no other meaning than
+         that higher numbers are more recent.  This is not a version
+         number generally shown to the user, that is usually supplied
+         with {@link android.R.attr#versionName}. -->
+    <attr name="versionCodeMajor" format="integer" />
+
     <!-- Internal revision code.  This number is the number used to determine
          whether one APK is more recent than another: it has no other meaning
          than that higher numbers are more recent.  This value is only meaningful
@@ -1389,6 +1396,7 @@
          {@link #AndroidManifestUsesFeature uses-feature}.  -->
     <declare-styleable name="AndroidManifest">
         <attr name="versionCode" />
+        <attr name="versionCodeMajor" />
         <attr name="versionName" />
         <attr name="revisionCode" />
         <attr name="sharedUserId" />
@@ -1787,6 +1795,10 @@
         <attr name="name" />
         <!-- Required specific library version. -->
         <attr name="version" />
+        <!-- Required specific library major version code.  This matches
+             android:versionCodeMajor of the library. -->
+        <!-- Required specific library version. -->
+        <attr name="versionMajor" format="integer" />
     </declare-styleable>
 
     <!-- The <code>uses-libraries</code> specifies a shared library that this
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fc64918..0b38d1b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2441,10 +2441,12 @@
          "users" = list of users
          "restart" = restart device
          "lockdown" = Lock down device until the user authenticates
+         "logout" =  Logout the current user
          -->
     <string-array translatable="false" name="config_globalActionsList">
         <item>power</item>
         <item>restart</item>
+        <item>logout</item>
         <item>bugreport</item>
         <item>users</item>
         <item>lockdown</item>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3dbb89c..373a656 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2854,6 +2854,11 @@
       <public name="compileSdkVersionCodename" />
       <public name="screenReaderFocusable" />
       <public name="buttonCornerRadius" />
+      <public name="versionCodeMajor" />
+      <public name="versionMajor" />
+      <!-- @hide @SystemApi -->
+      <public name="isVrOnly"/>
+      <public name="widgetFeatures" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 93bd4ec..5ccaf5c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -471,6 +471,9 @@
     <!-- label for item that generates a bug report in the phone options dialog -->
     <string name="global_action_bug_report">Bug report</string>
 
+    <!-- label for item that logouts the current user -->
+    <string name="global_action_logout">End session</string>
+
     <!-- Take bug report menu title [CHAR LIMIT=NONE] -->
     <string name="bugreport_title">Take bug report</string>
     <!-- Message in bugreport dialog describing what it does [CHAR LIMIT=NONE] -->
@@ -4031,6 +4034,14 @@
     shortcut enabled.-->
     <string name="leave_accessibility_shortcut_on">Use Shortcut</string>
 
+    <!-- Title of Color Inversion feature shown in the warning dialog about the accessibility
+    shortcut. -->
+    <string name="color_inversion_feature_name">Color Inversion</string>
+
+    <!-- Title of Color Correction feature, which is mostly used to help users who are colorblind,
+     shown in the warning dialog about the accessibility shortcut. -->
+    <string name="color_correction_feature_name">Color Correction</string>
+
     <!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility
     service.-->
     <string name="accessibility_shortcut_enabling_service">Accessibility Shortcut turned
@@ -4463,6 +4474,9 @@
     <!-- Zen mode - name of default automatic calendar event-based rule. [CHAR LIMIT=40] -->
     <string name="zen_mode_default_events_name">Event</string>
 
+    <!-- Zen mode - name of default automatic calendar time-based rule that is triggered every night (when sleeping). [CHAR LIMIT=40] -->
+    <string name="zen_mode_default_every_night_name">Sleeping</string>
+
     <!-- Indication that the current volume and other effects (vibration) are being suppressed by a third party, such as a notification listener. [CHAR LIMIT=30] -->
     <string name="muted_by">Muted by <xliff:g id="third_party">%1$s</xliff:g></string>
 
@@ -4476,7 +4490,9 @@
     <string name="stk_cc_ussd_to_dial">USSD request is modified to DIAL request.</string>
     <string name="stk_cc_ussd_to_ss">USSD request is modified to SS request.</string>
     <string name="stk_cc_ussd_to_ussd">USSD request is modified to new USSD request.</string>
+    <string name="stk_cc_ussd_to_dial_video">USSD request is modified to Video DIAL request.</string>
     <string name="stk_cc_ss_to_dial">SS request is modified to DIAL request.</string>
+    <string name="stk_cc_ss_to_dial_video">SS request is modified to Video DIAL request.</string>
     <string name="stk_cc_ss_to_ussd">SS request is modified to USSD request.</string>
     <string name="stk_cc_ss_to_ss">SS request is modified to new SS request.</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index feb7800..f659360 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1048,9 +1048,11 @@
   <java-symbol type="string" name="stk_cc_ss_to_dial" />
   <java-symbol type="string" name="stk_cc_ss_to_ss" />
   <java-symbol type="string" name="stk_cc_ss_to_ussd" />
+  <java-symbol type="string" name="stk_cc_ss_to_dial_video" />
   <java-symbol type="string" name="stk_cc_ussd_to_dial" />
   <java-symbol type="string" name="stk_cc_ussd_to_ss" />
   <java-symbol type="string" name="stk_cc_ussd_to_ussd" />
+  <java-symbol type="string" name="stk_cc_ussd_to_dial_video" />
   <java-symbol type="string" name="safe_media_volume_warning" />
   <java-symbol type="string" name="media_route_status_scanning" />
   <java-symbol type="string" name="media_route_status_connecting" />
@@ -1067,6 +1069,8 @@
   <java-symbol type="string" name="action_bar_home_description_format" />
   <java-symbol type="string" name="action_bar_home_subtitle_description_format" />
   <java-symbol type="string" name="wireless_display_route_description" />
+  <java-symbol type="string" name="user_owner_label" />
+  <java-symbol type="string" name="managed_profile_label" />
   <java-symbol type="string" name="managed_profile_label_badge" />
   <java-symbol type="string" name="managed_profile_label_badge_2" />
   <java-symbol type="string" name="managed_profile_label_badge_3" />
@@ -2262,6 +2266,7 @@
   <java-symbol type="string" name="zen_mode_default_weeknights_name" />
   <java-symbol type="string" name="zen_mode_default_weekends_name" />
   <java-symbol type="string" name="zen_mode_default_events_name" />
+  <java-symbol type="string" name="zen_mode_default_every_night_name" />
   <java-symbol type="array" name="config_system_condition_providers" />
   <java-symbol type="string" name="muted_by" />
   <java-symbol type="string" name="zen_mode_alarm" />
@@ -2911,6 +2916,8 @@
   <java-symbol type="string" name="accessibility_shortcut_disabling_service" />
   <java-symbol type="string" name="disable_accessibility_shortcut" />
   <java-symbol type="string" name="leave_accessibility_shortcut_on" />
+  <java-symbol type="string" name="color_inversion_feature_name" />
+  <java-symbol type="string" name="color_correction_feature_name" />
   <java-symbol type="string" name="config_defaultAccessibilityService" />
 
   <!-- Accessibility Button -->
@@ -3163,4 +3170,7 @@
   <java-symbol type="string" name="unsupported_compile_sdk_check_update" />
 
   <java-symbol type="string" name="battery_saver_warning_title" />
+
+  <java-symbol type="string" name="global_action_logout" />
+  <java-symbol type="drawable" name="ic_logout" />
 </resources>
diff --git a/core/tests/BroadcastRadioTests/Android.mk b/core/tests/BroadcastRadioTests/Android.mk
index c409e3a..8df3827 100644
--- a/core/tests/BroadcastRadioTests/Android.mk
+++ b/core/tests/BroadcastRadioTests/Android.mk
@@ -26,6 +26,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test testng
 
+LOCAL_JAVA_LIBRARIES := android.test.base
+
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index f2eb872..47990a1 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -30,6 +30,7 @@
 LOCAL_JACK_FLAGS := --multi-dex native
 LOCAL_AAPT_FLAGS = -0 dat -0 gld -c fa
 LOCAL_STATIC_JAVA_LIBRARIES := \
+    frameworks-base-testutils \
     core-tests-support \
     android-common \
     frameworks-core-util-lib \
@@ -44,7 +45,14 @@
     truth-prebuilt \
     print-test-util-lib
 
-LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt telephony-common org.apache.http.legacy
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner \
+    conscrypt \
+    telephony-common \
+    org.apache.http.legacy \
+    android.test.base \
+    android.test.mock \
+
 LOCAL_PACKAGE_NAME := FrameworksCoreTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
 
diff --git a/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttf b/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttf
new file mode 100644
index 0000000..1bad6fe
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttx b/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttx
new file mode 100644
index 0000000..0cf0f79
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttx
@@ -0,0 +1,207 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="0em"/>
+    <GlyphID id="2" name="1em"/>
+    <GlyphID id="3" name="3em"/>
+    <GlyphID id="4" name="5em"/>
+    <GlyphID id="5" name="7em"/>
+    <GlyphID id="6" name="10em"/>
+    <GlyphID id="7" name="50em"/>
+    <GlyphID id="8" name="100em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="100"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="50" lsb="0"/>
+    <mtx name="0em" width="0" lsb="0"/>
+    <mtx name="1em" width="100" lsb="0"/>
+    <mtx name="3em" width="300" lsb="0"/>
+    <mtx name="5em" width="500" lsb="0"/>
+    <mtx name="7em" width="700" lsb="0"/>
+    <mtx name="10em" width="1000" lsb="0"/>
+    <mtx name="50em" width="5000" lsb="0"/>
+    <mtx name="100em" width="10000" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_12 format="12" reserved="0" length="6" nGroups="1" platformID="3" platEncID="10" language="0">
+      <map code="0x0020" name="10em" />
+      <map code="0x002e" name="10em" />  <!-- . -->
+      <map code="0x0043" name="100em" />  <!-- C -->
+      <map code="0x0049" name="1em" />  <!-- I -->
+      <map code="0x004c" name="50em" />  <!-- L -->
+      <map code="0x0056" name="5em" />  <!-- V -->
+      <map code="0x0058" name="10em" />  <!-- X -->
+      <map code="0x005f" name="0em" /> <!-- _ -->
+      <map code="0xfffd" name="7em" /> <!-- REPLACEMENT CHAR -->
+      <map code="0x10331" name="10em" />
+    </cmap_format_12>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="0em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="5em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="7em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="10em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="50em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="100em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Font for StaticLayoutLineBreakingTest
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Font for StaticLayoutLineBreakingTest
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/res/xml/ime_meta_vr_only.xml b/core/tests/coretests/res/xml/ime_meta_vr_only.xml
new file mode 100644
index 0000000..653a8ff
--- /dev/null
+++ b/core/tests/coretests/res/xml/ime_meta_vr_only.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<input-method
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity"
+    android:isVrOnly="true">
+</input-method>
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
index e0fcf08..ea2dd59 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
@@ -22,6 +22,7 @@
 
 import android.app.ClientTransactionHandler;
 import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -30,11 +31,11 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-// TODO(lifecycler): Add to presubmit after checking for flakiness.
+@Presubmit
 public class ClientTransactionTests {
 
     @Test
-    public void testPrepare() {
+    public void testPreExecute() {
         ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
         ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
         ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
@@ -47,32 +48,10 @@
         transaction.addCallback(callback2);
         transaction.setLifecycleStateRequest(stateRequest);
 
-        transaction.prepare(clientTransactionHandler);
+        transaction.preExecute(clientTransactionHandler);
 
-        verify(callback1, times(1)).prepare(clientTransactionHandler, token);
-        verify(callback2, times(1)).prepare(clientTransactionHandler, token);
-        verify(stateRequest, times(1)).prepare(clientTransactionHandler, token);
-    }
-
-    @Test
-    public void testExecute() {
-        ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
-        ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
-        ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
-        IBinder token = mock(IBinder.class);
-
-        ClientTransaction transaction = new ClientTransaction(null /* client */,
-                token /* activityToken */);
-        transaction.addCallback(callback1);
-        transaction.addCallback(callback2);
-        transaction.setLifecycleStateRequest(stateRequest);
-
-        ClientTransactionHandler clientTransactionHandler = mock(ClientTransactionHandler.class);
-        transaction.prepare(clientTransactionHandler);
-        transaction.execute(clientTransactionHandler);
-
-        verify(callback1, times(1)).execute(clientTransactionHandler, token);
-        verify(callback2, times(1)).execute(clientTransactionHandler, token);
-        verify(stateRequest, times(1)).execute(clientTransactionHandler, token);
+        verify(callback1, times(1)).preExecute(clientTransactionHandler, token);
+        verify(callback2, times(1)).preExecute(clientTransactionHandler, token);
+        verify(stateRequest, times(1)).preExecute(clientTransactionHandler, token);
     }
 }
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
new file mode 100644
index 0000000..d283004
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 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.app.servertransaction;
+
+import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESTART;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityThread.ActivityClientRecord;
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+
+import java.util.ArrayList;
+
+/** Test {@link TransactionExecutor} logic. */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TransactionExecutorTests {
+
+    private TransactionExecutor mExecutor;
+    private ClientTransactionHandler mTransactionHandler;
+    private ActivityClientRecord mClientRecord;
+
+    @Before
+    public void setUp() throws Exception {
+        mTransactionHandler = mock(ClientTransactionHandler.class);
+
+        mClientRecord = new ActivityClientRecord();
+        when(mTransactionHandler.getActivityClient(any())).thenReturn(mClientRecord);
+
+        mExecutor = spy(new TransactionExecutor(mTransactionHandler));
+    }
+
+    @Test
+    public void testLifecycleFromPreOnCreate() {
+        mClientRecord.setState(PRE_ON_CREATE);
+        assertArrayEquals(new int[] {}, path(PRE_ON_CREATE));
+        assertArrayEquals(new int[] {ON_CREATE}, path(ON_CREATE));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START}, path(ON_START));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME}, path(ON_RESUME));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP},
+                path(ON_STOP));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY},
+                path(ON_DESTROY));
+    }
+
+    @Test
+    public void testLifecycleFromOnCreate() {
+        mClientRecord.setState(ON_CREATE);
+        assertArrayEquals(new int[] {}, path(ON_CREATE));
+        assertArrayEquals(new int[] {ON_START}, path(ON_START));
+        assertArrayEquals(new int[] {ON_START, ON_RESUME}, path(ON_RESUME));
+        assertArrayEquals(new int[] {ON_START, ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
+        assertArrayEquals(new int[] {ON_START, ON_RESUME, ON_PAUSE, ON_STOP}, path(ON_STOP));
+        assertArrayEquals(new int[] {ON_START, ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY},
+                path(ON_DESTROY));
+    }
+
+    @Test
+    public void testLifecycleFromOnStart() {
+        mClientRecord.setState(ON_START);
+        assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY, ON_CREATE},
+                path(ON_CREATE));
+        assertArrayEquals(new int[] {}, path(ON_START));
+        assertArrayEquals(new int[] {ON_RESUME}, path(ON_RESUME));
+        assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
+        assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP}, path(ON_STOP));
+        assertArrayEquals(new int[] {ON_RESUME, ON_PAUSE, ON_STOP, ON_DESTROY}, path(ON_DESTROY));
+    }
+
+    @Test
+    public void testLifecycleFromOnResume() {
+        mClientRecord.setState(ON_RESUME);
+        assertArrayEquals(new int[] {ON_PAUSE, ON_STOP, ON_DESTROY, ON_CREATE}, path(ON_CREATE));
+        assertArrayEquals(new int[] {ON_PAUSE, ON_STOP, ON_RESTART, ON_START}, path(ON_START));
+        assertArrayEquals(new int[] {}, path(ON_RESUME));
+        assertArrayEquals(new int[] {ON_PAUSE}, path(ON_PAUSE));
+        assertArrayEquals(new int[] {ON_PAUSE, ON_STOP}, path(ON_STOP));
+        assertArrayEquals(new int[] {ON_PAUSE, ON_STOP, ON_DESTROY}, path(ON_DESTROY));
+    }
+
+    @Test
+    public void testLifecycleFromOnPause() {
+        mClientRecord.setState(ON_PAUSE);
+        assertArrayEquals(new int[] {ON_STOP, ON_DESTROY, ON_CREATE}, path(ON_CREATE));
+        assertArrayEquals(new int[] {ON_STOP, ON_RESTART, ON_START}, path(ON_START));
+        assertArrayEquals(new int[] {ON_RESUME}, path(ON_RESUME));
+        assertArrayEquals(new int[] {}, path(ON_PAUSE));
+        assertArrayEquals(new int[] {ON_STOP}, path(ON_STOP));
+        assertArrayEquals(new int[] {ON_STOP, ON_DESTROY}, path(ON_DESTROY));
+    }
+
+    @Test
+    public void testLifecycleFromOnStop() {
+        mClientRecord.setState(ON_STOP);
+        assertArrayEquals(new int[] {ON_DESTROY, ON_CREATE}, path(ON_CREATE));
+        assertArrayEquals(new int[] {ON_RESTART, ON_START}, path(ON_START));
+        assertArrayEquals(new int[] {ON_RESTART, ON_START, ON_RESUME}, path(ON_RESUME));
+        assertArrayEquals(new int[] {ON_RESTART, ON_START, ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
+        assertArrayEquals(new int[] {}, path(ON_STOP));
+        assertArrayEquals(new int[] {ON_DESTROY}, path(ON_DESTROY));
+    }
+
+    @Test
+    public void testLifecycleFromOnDestroy() {
+        mClientRecord.setState(ON_DESTROY);
+        assertArrayEquals(new int[] {ON_CREATE}, path(ON_CREATE));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START}, path(ON_START));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME}, path(ON_RESUME));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE}, path(ON_PAUSE));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP},
+                path(ON_STOP));
+        assertArrayEquals(new int[] {}, path(ON_DESTROY));
+    }
+
+    @Test
+    public void testLifecycleExcludeLastItem() {
+        mClientRecord.setState(PRE_ON_CREATE);
+        assertArrayEquals(new int[] {}, pathExcludeLast(PRE_ON_CREATE));
+        assertArrayEquals(new int[] {}, pathExcludeLast(ON_CREATE));
+        assertArrayEquals(new int[] {ON_CREATE}, pathExcludeLast(ON_START));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START}, pathExcludeLast(ON_RESUME));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME}, pathExcludeLast(ON_PAUSE));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE},
+                pathExcludeLast(ON_STOP));
+        assertArrayEquals(new int[] {ON_CREATE, ON_START, ON_RESUME, ON_PAUSE, ON_STOP},
+                pathExcludeLast(ON_DESTROY));
+    }
+
+    @Test
+    public void testTransactionResolution() {
+        ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
+        ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
+        ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
+        IBinder token = mock(IBinder.class);
+
+        ClientTransaction transaction = new ClientTransaction(null /* client */,
+                token /* activityToken */);
+        transaction.addCallback(callback1);
+        transaction.addCallback(callback2);
+        transaction.setLifecycleStateRequest(stateRequest);
+
+        transaction.preExecute(mTransactionHandler);
+        mExecutor.execute(transaction);
+
+        InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2, stateRequest);
+        inOrder.verify(callback1, times(1)).execute(eq(mTransactionHandler), eq(token), any());
+        inOrder.verify(callback2, times(1)).execute(eq(mTransactionHandler), eq(token), any());
+        inOrder.verify(stateRequest, times(1)).execute(eq(mTransactionHandler), eq(token), any());
+    }
+
+    @Test
+    public void testRequiredStateResolution() {
+        ActivityResultItem activityResultItem = new ActivityResultItem(new ArrayList<>());
+
+        IBinder token = mock(IBinder.class);
+        ClientTransaction transaction = new ClientTransaction(null /* client */,
+                token /* activityToken */);
+        transaction.addCallback(activityResultItem);
+
+        mExecutor.executeCallbacks(transaction);
+
+        verify(mExecutor, times(1)).cycleToPath(eq(mClientRecord), eq(ON_PAUSE));
+    }
+
+    private int[] path(int finish) {
+        mExecutor.initLifecyclePath(mClientRecord.getLifecycleState(), finish,
+                false /* excludeLastState */);
+        return mExecutor.getLifecycleSequence();
+    }
+
+    private int[] pathExcludeLast(int finish) {
+        mExecutor.initLifecyclePath(mClientRecord.getLifecycleState(), finish,
+                true /* excludeLastState */);
+        return mExecutor.getLifecycleSequence();
+    }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 9db7550..f6c656d 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -46,6 +46,7 @@
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -63,7 +64,7 @@
 /** Test parcelling and unparcelling of transactions and transaction items. */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-// TODO(lifecycler): Add to presubmit after checking for flakiness.
+@Presubmit
 public class TransactionParcelTests {
 
     private Parcel mParcel;
@@ -230,7 +231,7 @@
         LaunchActivityItem item = new LaunchActivityItem(intent, ident, activityInfo,
                 config(), overrideConfig, compat, referrer, null /* voiceInteractor */,
                 procState, bundle, persistableBundle, resultInfoList(), referrerIntentList(),
-                true /* notResumed */, true /* isForward */, null /* profilerInfo */);
+                true /* isForward */, null /* profilerInfo */);
         writeAndPrepareForReading(item);
 
         // Read from parcel and assert
diff --git a/core/tests/coretests/src/android/content/pm/crossprofile/CrossProfileAppsTest.java b/core/tests/coretests/src/android/content/pm/crossprofile/CrossProfileAppsTest.java
new file mode 100644
index 0000000..80e4c02
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/crossprofile/CrossProfileAppsTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.content.pm.crossprofile;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.internal.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Build/Install/Run:
+ * bit FrameworksCoreTests:android.content.pm.crossprofile.CrossProfileAppsTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public class CrossProfileAppsTest {
+    private static final UserHandle PERSONAL_PROFILE = UserHandle.of(0);
+    private static final UserHandle MANAGED_PROFILE = UserHandle.of(10);
+    private static final String MY_PACKAGE = "my.package";
+
+    private List<UserHandle> mTargetProfiles;
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private ICrossProfileApps mService;
+    @Mock
+    private Resources mResources;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Drawable mDrawable;
+    private CrossProfileApps mCrossProfileApps;
+
+    @Before
+    public void initCrossProfileApps() {
+        mCrossProfileApps = new CrossProfileApps(mContext, mService);
+    }
+
+    @Before
+    public void mockContext() {
+        when(mContext.getPackageName()).thenReturn(MY_PACKAGE);
+        when(mContext.getSystemServiceName(UserManager.class)).thenReturn(Context.USER_SERVICE);
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+    }
+
+    @Before
+    public void mockResources() {
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.getDrawable(anyInt(), nullable(Resources.Theme.class)))
+                .thenReturn(mDrawable);
+    }
+
+    @Before
+    public void initUsers() throws Exception {
+        when(mUserManager.isManagedProfile(PERSONAL_PROFILE.getIdentifier())).thenReturn(false);
+        when(mUserManager.isManagedProfile(MANAGED_PROFILE.getIdentifier())).thenReturn(true);
+
+        mTargetProfiles = new ArrayList<>();
+        when(mService.getTargetUserProfiles(MY_PACKAGE)).thenReturn(mTargetProfiles);
+    }
+
+    @Test
+    public void getProfileSwitchingLabel_managedProfile() {
+        setValidTargetProfile(MANAGED_PROFILE);
+
+        mCrossProfileApps.getProfileSwitchingLabel(MANAGED_PROFILE);
+        verify(mResources).getString(R.string.managed_profile_label);
+    }
+
+    @Test
+    public void getProfileSwitchingLabel_personalProfile() {
+        setValidTargetProfile(PERSONAL_PROFILE);
+
+        mCrossProfileApps.getProfileSwitchingLabel(PERSONAL_PROFILE);
+        verify(mResources).getString(R.string.user_owner_label);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void getProfileSwitchingLabel_securityException() {
+        mCrossProfileApps.getProfileSwitchingLabel(PERSONAL_PROFILE);
+    }
+
+    @Test
+    public void getProfileSwitchingIcon_managedProfile() {
+        setValidTargetProfile(MANAGED_PROFILE);
+
+        mCrossProfileApps.getProfileSwitchingIcon(MANAGED_PROFILE);
+        verify(mResources).getDrawable(R.drawable.ic_corp_badge, null);
+    }
+
+    @Test
+    public void getProfileSwitchingIcon_personalProfile() {
+        setValidTargetProfile(PERSONAL_PROFILE);
+
+        mCrossProfileApps.getProfileSwitchingIcon(PERSONAL_PROFILE);
+        verify(mResources).getDrawable(R.drawable.ic_account_circle, null);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void getProfileSwitchingIcon_securityException() {
+        mCrossProfileApps.getProfileSwitchingIcon(PERSONAL_PROFILE);
+    }
+
+    private void setValidTargetProfile(UserHandle userHandle) {
+        mTargetProfiles.add(userHandle);
+    }
+}
diff --git a/core/tests/coretests/src/android/os/EnvironmentTest.java b/core/tests/coretests/src/android/os/EnvironmentTest.java
new file mode 100644
index 0000000..5189df5
--- /dev/null
+++ b/core/tests/coretests/src/android/os/EnvironmentTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.os;
+
+import static android.os.Environment.HAS_ANDROID;
+import static android.os.Environment.HAS_DCIM;
+import static android.os.Environment.HAS_DOWNLOADS;
+import static android.os.Environment.HAS_OTHER;
+import static android.os.Environment.classifyExternalStorageDirectory;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+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;
+
+@RunWith(AndroidJUnit4.class)
+public class EnvironmentTest {
+    private File dir;
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        dir = getContext().getDir("testing", Context.MODE_PRIVATE);
+        FileUtils.deleteContents(dir);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(dir);
+    }
+
+    @Test
+    public void testClassify_empty() {
+        assertEquals(0, classifyExternalStorageDirectory(dir));
+    }
+
+    @Test
+    public void testClassify_emptyDirs() {
+        Environment.buildPath(dir, "DCIM").mkdirs();
+        Environment.buildPath(dir, "DCIM", "January").mkdirs();
+        Environment.buildPath(dir, "Downloads").mkdirs();
+        Environment.buildPath(dir, "LOST.DIR").mkdirs();
+        assertEquals(0, classifyExternalStorageDirectory(dir));
+    }
+
+    @Test
+    public void testClassify_emptyFactory() throws Exception {
+        Environment.buildPath(dir, "autorun.inf").createNewFile();
+        Environment.buildPath(dir, "LaunchU3.exe").createNewFile();
+        Environment.buildPath(dir, "LaunchPad.zip").createNewFile();
+        assertEquals(0, classifyExternalStorageDirectory(dir));
+    }
+
+    @Test
+    public void testClassify_photos() throws Exception {
+        Environment.buildPath(dir, "DCIM").mkdirs();
+        Environment.buildPath(dir, "DCIM", "IMG_1024.JPG").createNewFile();
+        Environment.buildPath(dir, "Download").mkdirs();
+        Environment.buildPath(dir, "Download", "foobar.pdf").createNewFile();
+        assertEquals(HAS_DCIM | HAS_DOWNLOADS, classifyExternalStorageDirectory(dir));
+    }
+
+    @Test
+    public void testClassify_other() throws Exception {
+        Environment.buildPath(dir, "Android").mkdirs();
+        Environment.buildPath(dir, "Android", "com.example").mkdirs();
+        Environment.buildPath(dir, "Android", "com.example", "internal.dat").createNewFile();
+        Environment.buildPath(dir, "Linux").mkdirs();
+        Environment.buildPath(dir, "Linux", "install-amd64-minimal-20170907.iso").createNewFile();
+        assertEquals(HAS_ANDROID | HAS_OTHER, classifyExternalStorageDirectory(dir));
+    }
+
+    @Test
+    public void testClassify_otherRoot() throws Exception {
+        Environment.buildPath(dir, "Taxes.pdf").createNewFile();
+        assertEquals(HAS_OTHER, classifyExternalStorageDirectory(dir));
+    }
+}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 3a751af..cd20192 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -21,15 +21,24 @@
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.WEEK_IN_MILLIS;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import android.content.Context;
 import android.provider.DocumentsContract.Document;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
 
 import libcore.io.IoUtils;
 
 import com.google.android.collect.Sets;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -37,8 +46,8 @@
 import java.util.Arrays;
 import java.util.HashSet;
 
-@MediumTest
-public class FileUtilsTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class FileUtilsTest {
     private static final String TEST_DATA =
             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 
@@ -47,10 +56,12 @@
     private File mCopyFile;
     private File mTarget;
 
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Before
+    public void setUp() throws Exception {
         mDir = getContext().getDir("testing", Context.MODE_PRIVATE);
         mTestFile = new File(mDir, "test.file");
         mCopyFile = new File(mDir, "copy.file");
@@ -59,14 +70,15 @@
         FileUtils.deleteContents(mTarget);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         IoUtils.deleteContents(mDir);
         FileUtils.deleteContents(mTarget);
     }
 
     // TODO: test setPermissions(), getPermissions()
 
+    @Test
     public void testCopyFile() throws Exception {
         stageFile(mTestFile, TEST_DATA);
         assertFalse(mCopyFile.exists());
@@ -75,6 +87,7 @@
         assertEquals(TEST_DATA, FileUtils.readTextFile(mCopyFile, 0, null));
     }
 
+    @Test
     public void testCopyToFile() throws Exception {
         final String s = "Foo Bar";
         assertFalse(mCopyFile.exists());
@@ -83,6 +96,7 @@
         assertEquals(s, FileUtils.readTextFile(mCopyFile, 0, null));
     }
 
+    @Test
     public void testIsFilenameSafe() throws Exception {
         assertTrue(FileUtils.isFilenameSafe(new File("foobar")));
         assertTrue(FileUtils.isFilenameSafe(new File("a_b-c=d.e/0,1+23")));
@@ -90,6 +104,7 @@
         assertFalse(FileUtils.isFilenameSafe(new File("foo\nbar")));
     }
 
+    @Test
     public void testReadTextFile() throws Exception {
         stageFile(mTestFile, TEST_DATA);
 
@@ -110,6 +125,7 @@
         assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, -100, "<>"));
     }
 
+    @Test
     public void testReadTextFileWithZeroLengthFile() throws Exception {
         stageFile(mTestFile, TEST_DATA);
         new FileOutputStream(mTestFile).close();  // Zero out the file
@@ -120,6 +136,7 @@
         assertEquals("", FileUtils.readTextFile(mTestFile, -10, "<>"));
     }
 
+    @Test
     public void testContains() throws Exception {
         assertTrue(FileUtils.contains(new File("/"), new File("/moo.txt")));
         assertTrue(FileUtils.contains(new File("/"), new File("/")));
@@ -137,11 +154,13 @@
         assertFalse(FileUtils.contains(new File("/sdcard/"), new File("/sdcard.txt")));
     }
 
+    @Test
     public void testDeleteOlderEmptyDir() throws Exception {
         FileUtils.deleteOlderFiles(mDir, 10, WEEK_IN_MILLIS);
         assertDirContents();
     }
 
+    @Test
     public void testDeleteOlderTypical() throws Exception {
         touch("file1", HOUR_IN_MILLIS);
         touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
@@ -152,6 +171,7 @@
         assertDirContents("file1", "file2", "file3");
     }
 
+    @Test
     public void testDeleteOlderInFuture() throws Exception {
         touch("file1", -HOUR_IN_MILLIS);
         touch("file2", HOUR_IN_MILLIS);
@@ -166,6 +186,7 @@
         assertDirContents("file1", "file2");
     }
 
+    @Test
     public void testDeleteOlderOnlyAge() throws Exception {
         touch("file1", HOUR_IN_MILLIS);
         touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
@@ -177,6 +198,7 @@
         assertDirContents("file1");
     }
 
+    @Test
     public void testDeleteOlderOnlyCount() throws Exception {
         touch("file1", HOUR_IN_MILLIS);
         touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
@@ -188,6 +210,7 @@
         assertDirContents("file1", "file2");
     }
 
+    @Test
     public void testValidExtFilename() throws Exception {
         assertTrue(FileUtils.isValidExtFilename("a"));
         assertTrue(FileUtils.isValidExtFilename("foo.bar"));
@@ -208,6 +231,7 @@
         assertEquals("foo.bar", FileUtils.buildValidExtFilename("foo.bar"));
     }
 
+    @Test
     public void testValidFatFilename() throws Exception {
         assertTrue(FileUtils.isValidFatFilename("a"));
         assertTrue(FileUtils.isValidFatFilename("foo bar.baz"));
@@ -233,6 +257,7 @@
         assertEquals("foo_bar__baz", FileUtils.buildValidFatFilename("foo?bar**baz"));
     }
 
+    @Test
     public void testTrimFilename() throws Exception {
         assertEquals("short.txt", FileUtils.trimFilename("short.txt", 16));
         assertEquals("extrem...eme.txt", FileUtils.trimFilename("extremelylongfilename.txt", 16));
@@ -245,6 +270,7 @@
         assertEquals("a...z", FileUtils.trimFilename(unicode, 6));
     }
 
+    @Test
     public void testBuildUniqueFile_normal() throws Exception {
         assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test"));
         assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
@@ -263,6 +289,7 @@
                 FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test.flac"));
     }
 
+    @Test
     public void testBuildUniqueFile_unknown() throws Exception {
         assertNameEquals("test",
                 FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test"));
@@ -275,6 +302,7 @@
         assertNameEquals("test.lolz", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test.lolz"));
     }
 
+    @Test
     public void testBuildUniqueFile_dir() throws Exception {
         assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
         new File(mTarget, "test").mkdir();
@@ -288,6 +316,7 @@
                 FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
     }
 
+    @Test
     public void testBuildUniqueFile_increment() throws Exception {
         assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
         new File(mTarget, "test.jpg").createNewFile();
@@ -298,6 +327,7 @@
                 FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
     }
 
+    @Test
     public void testBuildUniqueFile_mimeless() throws Exception {
         assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "test.jpg"));
         new File(mTarget, "test.jpg").createNewFile();
@@ -312,6 +342,7 @@
         assertNameEquals("test.foo (1).bar", FileUtils.buildUniqueFile(mTarget, "test.foo.bar"));
     }
 
+    @Test
     public void testRoundStorageSize() throws Exception {
         final long M128 = 128000000L;
         final long M256 = 256000000L;
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 0367275..77c6c3e 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -420,7 +420,7 @@
                  Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
                  // TODO(b/67867469): Move autofill settings below to
                  // BACKUP_BLACKLISTED_SYSTEM_SETTINGS once feature is moved out of experimental
-                 Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION,
+                 Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
                  Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
                  Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE,
                  Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH,
diff --git a/core/tests/coretests/src/android/text/MeasuredTextTest.java b/core/tests/coretests/src/android/text/MeasuredTextTest.java
new file mode 100644
index 0000000..ddef0c6
--- /dev/null
+++ b/core/tests/coretests/src/android/text/MeasuredTextTest.java
@@ -0,0 +1,168 @@
+/*
+ * 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.text;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.support.test.InstrumentationRegistry;
+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 MeasuredTextTest {
+    private static final TextDirectionHeuristic LTR = TextDirectionHeuristics.LTR;
+    private static final TextDirectionHeuristic RTL = TextDirectionHeuristics.RTL;
+
+    private static final TextPaint PAINT = new TextPaint();
+    static {
+        // The test font has following coverage and width.
+        // U+0020: 10em
+        // U+002E (.): 10em
+        // U+0043 (C): 100em
+        // U+0049 (I): 1em
+        // U+004C (L): 50em
+        // U+0056 (V): 5em
+        // U+0058 (X): 10em
+        // U+005F (_): 0em
+        // U+FFFD (invalid surrogate will be replaced to this): 7em
+        // U+10331 (\uD800\uDF31): 10em
+        Context context = InstrumentationRegistry.getTargetContext();
+        PAINT.setTypeface(Typeface.createFromAsset(context.getAssets(),
+                  "fonts/StaticLayoutLineBreakingTestFont.ttf"));
+        PAINT.setTextSize(1.0f);  // Make 1em == 1px.
+    }
+
+    private String charsToString(char[] chars) {
+        return (new StringBuilder()).append(chars).toString();
+    }
+
+    @Test
+    public void buildForBidi() {
+        MeasuredText mt = null;
+
+        mt = MeasuredText.buildForBidi("XXX", 0, 3, LTR, null);
+        assertNotNull(mt);
+        assertNotNull(mt.getChars());
+        assertEquals("XXX", charsToString(mt.getChars()));
+        assertEquals(Layout.DIR_LEFT_TO_RIGHT, mt.getParagraphDir());
+        assertNotNull(mt.getDirections(0, 3));
+        assertEquals(0, mt.getWholeWidth(), 0);
+        assertEquals(0, mt.getWidths().size());
+        assertEquals(0, mt.getSpanEndCache().size());
+        assertEquals(0, mt.getFontMetrics().size());
+        assertEquals(0, mt.getNativePtr());
+
+        // Recycle it
+        MeasuredText mt2 = MeasuredText.buildForBidi("_VVV_", 1, 4, RTL, mt);
+        assertEquals(mt2, mt);
+        assertNotNull(mt2.getChars());
+        assertEquals("VVV", charsToString(mt.getChars()));
+        assertNotNull(mt2.getDirections(0, 3));
+        assertEquals(0, mt2.getWholeWidth(), 0);
+        assertEquals(0, mt2.getWidths().size());
+        assertEquals(0, mt2.getSpanEndCache().size());
+        assertEquals(0, mt2.getFontMetrics().size());
+        assertEquals(0, mt2.getNativePtr());
+
+        mt2.recycle();
+    }
+
+    @Test
+    public void buildForMeasurement() {
+        MeasuredText mt = null;
+
+        mt = MeasuredText.buildForMeasurement(PAINT, "XXX", 0, 3, LTR, null);
+        assertNotNull(mt);
+        assertNotNull(mt.getChars());
+        assertEquals("XXX", charsToString(mt.getChars()));
+        assertEquals(Layout.DIR_LEFT_TO_RIGHT, mt.getParagraphDir());
+        assertNotNull(mt.getDirections(0, 3));
+        assertEquals(30, mt.getWholeWidth(), 0);
+        assertEquals(3, mt.getWidths().size());
+        assertEquals(10, mt.getWidths().get(0), 0);
+        assertEquals(10, mt.getWidths().get(1), 0);
+        assertEquals(10, mt.getWidths().get(2), 0);
+        assertEquals(0, mt.getSpanEndCache().size());
+        assertEquals(0, mt.getFontMetrics().size());
+        assertEquals(0, mt.getNativePtr());
+
+        // Recycle it
+        MeasuredText mt2 = MeasuredText.buildForMeasurement(PAINT, "_VVV_", 1, 4, RTL, mt);
+        assertEquals(mt2, mt);
+        assertNotNull(mt2.getChars());
+        assertEquals("VVV", charsToString(mt.getChars()));
+        assertEquals(Layout.DIR_RIGHT_TO_LEFT, mt2.getParagraphDir());
+        assertNotNull(mt2.getDirections(0, 3));
+        assertEquals(15, mt2.getWholeWidth(), 0);
+        assertEquals(3, mt2.getWidths().size());
+        assertEquals(5, mt2.getWidths().get(0), 0);
+        assertEquals(5, mt2.getWidths().get(1), 0);
+        assertEquals(5, mt2.getWidths().get(2), 0);
+        assertEquals(0, mt2.getSpanEndCache().size());
+        assertEquals(0, mt2.getFontMetrics().size());
+        assertEquals(0, mt2.getNativePtr());
+
+        mt2.recycle();
+    }
+
+    @Test
+    public void buildForStaticLayout() {
+        MeasuredText mt = null;
+
+        mt = MeasuredText.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, null);
+        assertNotNull(mt);
+        assertNotNull(mt.getChars());
+        assertEquals("XXX", charsToString(mt.getChars()));
+        assertEquals(Layout.DIR_LEFT_TO_RIGHT, mt.getParagraphDir());
+        assertNotNull(mt.getDirections(0, 3));
+        assertEquals(0, mt.getWholeWidth(), 0);
+        assertEquals(0, mt.getWidths().size());
+        assertEquals(1, mt.getSpanEndCache().size());
+        assertEquals(3, mt.getSpanEndCache().get(0));
+        assertNotEquals(0, mt.getFontMetrics().size());
+        assertNotEquals(0, mt.getNativePtr());
+
+        // Recycle it
+        MeasuredText mt2 = MeasuredText.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, mt);
+        assertEquals(mt2, mt);
+        assertNotNull(mt2.getChars());
+        assertEquals("VVV", charsToString(mt.getChars()));
+        assertEquals(Layout.DIR_RIGHT_TO_LEFT, mt2.getParagraphDir());
+        assertNotNull(mt2.getDirections(0, 3));
+        assertEquals(0, mt2.getWholeWidth(), 0);
+        assertEquals(0, mt2.getWidths().size());
+        assertEquals(1, mt2.getSpanEndCache().size());
+        assertEquals(4, mt2.getSpanEndCache().get(0));
+        assertNotEquals(0, mt2.getFontMetrics().size());
+        assertNotEquals(0, mt2.getNativePtr());
+
+        mt2.recycle();
+    }
+
+    @Test
+    public void testFor70146381() {
+        MeasuredText.buildForMeasurement(PAINT, "X…", 0, 2, RTL, null);
+    }
+}
diff --git a/core/tests/coretests/src/android/util/KeyValueListParserTest.java b/core/tests/coretests/src/android/util/KeyValueListParserTest.java
new file mode 100644
index 0000000..038f0b7
--- /dev/null
+++ b/core/tests/coretests/src/android/util/KeyValueListParserTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link KeyValueListParser}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class KeyValueListParserTest {
+    private static final String TAG = "KeyValueListParserTest";
+    private static final int[] DEFAULT = {1, 2, 3, 4};
+
+    private KeyValueListParser mParser;
+
+    @Before
+    public void setUp() {
+        mParser = new KeyValueListParser(',');
+    }
+
+    @Test
+    public void testParseIntArrayNullInput() throws Exception {
+        mParser.setString(null);
+        int[] result = mParser.getIntArray("test", DEFAULT);
+        assertEquals(DEFAULT, result);
+    }
+
+    @Test
+    public void testParseIntArrayEmptyInput() throws Exception {
+        mParser.setString("test=");
+        int[] result = mParser.getIntArray("test", DEFAULT);
+        assertEquals(DEFAULT, result);
+    }
+
+    @Test
+    public void testParseIntArrayNullKey() throws Exception {
+        mParser.setString("foo=bar,test=100:200,baz=123");
+        int[] result = mParser.getIntArray(null, DEFAULT);
+        assertEquals(DEFAULT, result);
+    }
+
+    @Test
+    public void testParseIntArrayComplexInput() throws Exception {
+        mParser.setString("foo=bar,test=100:200,baz=123");
+        int[] result = mParser.getIntArray("test", DEFAULT);
+        assertEquals(2, result.length);
+        assertEquals(100, result[0]);  // respect order
+        assertEquals(200, result[1]);
+    }
+
+    @Test
+    public void testParseIntArrayLeadingSep() throws Exception {
+        mParser.setString("test=:4:5:6");
+        int[] result = mParser.getIntArray("test", DEFAULT);
+        assertEquals(DEFAULT, result);
+    }
+
+    @Test
+    public void testParseIntArrayEmptyItem() throws Exception {
+        mParser.setString("test=:4::6");
+        int[] result = mParser.getIntArray("test", DEFAULT);
+        assertEquals(DEFAULT, result);
+    }
+
+    @Test
+    public void testParseIntArrayTrailingSep() throws Exception {
+        mParser.setString("test=4:5:6:");
+        int[] result = mParser.getIntArray("test", DEFAULT);
+        assertEquals(3, result.length);
+        assertEquals(4, result[0]);  // respect order
+        assertEquals(5, result[1]);
+        assertEquals(6, result[2]);
+    }
+
+    @Test
+    public void testParseIntArrayGoodData() throws Exception {
+        mParser.setString("test=4:5:6");
+        int[] result = mParser.getIntArray("test", DEFAULT);
+        assertEquals(3, result.length);
+        assertEquals(4, result[0]);  // respect order
+        assertEquals(5, result[1]);
+        assertEquals(6, result[2]);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
index 13cef52..e3c91e6 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -68,6 +68,17 @@
         assertThat(clone.supportsSwitchingToNextInputMethod(), is(true));
     }
 
+    @Test
+    public void testIsVrOnly() throws Exception {
+        final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta_vr_only);
+
+        assertThat(imi.isVrOnly(), is(true));
+
+        final InputMethodInfo clone = cloneViaParcel(imi);
+
+        assertThat(clone.isVrOnly(), is(true));
+    }
+
     private InputMethodInfo buildInputMethodForTest(final @XmlRes int metaDataRes)
             throws Exception {
         final Context context = InstrumentationRegistry.getContext();
diff --git a/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
similarity index 88%
rename from services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java
rename to core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index a4e3988..449e374 100644
--- a/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.policy;
+package com.android.internal.accessibility;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.AlertDialog;
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.os.Handler;
@@ -38,7 +39,7 @@
 import android.widget.Toast;
 import com.android.internal.R;
 import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.policy.AccessibilityShortcutController.FrameworkObjectProvider;
+import com.android.internal.accessibility.AccessibilityShortcutController.FrameworkObjectProvider;
 
 import org.junit.After;
 import org.junit.Before;
@@ -50,6 +51,7 @@
 
 import java.lang.reflect.Field;
 import java.util.Collections;
+import java.util.Map;
 
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN;
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED;
@@ -58,7 +60,10 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.fail;
 import static org.mockito.AdditionalMatchers.aryEq;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyObject;
@@ -72,6 +77,7 @@
 @RunWith(AndroidJUnit4.class)
 public class AccessibilityShortcutControllerTest {
     private static final String SERVICE_NAME_STRING = "fake.package/fake.service.name";
+    private static final String SERVICE_NAME_SUMMARY = "Summary";
     private static final long VIBRATOR_PATTERN_1 = 100L;
     private static final long VIBRATOR_PATTERN_2 = 150L;
     private static final int[] VIBRATOR_PATTERN_INT = {(int) VIBRATOR_PATTERN_1,
@@ -95,6 +101,7 @@
     private @Mock Toast mToast;
     private @Mock Vibrator mVibrator;
     private @Mock ApplicationInfo mApplicationInfo;
+    private @Mock PackageManager mPackageManager;
 
     private MockContentResolver mContentResolver;
     private WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
@@ -108,6 +115,10 @@
         when(mContext.getResources()).thenReturn(mResources);
         when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo);
         when(mContext.getSystemService(Context.VIBRATOR_SERVICE)).thenReturn(mVibrator);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+
+        // We're not checking the text. Just prevent us crashing when getting text.
+        when(mPackageManager.getText(any(), anyInt(), any())).thenReturn("text");
 
         mContentResolver = new MockContentResolver(mContext);
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
@@ -125,6 +136,7 @@
                 .thenReturn(mAlertDialogBuilder);
         when(mFrameworkObjectProvider.makeToastFromText(eq(mContext), anyObject(), anyInt()))
                 .thenReturn(mToast);
+        when(mFrameworkObjectProvider.getSystemUiContext()).thenReturn(mContext);
 
         when(mResources.getString(anyInt())).thenReturn("Howdy %s");
         when(mResources.getIntArray(anyInt())).thenReturn(VIBRATOR_PATTERN_INT);
@@ -134,6 +146,7 @@
         when(mServiceInfo.getResolveInfo()).thenReturn(resolveInfo);
         when(mServiceInfo.getComponentName())
                 .thenReturn(ComponentName.unflattenFromString(SERVICE_NAME_STRING));
+        when(mServiceInfo.loadSummary(any())).thenReturn(SERVICE_NAME_SUMMARY);
 
         when(mAlertDialogBuilder.setTitle(anyInt())).thenReturn(mAlertDialogBuilder);
         when(mAlertDialogBuilder.setCancelable(anyBoolean())).thenReturn(mAlertDialogBuilder);
@@ -369,6 +382,33 @@
         verify(mAccessibilityManagerService).performAccessibilityShortcut();
     }
 
+    @Test
+    public void getFrameworkFeatureMap_shouldBeNonNullAndUnmodifiable() {
+        Map<ComponentName, AccessibilityShortcutController.ToggleableFrameworkFeatureInfo>
+                frameworkFeatureMap =
+                AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
+        assertTrue("Framework features not supported", frameworkFeatureMap.size() > 0);
+
+        try {
+            frameworkFeatureMap.clear();
+            fail("Framework feature map should be unmodifieable");
+        } catch (UnsupportedOperationException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public void testOnAccessibilityShortcut_forFrameworkFeature_callsServiceWithNoToast()
+            throws Exception {
+        configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
+        configureFirstFrameworkFeature();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1);
+        getController().performAccessibilityShortcut();
+
+        verifyZeroInteractions(mToast);
+        verify(mAccessibilityManagerService).performAccessibilityShortcut();
+    }
+
     private void configureNoShortcutService() {
         Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "");
     }
@@ -378,6 +418,14 @@
                 mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, SERVICE_NAME_STRING);
     }
 
+    private void configureFirstFrameworkFeature() {
+        ComponentName featureComponentName =
+                (ComponentName) AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
+                        .keySet().toArray()[0];
+        Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+                featureComponentName.flattenToString());
+    }
+
     private void configureShortcutEnabled(int enabledValue) {
         final boolean enabled;
         final boolean lockscreen;
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index 515e558..9061c44 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -38,6 +38,7 @@
     private static final String DUMMY_SETTING_ACTIVITY_NAME = "";
     private static final boolean DUMMY_IS_AUX_IME = false;
     private static final boolean DUMMY_FORCE_DEFAULT = false;
+    private static final boolean DUMMY_IS_VR_IME = false;
     private static final int DUMMY_IS_DEFAULT_RES_ID = 0;
     private static final String SYSTEM_LOCALE = "en_US";
     private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
@@ -75,7 +76,7 @@
         }
         final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
                 DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
-                DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod);
+                DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod, DUMMY_IS_VR_IME);
         if (subtypes == null) {
             items.add(new ImeSubtypeListItem(imeName, null /* variableName */, imi,
                     NOT_A_SUBTYPE_ID, null, SYSTEM_LOCALE));
@@ -111,7 +112,8 @@
                 .build());
         final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
                 DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
-                DUMMY_FORCE_DEFAULT, true /* supportsSwitchingToNextInputMethod */);
+                DUMMY_FORCE_DEFAULT, true /* supportsSwitchingToNextInputMethod */,
+                DUMMY_IS_VR_IME);
         return new ImeSubtypeListItem(imeName, subtypeName, imi, subtypeIndex, subtypeLocale,
                 systemLocale);
     }
diff --git a/core/tests/featureflagtests/Android.mk b/core/tests/featureflagtests/Android.mk
index f2d2058..6330b8e 100644
--- a/core/tests/featureflagtests/Android.mk
+++ b/core/tests/featureflagtests/Android.mk
@@ -10,7 +10,7 @@
 
 LOCAL_DX_FLAGS := --core-library
 LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib android-support-test
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 LOCAL_PACKAGE_NAME := FrameworksCoreFeatureFlagTests
 
 LOCAL_CERTIFICATE := platform
diff --git a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
index b8c4123..0b1b333 100644
--- a/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
+++ b/core/tests/featureflagtests/src/android/util/FeatureFlagUtilsTest.java
@@ -18,6 +18,7 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
 import android.content.Context;
@@ -59,7 +60,7 @@
 
     @Test
     public void testGetFlag_enabled_shouldReturnTrue() {
-        SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + TEST_FEATURE_NAME, "true");
+        FeatureFlagUtils.getAllFeatureFlags().put(TEST_FEATURE_NAME, "true");
 
         assertTrue(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME));
     }
@@ -96,7 +97,11 @@
 
     @Test
     public void testGetFlag_notSet_shouldReturnFalse() {
-        assertFalse(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME));
+        assertFalse(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME + "does_not_exist"));
     }
 
+    @Test
+    public void getAllFeatureFlags_shouldNotBeNull() {
+        assertNotNull(FeatureFlagUtils.getAllFeatureFlags());
+    }
 }
diff --git a/core/tests/systemproperties/Android.mk b/core/tests/systemproperties/Android.mk
index 4c2e224..57e2059 100644
--- a/core/tests/systemproperties/Android.mk
+++ b/core/tests/systemproperties/Android.mk
@@ -10,7 +10,7 @@
 
 LOCAL_DX_FLAGS := --core-library
 LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 LOCAL_PACKAGE_NAME := FrameworksCoreSystemPropertiesTests
 
 LOCAL_CERTIFICATE := platform
diff --git a/core/tests/utillib/Android.mk b/core/tests/utillib/Android.mk
index 8811256..be1ab1f 100644
--- a/core/tests/utillib/Android.mk
+++ b/core/tests/utillib/Android.mk
@@ -19,7 +19,8 @@
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_MODULE := frameworks-core-util-lib
-LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := junit
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk
index 233d070..2dc1059 100644
--- a/core/tests/utiltests/Android.mk
+++ b/core/tests/utiltests/Android.mk
@@ -19,7 +19,7 @@
     frameworks-base-testutils \
     mockito-target-minus-junit4 \
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock
 
 LOCAL_PACKAGE_NAME := FrameworksUtilTests
 
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index b3bba07..74f8c71 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -404,6 +404,8 @@
 # key 503 KEY_BRL_DOT7
 # key 504 KEY_BRL_DOT8
 
+key 522   STAR
+key 523   POUND
 key 580   APP_SWITCH
 key 582   VOICE_ASSIST
 
diff --git a/data/keyboards/Generic_Iot.kl b/data/keyboards/Generic_Iot.kl
deleted file mode 100644
index 89df224..0000000
--- a/data/keyboards/Generic_Iot.kl
+++ /dev/null
@@ -1,443 +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.
-
-#
-# Generic key layout file for full alphabetic US English PC style external keyboards.
-#
-# This file is intentionally very generic and is intended to support a broad range of keyboards.
-# Do not edit the generic key layout to support a specific keyboard; instead, create
-# a new key layout file with the required keyboard configuration.
-#
-
-key 1     ESCAPE
-key 2     1
-key 3     2
-key 4     3
-key 5     4
-key 6     5
-key 7     6
-key 8     7
-key 9     8
-key 10    9
-key 11    0
-key 12    MINUS
-key 13    EQUALS
-key 14    DEL
-key 15    TAB
-key 16    Q
-key 17    W
-key 18    E
-key 19    R
-key 20    T
-key 21    Y
-key 22    U
-key 23    I
-key 24    O
-key 25    P
-key 26    LEFT_BRACKET
-key 27    RIGHT_BRACKET
-key 28    ENTER
-key 29    CTRL_LEFT
-key 30    A
-key 31    S
-key 32    D
-key 33    F
-key 34    G
-key 35    H
-key 36    J
-key 37    K
-key 38    L
-key 39    SEMICOLON
-key 40    APOSTROPHE
-key 41    GRAVE
-key 42    SHIFT_LEFT
-key 43    BACKSLASH
-key 44    Z
-key 45    X
-key 46    C
-key 47    V
-key 48    B
-key 49    N
-key 50    M
-key 51    COMMA
-key 52    PERIOD
-key 53    SLASH
-key 54    SHIFT_RIGHT
-key 55    NUMPAD_MULTIPLY
-key 56    ALT_LEFT
-key 57    SPACE
-key 58    CAPS_LOCK
-key 59    F1
-key 60    F2
-key 61    F3
-key 62    F4
-key 63    F5
-key 64    F6
-key 65    F7
-key 66    F8
-key 67    F9
-key 68    F10
-key 69    NUM_LOCK
-key 70    SCROLL_LOCK
-key 71    NUMPAD_7
-key 72    NUMPAD_8
-key 73    NUMPAD_9
-key 74    NUMPAD_SUBTRACT
-key 75    NUMPAD_4
-key 76    NUMPAD_5
-key 77    NUMPAD_6
-key 78    NUMPAD_ADD
-key 79    NUMPAD_1
-key 80    NUMPAD_2
-key 81    NUMPAD_3
-key 82    NUMPAD_0
-key 83    NUMPAD_DOT
-# key 84 (undefined)
-key 85    ZENKAKU_HANKAKU
-key 86    BACKSLASH
-key 87    F11
-key 88    F12
-key 89    RO
-# key 90 "KEY_KATAKANA"
-# key 91 "KEY_HIRAGANA"
-key 92    HENKAN
-key 93    KATAKANA_HIRAGANA
-key 94    MUHENKAN
-key 95    NUMPAD_COMMA
-key 96    NUMPAD_ENTER
-key 97    CTRL_RIGHT
-key 98    NUMPAD_DIVIDE
-key 99    SYSRQ
-key 100   ALT_RIGHT
-# key 101 "KEY_LINEFEED"
-key 102   MOVE_HOME
-key 103   DPAD_UP
-key 104   PAGE_UP
-key 105   DPAD_LEFT
-key 106   DPAD_RIGHT
-key 107   MOVE_END
-key 108   DPAD_DOWN
-key 109   PAGE_DOWN
-key 110   INSERT
-key 111   FORWARD_DEL
-# key 112 "KEY_MACRO"
-key 113   VOLUME_MUTE
-key 114   VOLUME_DOWN
-key 115   VOLUME_UP
-key 116   POWER
-key 117   NUMPAD_EQUALS
-# key 118 "KEY_KPPLUSMINUS"
-key 119   BREAK
-# key 120 (undefined)
-key 121   NUMPAD_COMMA
-key 122   KANA
-key 123   EISU
-key 124   YEN
-key 125   META_LEFT
-key 126   META_RIGHT
-key 127   MENU
-key 128   MEDIA_STOP
-# key 129 "KEY_AGAIN"
-# key 130 "KEY_PROPS"
-# key 131 "KEY_UNDO"
-# key 132 "KEY_FRONT"
-key 133   COPY
-# key 134 "KEY_OPEN"
-key 135   PASTE
-# key 136 "KEY_FIND"
-key 137   CUT
-# key 138 "KEY_HELP"
-key 139   MENU
-key 140   CALCULATOR
-# key 141 "KEY_SETUP"
-key 142   SLEEP
-key 143   WAKEUP
-# key 144 "KEY_FILE"
-# key 145 "KEY_SENDFILE"
-# key 146 "KEY_DELETEFILE"
-# key 147 "KEY_XFER"
-# key 148 "KEY_PROG1"
-# key 149 "KEY_PROG2"
-key 150   EXPLORER
-# key 151 "KEY_MSDOS"
-key 152   POWER
-# key 153 "KEY_DIRECTION"
-# key 154 "KEY_CYCLEWINDOWS"
-key 155   ENVELOPE
-key 156   BOOKMARK
-# key 157 "KEY_COMPUTER"
-key 158   BACK
-key 159   FORWARD
-key 160   MEDIA_CLOSE
-key 161   MEDIA_EJECT
-key 162   MEDIA_EJECT
-key 163   MEDIA_NEXT
-key 164   MEDIA_PLAY_PAUSE
-key 165   MEDIA_PREVIOUS
-key 166   MEDIA_STOP
-key 167   MEDIA_RECORD
-key 168   MEDIA_REWIND
-key 169   CALL
-# key 170 "KEY_ISO"
-key 171   MUSIC
-key 172   HOME
-# key 173 "KEY_REFRESH"
-# key 174 "KEY_EXIT"
-# key 175 "KEY_MOVE"
-# key 176 "KEY_EDIT"
-key 177   PAGE_UP
-key 178   PAGE_DOWN
-key 179   NUMPAD_LEFT_PAREN
-key 180   NUMPAD_RIGHT_PAREN
-# key 181 "KEY_NEW"
-# key 182 "KEY_REDO"
-# key 183   F13
-# key 184   F14
-# key 185   F15
-# key 186   F16
-# key 187   F17
-# key 188   F18
-# key 189   F19
-# key 190   F20
-# key 191   F21
-# key 192   F22
-# key 193   F23
-# key 194   F24
-# key 195 (undefined)
-# key 196 (undefined)
-# key 197 (undefined)
-# key 198 (undefined)
-# key 199 (undefined)
-key 200   MEDIA_PLAY
-key 201   MEDIA_PAUSE
-# key 202 "KEY_PROG3"
-# key 203 "KEY_PROG4"
-# key 204 (undefined)
-# key 205 "KEY_SUSPEND"
-# key 206 "KEY_CLOSE"
-key 207   MEDIA_PLAY
-key 208   MEDIA_FAST_FORWARD
-# key 209 "KEY_BASSBOOST"
-# key 210 "KEY_PRINT"
-# key 211 "KEY_HP"
-key 212   CAMERA
-key 213   MUSIC
-# key 214 "KEY_QUESTION"
-key 215   ENVELOPE
-# key 216 "KEY_CHAT"
-key 217   SEARCH
-# key 218 "KEY_CONNECT"
-# key 219 "KEY_FINANCE"
-# key 220 "KEY_SPORT"
-# key 221 "KEY_SHOP"
-# key 222 "KEY_ALTERASE"
-# key 223 "KEY_CANCEL"
-key 224   BRIGHTNESS_DOWN
-key 225   BRIGHTNESS_UP
-key 226   HEADSETHOOK
-key 227   POUND
-key 228   STAR
-
-key 256   BUTTON_1
-key 257   BUTTON_2
-key 258   BUTTON_3
-key 259   BUTTON_4
-key 260   BUTTON_5
-key 261   BUTTON_6
-key 262   BUTTON_7
-key 263   BUTTON_8
-key 264   BUTTON_9
-key 265   BUTTON_10
-key 266   BUTTON_11
-key 267   BUTTON_12
-key 268   BUTTON_13
-key 269   BUTTON_14
-key 270   BUTTON_15
-key 271   BUTTON_16
-
-key 288   BUTTON_1
-key 289   BUTTON_2
-key 290   BUTTON_3
-key 291   BUTTON_4
-key 292   BUTTON_5
-key 293   BUTTON_6
-key 294   BUTTON_7
-key 295   BUTTON_8
-key 296   BUTTON_9
-key 297   BUTTON_10
-key 298   BUTTON_11
-key 299   BUTTON_12
-key 300   BUTTON_13
-key 301   BUTTON_14
-key 302   BUTTON_15
-key 303   BUTTON_16
-
-
-key 304   BUTTON_A
-key 305   BUTTON_B
-key 306   BUTTON_C
-key 307   BUTTON_X
-key 308   BUTTON_Y
-key 309   BUTTON_Z
-key 310   BUTTON_L1
-key 311   BUTTON_R1
-key 312   BUTTON_L2
-key 313   BUTTON_R2
-key 314   BUTTON_SELECT
-key 315   BUTTON_START
-key 316   BUTTON_MODE
-key 317   BUTTON_THUMBL
-key 318   BUTTON_THUMBR
-
-
-# key 352 "KEY_OK"
-key 353   DPAD_CENTER
-# key 354 "KEY_GOTO"
-# key 355 "KEY_CLEAR"
-# key 356 "KEY_POWER2"
-# key 357 "KEY_OPTION"
-# key 358 "KEY_INFO"
-# key 359 "KEY_TIME"
-# key 360 "KEY_VENDOR"
-# key 361 "KEY_ARCHIVE"
-key 362   GUIDE
-# key 363 "KEY_CHANNEL"
-# key 364 "KEY_FAVORITES"
-# key 365 "KEY_EPG"
-key 366   DVR
-# key 367 "KEY_MHP"
-# key 368 "KEY_LANGUAGE"
-# key 369 "KEY_TITLE"
-# key 370 "KEY_SUBTITLE"
-# key 371 "KEY_ANGLE"
-# key 372 "KEY_ZOOM"
-# key 373 "KEY_MODE"
-# key 374 "KEY_KEYBOARD"
-# key 375 "KEY_SCREEN"
-# key 376 "KEY_PC"
-key 377   TV
-# key 378 "KEY_TV2"
-# key 379 "KEY_VCR"
-# key 380 "KEY_VCR2"
-# key 381 "KEY_SAT"
-# key 382 "KEY_SAT2"
-# key 383 "KEY_CD"
-# key 384 "KEY_TAPE"
-# key 385 "KEY_RADIO"
-# key 386 "KEY_TUNER"
-# key 387 "KEY_PLAYER"
-# key 388 "KEY_TEXT"
-# key 389 "KEY_DVD"
-# key 390 "KEY_AUX"
-# key 391 "KEY_MP3"
-# key 392 "KEY_AUDIO"
-# key 393 "KEY_VIDEO"
-# key 394 "KEY_DIRECTORY"
-# key 395 "KEY_LIST"
-# key 396 "KEY_MEMO"
-key 397   CALENDAR
-# key 398 "KEY_RED"
-# key 399 "KEY_GREEN"
-# key 400 "KEY_YELLOW"
-# key 401 "KEY_BLUE"
-key 402   CHANNEL_UP
-key 403   CHANNEL_DOWN
-# key 404 "KEY_FIRST"
-# key 405 "KEY_LAST"
-# key 406 "KEY_AB"
-# key 407 "KEY_NEXT"
-# key 408 "KEY_RESTART"
-# key 409 "KEY_SLOW"
-# key 410 "KEY_SHUFFLE"
-# key 411 "KEY_BREAK"
-# key 412 "KEY_PREVIOUS"
-# key 413 "KEY_DIGITS"
-# key 414 "KEY_TEEN"
-# key 415 "KEY_TWEN"
-
-key 429   CONTACTS
-
-# key 448 "KEY_DEL_EOL"
-# key 449 "KEY_DEL_EOS"
-# key 450 "KEY_INS_LINE"
-# key 451 "KEY_DEL_LINE"
-
-
-key 464   FUNCTION
-key 465   ESCAPE            FUNCTION
-key 466   F1                FUNCTION
-key 467   F2                FUNCTION
-key 468   F3                FUNCTION
-key 469   F4                FUNCTION
-key 470   F5                FUNCTION
-key 471   F6                FUNCTION
-key 472   F7                FUNCTION
-key 473   F8                FUNCTION
-key 474   F9                FUNCTION
-key 475   F10               FUNCTION
-key 476   F11               FUNCTION
-key 477   F12               FUNCTION
-key 478   1                 FUNCTION
-key 479   2                 FUNCTION
-key 480   D                 FUNCTION
-key 481   E                 FUNCTION
-key 482   F                 FUNCTION
-key 483   S                 FUNCTION
-key 484   B                 FUNCTION
-
-
-# key 497 KEY_BRL_DOT1
-# key 498 KEY_BRL_DOT2
-# key 499 KEY_BRL_DOT3
-# key 500 KEY_BRL_DOT4
-# key 501 KEY_BRL_DOT5
-# key 502 KEY_BRL_DOT6
-# key 503 KEY_BRL_DOT7
-# key 504 KEY_BRL_DOT8
-
-key 580   APP_SWITCH
-key 582   VOICE_ASSIST
-
-# Keys defined by HID usages
-key usage 0x0c006F BRIGHTNESS_UP
-key usage 0x0c0070 BRIGHTNESS_DOWN
-
-# Joystick and game controller axes.
-# Axes that are not mapped will be assigned generic axis numbers by the input subsystem.
-axis 0x00 X
-axis 0x01 Y
-axis 0x02 Z
-axis 0x03 RX
-axis 0x04 RY
-axis 0x05 RZ
-axis 0x06 THROTTLE
-axis 0x07 RUDDER
-axis 0x08 WHEEL
-axis 0x09 GAS
-axis 0x0a BRAKE
-axis 0x10 HAT_X
-axis 0x11 HAT_Y
-
-# LEDs
-led 0x00 NUM_LOCK
-led 0x01 CAPS_LOCK
-led 0x02 SCROLL_LOCK
-led 0x03 COMPOSE
-led 0x04 KANA
-led 0x05 SLEEP
-led 0x06 SUSPEND
-led 0x07 MUTE
-led 0x08 MISC
-led 0x09 MAIL
-led 0x0a CHARGING
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 7b2e21a..c71585f 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -896,6 +896,13 @@
         return mVectorState.getNativeRenderer();
     }
 
+    /**
+     * @hide
+     */
+    public void setAntiAlias(boolean aa) {
+        nSetAntiAlias(mVectorState.mNativeTree.get(), aa);
+    }
+
     static class VectorDrawableState extends ConstantState {
         // Variables below need to be copied (deep copy if applicable) for mutation.
         int[] mThemeAttrs;
@@ -2269,6 +2276,8 @@
     @FastNative
     private static native float nGetRootAlpha(long rendererPtr);
     @FastNative
+    private static native void nSetAntiAlias(long rendererPtr, boolean aa);
+    @FastNative
     private static native void nSetAllowCaching(long rendererPtr, boolean allowCaching);
 
     @FastNative
diff --git a/keystore/java/android/security/AttestedKeyPair.java b/keystore/java/android/security/AttestedKeyPair.java
new file mode 100644
index 0000000..c6bff5c
--- /dev/null
+++ b/keystore/java/android/security/AttestedKeyPair.java
@@ -0,0 +1,75 @@
+/*
+ * 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.security;
+
+import java.security.KeyPair;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * The {@code AttestedKeyPair} class contains a {@code KeyPair} instance of
+ * keys generated by Keystore and owned by KeyChain, as well as an attestation
+ * record for the key.
+ *
+ * <p>Such keys can be obtained by calling
+ * {@link android.app.admin.DevicePolicyManager#generateKeyPair}.
+ */
+
+public final class AttestedKeyPair {
+    private final KeyPair mKeyPair;
+    private final Certificate[] mAttestationRecord;
+
+    /**
+     * @hide Only created by the platform, no need to expose as public API.
+     */
+    public AttestedKeyPair(KeyPair keyPair, Certificate[] attestationRecord) {
+        mKeyPair = keyPair;
+        mAttestationRecord = attestationRecord;
+    }
+
+    /**
+     * Returns the generated key pair associated with the attestation record
+     * in this instance.
+     */
+    public KeyPair getKeyPair() {
+        return mKeyPair;
+    }
+
+    /**
+     * Returns the attestation record for the key pair in this instance.
+     *
+     * The attestation record is a chain of certificates. The leaf certificate links to the public
+     * key of this key pair and other properties of the key or the device. If the key is in secure
+     * hardware, and if the secure hardware supports attestation, the leaf certificate will be
+     * signed by a chain of certificates rooted at a trustworthy CA key. Otherwise the chain will be
+     * rooted at an untrusted certificate.
+     *
+     * The attestation record could be for properties of the key, or include device identifiers.
+     *
+     * See {@link android.security.keystore.KeyGenParameterSpec.Builder#setAttestationChallenge}
+     * and  <a href="https://developer.android.com/training/articles/security-key-attestation.html">
+     * Key Attestation</a> for the format of the attestation record inside the certificate.
+     */
+    public List<Certificate> getAttestationRecord() {
+        if (mAttestationRecord == null) {
+            return new ArrayList();
+        }
+        return Arrays.asList(mAttestationRecord);
+    }
+}
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 635432d..b4331b2 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -16,6 +16,7 @@
 package android.security;
 
 import android.content.pm.StringParceledListSlice;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
 
 /**
  * Caller is required to ensure that {@link KeyStore#unlock
@@ -31,6 +32,8 @@
     boolean isUserSelectable(String alias);
     void setUserSelectable(String alias, boolean isUserSelectable);
 
+    boolean generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
+
     // APIs used by CertInstaller and DevicePolicyManager
     String installCaCertificate(in byte[] caCertificate);
 
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index ed40b77..87677d4 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -195,7 +195,7 @@
  * <pre> {@code
  * KeyGenerator keyGenerator = KeyGenerator.getInstance(
  *         KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
- * keyGenerator.initialize(
+ * keyGenerator.init(
  *         new KeyGenParameterSpec.Builder("key2",
  *                 KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
  *                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
@@ -219,7 +219,7 @@
  * <pre> {@code
  * KeyGenerator keyGenerator = KeyGenerator.getInstance(
  *         KeyProperties.KEY_ALGORITHM_HMAC_SHA256, "AndroidKeyStore");
- * keyGenerator.initialize(
+ * keyGenerator.init(
  *         new KeyGenParameterSpec.Builder("key2", KeyProperties.PURPOSE_SIGN).build());
  * SecretKey key = keyGenerator.generateKey();
  * Mac mac = Mac.getInstance("HmacSHA256");
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.aidl b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.aidl
new file mode 100644
index 0000000..3fb7b4f
--- /dev/null
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.security.keystore;
+
+parcelable ParcelableKeyGenParameterSpec;
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
new file mode 100644
index 0000000..b15e0a2
--- /dev/null
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -0,0 +1,170 @@
+/*
+ * 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.security.keystore;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import java.math.BigInteger;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * A parcelable version of KeyGenParameterSpec
+ * @hide only used for communicating with the DPMS.
+ */
+public final class ParcelableKeyGenParameterSpec implements Parcelable {
+    private static final int ALGORITHM_PARAMETER_SPEC_NONE = 1;
+    private static final int ALGORITHM_PARAMETER_SPEC_RSA = 2;
+    private static final int ALGORITHM_PARAMETER_SPEC_EC = 3;
+
+    private final KeyGenParameterSpec mSpec;
+
+    public ParcelableKeyGenParameterSpec(
+            KeyGenParameterSpec spec) {
+        mSpec = spec;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    private static void writeOptionalDate(Parcel out, Date date) {
+        if (date != null) {
+            out.writeBoolean(true);
+            out.writeLong(date.getTime());
+        } else {
+            out.writeBoolean(false);
+        }
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mSpec.getKeystoreAlias());
+        out.writeInt(mSpec.getPurposes());
+        out.writeInt(mSpec.getUid());
+        out.writeInt(mSpec.getKeySize());
+
+        // Only needs to support RSAKeyGenParameterSpec and ECGenParameterSpec.
+        AlgorithmParameterSpec algoSpec = mSpec.getAlgorithmParameterSpec();
+        if (algoSpec == null) {
+            out.writeInt(ALGORITHM_PARAMETER_SPEC_NONE);
+        } else if (algoSpec instanceof RSAKeyGenParameterSpec) {
+            RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) algoSpec;
+            out.writeInt(ALGORITHM_PARAMETER_SPEC_RSA);
+            out.writeInt(rsaSpec.getKeysize());
+            out.writeByteArray(rsaSpec.getPublicExponent().toByteArray());
+        } else if (algoSpec instanceof ECGenParameterSpec) {
+            ECGenParameterSpec ecSpec = (ECGenParameterSpec) algoSpec;
+            out.writeInt(ALGORITHM_PARAMETER_SPEC_EC);
+            out.writeString(ecSpec.getName());
+        } else {
+            throw new IllegalArgumentException(
+                    String.format("Unknown algorithm parameter spec: %s", algoSpec.getClass()));
+        }
+        out.writeByteArray(mSpec.getCertificateSubject().getEncoded());
+        out.writeByteArray(mSpec.getCertificateSerialNumber().toByteArray());
+        writeOptionalDate(out, mSpec.getCertificateNotBefore());
+        writeOptionalDate(out, mSpec.getCertificateNotAfter());
+        writeOptionalDate(out, mSpec.getKeyValidityStart());
+        writeOptionalDate(out, mSpec.getKeyValidityForOriginationEnd());
+        writeOptionalDate(out, mSpec.getKeyValidityForConsumptionEnd());
+        out.writeStringArray(mSpec.getDigests());
+        out.writeStringArray(mSpec.getEncryptionPaddings());
+        out.writeStringArray(mSpec.getSignaturePaddings());
+        out.writeStringArray(mSpec.getBlockModes());
+        out.writeBoolean(mSpec.isRandomizedEncryptionRequired());
+        out.writeBoolean(mSpec.isUserAuthenticationRequired());
+        out.writeInt(mSpec.getUserAuthenticationValidityDurationSeconds());
+        out.writeByteArray(mSpec.getAttestationChallenge());
+        out.writeBoolean(mSpec.isUniqueIdIncluded());
+        out.writeBoolean(mSpec.isUserAuthenticationValidWhileOnBody());
+        out.writeBoolean(mSpec.isInvalidatedByBiometricEnrollment());
+    }
+
+    private static Date readDateOrNull(Parcel in) {
+        boolean hasDate = in.readBoolean();
+        if (hasDate) {
+            return new Date(in.readLong());
+        } else {
+            return null;
+        }
+    }
+
+    private ParcelableKeyGenParameterSpec(Parcel in) {
+        String keystoreAlias = in.readString();
+        int purposes = in.readInt();
+        KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keystoreAlias, purposes);
+        builder.setUid(in.readInt());
+        builder.setKeySize(in.readInt());
+
+        int keySpecType = in.readInt();
+        AlgorithmParameterSpec algorithmSpec = null;
+        if (keySpecType == ALGORITHM_PARAMETER_SPEC_NONE) {
+            algorithmSpec = null;
+        } else if (keySpecType == ALGORITHM_PARAMETER_SPEC_RSA) {
+            int rsaKeySize = in.readInt();
+            BigInteger publicExponent = new BigInteger(in.createByteArray());
+            algorithmSpec = new RSAKeyGenParameterSpec(rsaKeySize, publicExponent);
+        } else if (keySpecType == ALGORITHM_PARAMETER_SPEC_EC) {
+            String stdName = in.readString();
+            algorithmSpec = new ECGenParameterSpec(stdName);
+        } else {
+            throw new IllegalArgumentException(
+                    String.format("Unknown algorithm parameter spec: %d", algorithmSpec));
+        }
+        builder.setAlgorithmParameterSpec(algorithmSpec);
+        builder.setCertificateSubject(new X500Principal(in.createByteArray()));
+        builder.setCertificateSerialNumber(new BigInteger(in.createByteArray()));
+        builder.setCertificateNotBefore(readDateOrNull(in));
+        builder.setCertificateNotAfter(readDateOrNull(in));
+        builder.setKeyValidityStart(readDateOrNull(in));
+        builder.setKeyValidityForOriginationEnd(readDateOrNull(in));
+        builder.setKeyValidityForConsumptionEnd(readDateOrNull(in));
+        builder.setDigests(in.createStringArray());
+        builder.setEncryptionPaddings(in.createStringArray());
+        builder.setSignaturePaddings(in.createStringArray());
+        builder.setBlockModes(in.createStringArray());
+        builder.setRandomizedEncryptionRequired(in.readBoolean());
+        builder.setUserAuthenticationRequired(in.readBoolean());
+        builder.setUserAuthenticationValidityDurationSeconds(in.readInt());
+        builder.setAttestationChallenge(in.createByteArray());
+        builder.setUniqueIdIncluded(in.readBoolean());
+        builder.setUserAuthenticationValidWhileOnBody(in.readBoolean());
+        builder.setInvalidatedByBiometricEnrollment(in.readBoolean());
+        mSpec = builder.build();
+    }
+
+    public static final Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() {
+        @Override
+        public ParcelableKeyGenParameterSpec createFromParcel(Parcel in) {
+            return new ParcelableKeyGenParameterSpec(in);
+        }
+
+        @Override
+        public ParcelableKeyGenParameterSpec[] newArray(int size) {
+            return new ParcelableKeyGenParameterSpec[size];
+        }
+    };
+
+    public KeyGenParameterSpec getSpec() {
+        return mSpec;
+    }
+}
diff --git a/keystore/tests/Android.mk b/keystore/tests/Android.mk
new file mode 100644
index 0000000..51adde4
--- /dev/null
+++ b/keystore/tests/Android.mk
@@ -0,0 +1,34 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# LOCAL_MODULE := keystore
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    legacy-android-test
+
+LOCAL_PACKAGE_NAME := KeystoreTests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
diff --git a/keystore/tests/AndroidManifest.xml b/keystore/tests/AndroidManifest.xml
new file mode 100644
index 0000000..9bf2d0c
--- /dev/null
+++ b/keystore/tests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.security.tests">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.security.tests"
+        android:label="Tests for Keystore">
+    </instrumentation>
+</manifest>
+
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
new file mode 100644
index 0000000..73b489f
--- /dev/null
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.security;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import android.os.Parcel;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.support.test.runner.AndroidJUnit4;
+import java.math.BigInteger;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
+import java.util.Date;
+import javax.security.auth.x500.X500Principal;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link ParcelableKeyGenParameterSpec}. */
+@RunWith(AndroidJUnit4.class)
+public final class ParcelableKeyGenParameterSpecTest {
+    static final String ALIAS = "keystore-alias";
+    static final String ANOTHER_ALIAS = "another-keystore-alias";
+    static final int KEY_PURPOSES = KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY;
+    static final int UID = 1230;
+    static final int KEYSIZE = 2048;
+    static final X500Principal SUBJECT = new X500Principal("CN=subject");
+    static final BigInteger SERIAL = new BigInteger("1234567890");
+    static final Date NOT_BEFORE = new Date(1511799590);
+    static final Date NOT_AFTER = new Date(1511899590);
+    static final Date KEY_VALIDITY_START = new Date(1511799591);
+    static final Date KEY_VALIDITY_FOR_ORIG_END = new Date(1511799593);
+    static final Date KEY_VALIDITY_FOR_CONSUMPTION_END = new Date(1511799594);
+    static final String DIGEST = KeyProperties.DIGEST_SHA256;
+    static final String ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1;
+    static final String SIGNATURE_PADDING = KeyProperties.SIGNATURE_PADDING_RSA_PSS;
+    static final String BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
+    static final int USER_AUTHENTICATION_DURATION = 300;
+    static final byte[] ATTESTATION_CHALLENGE = new byte[] {'c', 'h'};
+
+    KeyGenParameterSpec configureDefaultSpec() {
+        return new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+                .setUid(UID)
+                .setKeySize(KEYSIZE)
+                .setCertificateSubject(SUBJECT)
+                .setCertificateSerialNumber(SERIAL)
+                .setCertificateNotBefore(NOT_BEFORE)
+                .setCertificateNotAfter(NOT_AFTER)
+                .setKeyValidityStart(KEY_VALIDITY_START)
+                .setKeyValidityForOriginationEnd(KEY_VALIDITY_FOR_ORIG_END)
+                .setKeyValidityForConsumptionEnd(KEY_VALIDITY_FOR_CONSUMPTION_END)
+                .setDigests(DIGEST)
+                .setEncryptionPaddings(ENCRYPTION_PADDING)
+                .setSignaturePaddings(SIGNATURE_PADDING)
+                .setBlockModes(BLOCK_MODE)
+                .setRandomizedEncryptionRequired(true)
+                .setUserAuthenticationRequired(true)
+                .setUserAuthenticationValidityDurationSeconds(USER_AUTHENTICATION_DURATION)
+                .setAttestationChallenge(ATTESTATION_CHALLENGE)
+                .setUniqueIdIncluded(true)
+                .setUserAuthenticationValidWhileOnBody(true)
+                .setInvalidatedByBiometricEnrollment(true)
+                .build();
+    }
+
+    void validateSpecValues(KeyGenParameterSpec spec, int uid, String alias) {
+        assertThat(spec.getKeystoreAlias(), is(alias));
+        assertThat(spec.getPurposes(), is(KEY_PURPOSES));
+        assertThat(spec.getUid(), is(uid));
+        assertThat(spec.getKeySize(), is(KEYSIZE));
+        assertThat(spec.getCertificateSubject(), is(SUBJECT));
+        assertThat(spec.getCertificateSerialNumber(), is(SERIAL));
+        assertThat(spec.getCertificateNotBefore(), is(NOT_BEFORE));
+        assertThat(spec.getCertificateNotAfter(), is(NOT_AFTER));
+        assertThat(spec.getKeyValidityStart(), is(KEY_VALIDITY_START));
+        assertThat(spec.getKeyValidityForOriginationEnd(), is(KEY_VALIDITY_FOR_ORIG_END));
+        assertThat(spec.getKeyValidityForConsumptionEnd(), is(KEY_VALIDITY_FOR_CONSUMPTION_END));
+        assertThat(spec.getDigests(), is(new String[] {DIGEST}));
+        assertThat(spec.getEncryptionPaddings(), is(new String[] {ENCRYPTION_PADDING}));
+        assertThat(spec.getSignaturePaddings(), is(new String[] {SIGNATURE_PADDING}));
+        assertThat(spec.getBlockModes(), is(new String[] {BLOCK_MODE}));
+        assertThat(spec.isRandomizedEncryptionRequired(), is(true));
+        assertThat(spec.isUserAuthenticationRequired(), is(true));
+        assertThat(
+                spec.getUserAuthenticationValidityDurationSeconds(),
+                is(USER_AUTHENTICATION_DURATION));
+        assertThat(spec.getAttestationChallenge(), is(ATTESTATION_CHALLENGE));
+        assertThat(spec.isUniqueIdIncluded(), is(true));
+        assertThat(spec.isUserAuthenticationValidWhileOnBody(), is(true));
+        assertThat(spec.isInvalidatedByBiometricEnrollment(), is(true));
+    }
+
+    private Parcel parcelForReading(ParcelableKeyGenParameterSpec spec) {
+        Parcel parcel = Parcel.obtain();
+        spec.writeToParcel(parcel, spec.describeContents());
+
+        parcel.setDataPosition(0);
+        return parcel;
+    }
+
+    @Test
+    public void testParcelingWithAllValues() {
+        ParcelableKeyGenParameterSpec spec =
+            new ParcelableKeyGenParameterSpec(configureDefaultSpec());
+        Parcel parcel = parcelForReading(spec);
+        ParcelableKeyGenParameterSpec fromParcel =
+            ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel);
+        validateSpecValues(fromParcel.getSpec(), UID, ALIAS);
+        assertThat(parcel.dataAvail(), is(0));
+    }
+
+    @Test
+    public void testParcelingWithNullValues() {
+        ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+            new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES).build());
+
+        Parcel parcel = parcelForReading(spec);
+        KeyGenParameterSpec fromParcel = ParcelableKeyGenParameterSpec.CREATOR
+            .createFromParcel(parcel)
+            .getSpec();
+        assertThat(fromParcel.getKeystoreAlias(), is(ALIAS));
+        assertThat(fromParcel.getPurposes(), is(KEY_PURPOSES));
+        assertThat(fromParcel.getCertificateNotBefore(), is(new Date(0L)));
+        assertThat(fromParcel.getCertificateNotAfter(), is(new Date(2461449600000L)));
+        assertThat(parcel.dataAvail(), is(0));
+    }
+
+    @Test
+    public void testParcelingRSAAlgoParameter() {
+        RSAKeyGenParameterSpec rsaSpec =
+                new RSAKeyGenParameterSpec(2048, new BigInteger("5231123"));
+        ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+            new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+            .setAlgorithmParameterSpec(rsaSpec)
+            .build());
+
+        Parcel parcel = parcelForReading(spec);
+        KeyGenParameterSpec fromParcel =
+            ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel).getSpec();
+        RSAKeyGenParameterSpec parcelSpec =
+                (RSAKeyGenParameterSpec) fromParcel.getAlgorithmParameterSpec();
+        // Compare individual fields as RSAKeyGenParameterSpec, on android, does not
+        // implement equals()
+        assertEquals(parcelSpec.getKeysize(), rsaSpec.getKeysize());
+        assertEquals(parcelSpec.getPublicExponent(), rsaSpec.getPublicExponent());
+    }
+
+    @Test
+    public void testParcelingECAlgoParameter() {
+        ECGenParameterSpec ecSpec = new ECGenParameterSpec("P-256");
+        ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+                new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+                        .setAlgorithmParameterSpec(ecSpec)
+                        .build());
+        Parcel parcel = parcelForReading(spec);
+        KeyGenParameterSpec fromParcel =
+            ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel).getSpec();
+        // Compare individual fields as ECGenParameterSpec, on android, does not
+        // implement equals()
+        ECGenParameterSpec parcelSpec = (ECGenParameterSpec) fromParcel.getAlgorithmParameterSpec();
+        assertEquals(parcelSpec.getName(), ecSpec.getName());
+    }
+}
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index d41db63..4243e7e 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -59,6 +59,7 @@
 bool Properties::filterOutTestOverhead = false;
 bool Properties::disableVsync = false;
 bool Properties::skpCaptureEnabled = false;
+bool Properties::enableRTAnimations = true;
 
 static int property_get_int(const char* key, int defaultValue) {
     char buf[PROPERTY_VALUE_MAX] = {
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 9c30e4a..af4b694 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -255,6 +255,9 @@
 
     static bool skpCaptureEnabled;
 
+    // For experimentation b/68769804
+    ANDROID_API static bool enableRTAnimations;
+
     // Used for testing only to change the render pipeline.
     static void overrideRenderPipelineType(RenderPipelineType);
 
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index ce00488..f118e8d 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -166,7 +166,7 @@
 
     if (needsFill) {
         paint.setStyle(SkPaint::Style::kFill_Style);
-        paint.setAntiAlias(true);
+        paint.setAntiAlias(mAntiAlias);
         outCanvas->drawPath(renderPath, paint);
     }
 
@@ -182,7 +182,7 @@
     }
     if (needsStroke) {
         paint.setStyle(SkPaint::Style::kStroke_Style);
-        paint.setAntiAlias(true);
+        paint.setAntiAlias(mAntiAlias);
         paint.setStrokeJoin(SkPaint::Join(properties.getStrokeLineJoin()));
         paint.setStrokeCap(SkPaint::Cap(properties.getStrokeLineCap()));
         paint.setStrokeMiter(properties.getStrokeMiterLimit());
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 7f75609..d9cf8ab 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -124,6 +124,7 @@
     virtual void onPropertyChanged(Properties* properties) = 0;
     virtual ~Node() {}
     virtual void syncProperties() = 0;
+    virtual void setAntiAlias(bool aa) = 0;
 
 protected:
     std::string mName;
@@ -354,6 +355,7 @@
             }
         }
     }
+    virtual void setAntiAlias(bool aa) { mAntiAlias = aa; }
 
 protected:
     const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) override;
@@ -365,6 +367,8 @@
 
     // Intermediate data for drawing, render thread only
     SkPath mTrimmedSkPath;
+    // Default to use AntiAlias
+    bool mAntiAlias = true;
 };
 
 class ANDROID_API ClipPath : public Path {
@@ -373,6 +377,7 @@
     ClipPath(const char* path, size_t strLength) : Path(path, strLength) {}
     ClipPath() : Path() {}
     void draw(SkCanvas* outCanvas, bool useStagingData) override;
+    virtual void setAntiAlias(bool aa) {}
 };
 
 class ANDROID_API Group : public Node {
@@ -476,6 +481,12 @@
         }
     }
 
+    virtual void setAntiAlias(bool aa) {
+        for (auto& child : mChildren) {
+            child->setAntiAlias(aa);
+        }
+    }
+
 private:
     GroupProperties mProperties = GroupProperties(this);
     GroupProperties mStagingProperties = GroupProperties(this);
@@ -641,6 +652,8 @@
      */
     void updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext* context);
 
+    void setAntiAlias(bool aa) { mRootNode->setAntiAlias(aa); }
+
 private:
     class Cache {
     public:
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 845acc0..e2f02df 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -205,7 +205,13 @@
             if (layerNeedsPaint(layerProperties, alphaMultiplier, &tmpPaint)) {
                 paint = &tmpPaint;
             }
-            renderNode->getLayerSurface()->draw(canvas, 0, 0, paint);
+
+            // surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so
+            // we need to restrict the portion of the surface drawn to the size of the renderNode.
+            SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width());
+            SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height());
+            canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(),
+                                  bounds, bounds, paint);
 
             if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
                 renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index d5fe7f4..4ba368f 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -161,14 +161,18 @@
 
 bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
                                        bool wideColorGamut) {
+    // compute the size of the surface (i.e. texture) to be allocated for this layer
+    const int surfaceWidth = ceilf(node->getWidth() / float(LAYER_SIZE)) * LAYER_SIZE;
+    const int surfaceHeight = ceilf(node->getHeight() / float(LAYER_SIZE)) * LAYER_SIZE;
+
     SkSurface* layer = node->getLayerSurface();
-    if (!layer || layer->width() != node->getWidth() || layer->height() != node->getHeight()) {
+    if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) {
         SkImageInfo info;
         if (wideColorGamut) {
-            info = SkImageInfo::Make(node->getWidth(), node->getHeight(), kRGBA_F16_SkColorType,
+            info = SkImageInfo::Make(surfaceWidth, surfaceHeight, kRGBA_F16_SkColorType,
                                      kPremul_SkAlphaType);
         } else {
-            info = SkImageInfo::MakeN32Premul(node->getWidth(), node->getHeight());
+            info = SkImageInfo::MakeN32Premul(surfaceWidth, surfaceHeight);
         }
         SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
         SkASSERT(mRenderThread.getGrContext() != nullptr);
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b7bb2d15..820789d 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -34,6 +34,7 @@
 #include "renderstate/Stencil.h"
 #include "utils/GLUtils.h"
 #include "utils/TimeUtils.h"
+#include "../Properties.h"
 
 #include <cutils/properties.h>
 #include <google/protobuf/io/zero_copy_stream_impl.h>
@@ -375,6 +376,9 @@
     }
 
     if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
+        if (CC_UNLIKELY(!Properties::enableRTAnimations)) {
+            info.out.requiresUiRedraw = true;
+        }
         if (!info.out.requiresUiRedraw) {
             // If animationsNeedsRedraw is set don't bother posting for an RT anim
             // as we will just end up fighting the UI thread.
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 7dd271f..c7f57fe 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -460,15 +460,15 @@
         ProjectionLayer(int* drawCounter)
                 : SkSurface_Base(SkImageInfo::MakeN32Premul(LAYER_WIDTH, LAYER_HEIGHT), nullptr)
                 , mDrawCounter(drawCounter) {}
-        void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) override {
+        virtual sk_sp<SkImage> onNewImageSnapshot() override {
             EXPECT_EQ(3, (*mDrawCounter)++);
             EXPECT_EQ(SkRect::MakeLTRB(100 - SCROLL_X, 100 - SCROLL_Y, 300 - SCROLL_X,
                                        300 - SCROLL_Y),
                       TestUtils::getClipBounds(this->getCanvas()));
+            return nullptr;
         }
         SkCanvas* onNewCanvas() override { return new ProjectionTestCanvas(mDrawCounter); }
         sk_sp<SkSurface> onNewSurface(const SkImageInfo&) override { return nullptr; }
-        sk_sp<SkImage> onNewImageSnapshot() override { return nullptr; }
         void onCopyOnWrite(ContentChangeMode) override {}
         int* mDrawCounter;
     };
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index a92cbfd..e7496f7 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -85,32 +85,8 @@
              outPath->rCubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0);
              outPath->cubicTo(16.0, 16.0, 9.0, 9.0, 9.0, 9.0);
              outPath->rCubicTo(0.0, 0.0, 9.0, 9.0, 9.0, 9.0);
-             outPath->cubicTo(18.447775037328352, 20.404243860300607, 17.998389141249767,
-                              22.8911717921705, 16.737515350332117, 24.986664170401575);
-             outPath->cubicTo(15.476641559414468, 27.08215654863265, 13.489843598291483,
-                              28.644011882390082, 11.155893964798905, 29.37447073281729);
-             outPath->cubicTo(8.821944331306327, 30.1049295832445, 6.299226382436471,
-                              29.954422532383525, 4.0686829203897235, 28.951642951534332);
-             outPath->cubicTo(1.838139458342976, 27.94886337068514, 0.05113662931485696,
-                              26.161860541657013, -0.9516429515343354, 23.931317079610267);
-             outPath->cubicTo(-1.9544225323835278, 21.70077361756352, -2.1049295832444987,
-                              19.178055668693663, -1.37447073281729, 16.844106035201087);
-             outPath->cubicTo(-0.6440118823900814, 14.51015640170851, 0.9178434513673546,
-                              12.523358440585524, 3.0133358295984305, 11.262484649667876);
-             outPath->cubicTo(5.108828207829506, 10.001610858750228, 7.5957561396993984,
-                              9.552224962671648, 10.000000000000005, 10.0);
-             outPath->cubicTo(10.0, 7.348852265086975, 11.054287646850167, 4.803576729418881,
-                              12.928932188134523, 2.9289321881345254);
-             outPath->cubicTo(14.803576729418879, 1.0542876468501696, 17.348852265086972,
-                              4.870079381441987E-16, 19.999999999999996, 0.0);
-             outPath->cubicTo(22.65114773491302, -4.870079381441987E-16, 25.19642327058112,
-                              1.0542876468501678, 27.071067811865476, 2.9289321881345227);
-             outPath->cubicTo(28.94571235314983, 4.803576729418878, 30.0, 7.348852265086974, 30.0,
-                              9.999999999999998);
-             outPath->cubicTo(30.0, 12.651147734913023, 28.94571235314983, 15.19642327058112,
-                              27.071067811865476, 17.071067811865476);
-             outPath->cubicTo(25.19642327058112, 18.94571235314983, 22.651147734913028, 20.0,
-                              20.000000000000004, 20.0);
+             outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0, 10.0);
+             outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0, 20.0);
          }},
 
         // Check box VectorDrawable path data
@@ -181,22 +157,7 @@
          },
          [](SkPath* outPath) {
              outPath->moveTo(300.0, 70.0);
-             outPath->cubicTo(239.06697794203706, 70.13246340443499, 180.6164396449267,
-                              94.47383115953485, 137.6004913602211, 137.6302781499585);
-             outPath->cubicTo(94.58454307551551, 180.78672514038215, 70.43390412842275,
-                              239.3163266242308, 70.50013586976587, 300.2494566687817);
-             outPath->cubicTo(70.56636761110899, 361.1825867133326, 94.84418775550249,
-                              419.65954850554147, 137.9538527586204, 462.72238058830936);
-             outPath->cubicTo(181.06351776173827, 505.7852126710772, 239.5668339599056,
-                              529.999456521097, 300.49999999999994, 529.999456521097);
-             outPath->cubicTo(361.43316604009436, 529.999456521097, 419.93648223826176,
-                              505.78521267107726, 463.0461472413797, 462.7223805883093);
-             outPath->cubicTo(506.1558122444976, 419.65954850554135, 530.433632388891,
-                              361.1825867133324, 530.4998641302341, 300.2494566687815);
-             outPath->cubicTo(530.5660958715771, 239.31632662423056, 506.4154569244844,
-                              180.7867251403819, 463.3995086397787, 137.6302781499583);
-             outPath->cubicTo(420.383560355073, 94.47383115953468, 361.93302205796255,
-                              70.13246340443492, 300.9999999999996, 70.00000000000003);
+             outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction, 301.0, 70.0);
              outPath->close();
              outPath->moveTo(300.0, 70.0);
          }},
diff --git a/libs/hwui/utils/VectorDrawableUtils.cpp b/libs/hwui/utils/VectorDrawableUtils.cpp
index 1931d64..6b8f315 100644
--- a/libs/hwui/utils/VectorDrawableUtils.cpp
+++ b/libs/hwui/utils/VectorDrawableUtils.cpp
@@ -96,132 +96,6 @@
     }
 }
 
-/**
- * Converts an arc to cubic Bezier segments and records them in p.
- *
- * @param p The target for the cubic Bezier segments
- * @param cx The x coordinate center of the ellipse
- * @param cy The y coordinate center of the ellipse
- * @param a The radius of the ellipse in the horizontal direction
- * @param b The radius of the ellipse in the vertical direction
- * @param e1x E(eta1) x coordinate of the starting point of the arc
- * @param e1y E(eta2) y coordinate of the starting point of the arc
- * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
- * @param start The start angle of the arc on the ellipse
- * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
- */
-static void arcToBezier(SkPath* p, double cx, double cy, double a, double b, double e1x, double e1y,
-                        double theta, double start, double sweep) {
-    // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
-    // and http://www.spaceroots.org/documents/ellipse/node22.html
-
-    // Maximum of 45 degrees per cubic Bezier segment
-    int numSegments = ceil(fabs(sweep * 4 / M_PI));
-
-    double eta1 = start;
-    double cosTheta = cos(theta);
-    double sinTheta = sin(theta);
-    double cosEta1 = cos(eta1);
-    double sinEta1 = sin(eta1);
-    double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
-    double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
-
-    double anglePerSegment = sweep / numSegments;
-    for (int i = 0; i < numSegments; i++) {
-        double eta2 = eta1 + anglePerSegment;
-        double sinEta2 = sin(eta2);
-        double cosEta2 = cos(eta2);
-        double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
-        double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
-        double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
-        double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
-        double tanDiff2 = tan((eta2 - eta1) / 2);
-        double alpha = sin(eta2 - eta1) * (sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
-        double q1x = e1x + alpha * ep1x;
-        double q1y = e1y + alpha * ep1y;
-        double q2x = e2x - alpha * ep2x;
-        double q2y = e2y - alpha * ep2y;
-
-        p->cubicTo((float)q1x, (float)q1y, (float)q2x, (float)q2y, (float)e2x, (float)e2y);
-        eta1 = eta2;
-        e1x = e2x;
-        e1y = e2y;
-        ep1x = ep2x;
-        ep1y = ep2y;
-    }
-}
-
-inline double toRadians(float theta) {
-    return theta * M_PI / 180;
-}
-
-static void drawArc(SkPath* p, float x0, float y0, float x1, float y1, float a, float b,
-                    float theta, bool isMoreThanHalf, bool isPositiveArc) {
-    /* Convert rotation angle from degrees to radians */
-    double thetaD = toRadians(theta);
-    /* Pre-compute rotation matrix entries */
-    double cosTheta = cos(thetaD);
-    double sinTheta = sin(thetaD);
-    /* Transform (x0, y0) and (x1, y1) into unit space */
-    /* using (inverse) rotation, followed by (inverse) scale */
-    double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
-    double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
-    double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
-    double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
-
-    /* Compute differences and averages */
-    double dx = x0p - x1p;
-    double dy = y0p - y1p;
-    double xm = (x0p + x1p) / 2;
-    double ym = (y0p + y1p) / 2;
-    /* Solve for intersecting unit circles */
-    double dsq = dx * dx + dy * dy;
-    if (dsq == 0.0) {
-        VECTOR_DRAWABLE_LOGD("Points are coincident");
-        return; /* Points are coincident */
-    }
-    double disc = 1.0 / dsq - 1.0 / 4.0;
-    if (disc < 0.0) {
-        VECTOR_DRAWABLE_LOGD("Points are too far apart %f", dsq);
-        float adjust = (float)(sqrt(dsq) / 1.99999);
-        drawArc(p, x0, y0, x1, y1, a * adjust, b * adjust, theta, isMoreThanHalf, isPositiveArc);
-        return; /* Points are too far apart */
-    }
-    double s = sqrt(disc);
-    double sdx = s * dx;
-    double sdy = s * dy;
-    double cx;
-    double cy;
-    if (isMoreThanHalf == isPositiveArc) {
-        cx = xm - sdy;
-        cy = ym + sdx;
-    } else {
-        cx = xm + sdy;
-        cy = ym - sdx;
-    }
-
-    double eta0 = atan2((y0p - cy), (x0p - cx));
-
-    double eta1 = atan2((y1p - cy), (x1p - cx));
-
-    double sweep = (eta1 - eta0);
-    if (isPositiveArc != (sweep >= 0)) {
-        if (sweep > 0) {
-            sweep -= 2 * M_PI;
-        } else {
-            sweep += 2 * M_PI;
-        }
-    }
-
-    cx *= a;
-    cy *= b;
-    double tcx = cx;
-    cx = cx * cosTheta - cy * sinTheta;
-    cy = tcx * sinTheta + cy * cosTheta;
-
-    arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
-}
-
 // Use the given verb, and points in the range [start, end) to insert a command into the SkPath.
 void PathResolver::addCommand(SkPath* outPath, char previousCmd, char cmd,
                               const std::vector<float>* points, size_t start, size_t end) {
@@ -424,18 +298,20 @@
                 break;
             case 'a':  // Draws an elliptical arc
                 // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
-                drawArc(outPath, currentX, currentY, points->at(k + 5) + currentX,
-                        points->at(k + 6) + currentY, points->at(k + 0), points->at(k + 1),
-                        points->at(k + 2), points->at(k + 3) != 0, points->at(k + 4) != 0);
+                outPath->arcTo(points->at(k + 0), points->at(k + 1), points->at(k + 2),
+                               (SkPath::ArcSize) (points->at(k + 3) != 0),
+                               (SkPath::Direction) (points->at(k + 4) == 0), 
+                               points->at(k + 5) + currentX, points->at(k + 6) + currentY);
                 currentX += points->at(k + 5);
                 currentY += points->at(k + 6);
                 ctrlPointX = currentX;
                 ctrlPointY = currentY;
                 break;
             case 'A':  // Draws an elliptical arc
-                drawArc(outPath, currentX, currentY, points->at(k + 5), points->at(k + 6),
-                        points->at(k + 0), points->at(k + 1), points->at(k + 2),
-                        points->at(k + 3) != 0, points->at(k + 4) != 0);
+                outPath->arcTo(points->at(k + 0), points->at(k + 1), points->at(k + 2),
+                               (SkPath::ArcSize) (points->at(k + 3) != 0),
+                               (SkPath::Direction) (points->at(k + 4) == 0), 
+                               points->at(k + 5), points->at(k + 6));
                 currentX = points->at(k + 5);
                 currentY = points->at(k + 6);
                 ctrlPointX = currentX;
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 968f596..d15ab33 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -184,6 +184,17 @@
     public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
 
     /**
+     * Broadcast intent action when {@link android.provider.Settings.Secure#LOCATION_MODE} is
+     * about to be changed through Settings app or Quick Settings.
+     * For use with the {@link android.provider.Settings.Secure#LOCATION_MODE} API.
+     * If you're interacting with {@link #isProviderEnabled(String)}, use
+     * {@link #PROVIDERS_CHANGED_ACTION} instead.
+     *
+     * @hide
+     */
+    public static final String MODE_CHANGING_ACTION = "com.android.settings.location.MODE_CHANGING";
+
+    /**
      * Broadcast intent action indicating that the GPS has either started or
      * stopped receiving GPS fixes. An intent extra provides this state as a
      * boolean, where {@code true} means that the GPS is actively receiving fixes.
diff --git a/location/tests/locationtests/Android.mk b/location/tests/locationtests/Android.mk
index 73b2bb5..44d290e 100644
--- a/location/tests/locationtests/Android.mk
+++ b/location/tests/locationtests/Android.mk
@@ -7,7 +7,7 @@
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 LOCAL_PACKAGE_NAME := FrameworksLocationTests
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
diff --git a/lowpan/tests/Android.mk b/lowpan/tests/Android.mk
index bb0a944..99499dc 100644
--- a/lowpan/tests/Android.mk
+++ b/lowpan/tests/Android.mk
@@ -56,6 +56,7 @@
 
 LOCAL_JAVA_LIBRARIES := \
 	android.test.runner \
+	android.test.base \
 
 LOCAL_PACKAGE_NAME := FrameworksLowpanApiTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index aaf18e7..ba29d2d 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -423,6 +423,12 @@
         }
     }
 
+    private void doScanDirectory(String path) {
+        String[] scanPath;
+        scanPath = new String[] { path };
+        mMediaScanner.scanDirectories(scanPath);
+    }
+
     private Cursor createObjectQuery(int storageID, int format, int parent) throws RemoteException {
         String where;
         String[] whereArgs;
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index fe2a939..4e8c72b 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -55,6 +55,7 @@
 
 static jmethodID method_beginSendObject;
 static jmethodID method_endSendObject;
+static jmethodID method_doScanDirectory;
 static jmethodID method_getObjectList;
 static jmethodID method_getNumObjects;
 static jmethodID method_getSupportedPlaybackFormats;
@@ -119,6 +120,8 @@
                                             MtpObjectFormat format,
                                             bool succeeded);
 
+    virtual void                    doScanDirectory(const char* path);
+
     virtual MtpObjectHandleList*    getObjectList(MtpStorageID storageID,
                                     MtpObjectFormat format,
                                     MtpObjectHandle parent);
@@ -265,6 +268,16 @@
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
 }
 
+void MyMtpDatabase::doScanDirectory(const char* path) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jstring pathStr = env->NewStringUTF(path);
+    env->CallVoidMethod(mDatabase, method_doScanDirectory, pathStr);
+
+    if (pathStr)
+        env->DeleteLocalRef(pathStr);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
 MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID,
                                                   MtpObjectFormat format,
                                                   MtpObjectHandle parent) {
@@ -1311,6 +1324,11 @@
         ALOGE("Can't find endSendObject");
         return -1;
     }
+    method_doScanDirectory = env->GetMethodID(clazz, "doScanDirectory", "(Ljava/lang/String;)V");
+    if (method_doScanDirectory == NULL) {
+        ALOGE("Can't find doScanDirectory");
+        return -1;
+    }
     method_getObjectList = env->GetMethodID(clazz, "getObjectList", "(III)[I");
     if (method_getObjectList == NULL) {
         ALOGE("Can't find getObjectList");
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index ab0da07..21410ea 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -24,7 +24,6 @@
 #define USE_SHARED_MEM_BUFFER
 
 #include <media/AudioTrack.h>
-#include <media/IMediaHTTPService.h>
 #include <media/mediaplayer.h>
 #include "SoundPool.h"
 #include "SoundPoolThread.h"
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 4a771eb..8b01aef 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -138,6 +138,7 @@
             ret.packageName = pkg.packageName;
             ret.splitNames = pkg.splitNames;
             ret.versionCode = pkg.versionCode;
+            ret.versionCodeMajor = pkg.versionCodeMajor;
             ret.baseRevisionCode = pkg.baseRevisionCode;
             ret.splitRevisionCodes = pkg.splitRevisionCodes;
             ret.installLocation = pkg.installLocation;
diff --git a/packages/EasterEgg/Android.mk b/packages/EasterEgg/Android.mk
index d4c1e70..a825581 100644
--- a/packages/EasterEgg/Android.mk
+++ b/packages/EasterEgg/Android.mk
@@ -2,17 +2,23 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := optional
+
 LOCAL_STATIC_JAVA_LIBRARIES := \
+    jsr305
+
+LOCAL_STATIC_ANDROID_LIBRARIES := \
     android-support-v4 \
     android-support-v13 \
     android-support-dynamic-animation \
     android-support-v7-recyclerview \
     android-support-v7-preference \
     android-support-v7-appcompat \
-    android-support-v14-preference \
-    jsr305
+    android-support-v14-preference
+
+LOCAL_USE_AAPT2 := true
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
 LOCAL_PACKAGE_NAME := EasterEgg
 LOCAL_CERTIFICATE := platform
diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index 33b9ae3..f2409d4 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -25,7 +25,7 @@
     <string name="keyboard_layout_swedish" msgid="732959109088479351">"Swedia"</string>
     <string name="keyboard_layout_finnish" msgid="5585659438924315466">"Finlandia"</string>
     <string name="keyboard_layout_croatian" msgid="4172229471079281138">"Kroasia"</string>
-    <string name="keyboard_layout_czech" msgid="1349256901452975343">"Cheska"</string>
+    <string name="keyboard_layout_czech" msgid="1349256901452975343">"Ceko"</string>
     <string name="keyboard_layout_estonian" msgid="8775830985185665274">"Estonia"</string>
     <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"Hungaria"</string>
     <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"Islandia"</string>
diff --git a/packages/Osu2/tests/Android.mk b/packages/Osu2/tests/Android.mk
index 4b6e0e6..afc743d 100644
--- a/packages/Osu2/tests/Android.mk
+++ b/packages/Osu2/tests/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 
 LOCAL_JACK_FLAGS := --multi-dex native
 
diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk
index 49c5467..9d1c4ca 100644
--- a/packages/SettingsLib/common.mk
+++ b/packages/SettingsLib/common.mk
@@ -21,6 +21,10 @@
 LOCAL_STATIC_ANDROID_LIBRARIES += \
     android-support-v4 \
     apptoolkit-lifecycle-runtime \
+    android-support-v7-recyclerview \
+    android-support-v7-preference \
+    android-support-v7-appcompat \
+    android-support-v14-preference \
     SettingsLib
 else
 LOCAL_RESOURCE_DIR += $(call my-dir)/res
diff --git a/packages/SettingsLib/res/layout/preference_dropdown_material_settings.xml b/packages/SettingsLib/res/layout/preference_dropdown_material_settings.xml
new file mode 100644
index 0000000..a0b8155
--- /dev/null
+++ b/packages/SettingsLib/res/layout/preference_dropdown_material_settings.xml
@@ -0,0 +1,35 @@
+<?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.
+  -->
+
+
+<!-- Based off frameworks/base/core/res/res/layout/preference_dropdown_material.xml
+     except that icon space in this layout is always reserved -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <Spinner
+        android:id="@+id/spinner"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/preference_no_icon_padding_start"
+        android:visibility="invisible" />
+
+    <include layout="@layout/preference_material"/>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index dd420b3..ed0958c 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -225,7 +225,7 @@
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Pokaži opcije za certifikaciju Bežičnog prikaza"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Povećajte nivo Wi-Fi zapisivanja, pokazati po SSID RSSI Wi-Fi Picker"</string>
     <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Kada je omogućeno, Wi-Fi veza će u slučaju slabog signala agresivnije predavati vezu za prijenos podataka na mobilnu vezu"</string>
-    <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Dozvoli/Zabrani Wi-Fi lutajuće skeniranje na osnovu količine podatkovnog prometa prisutnog na sučelju"</string>
+    <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Dozvoli/Zabrani Wi-Fi lutajuće skeniranje na osnovu količine podatkovnog prometa prisutnog na interfejsu"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Veličine bafera za zapisnik"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Odaberite veličine za Logger prema međumemoriji evidencije"</string>
     <string name="dev_logpersist_clear_warning_title" msgid="684806692440237967">"Želite li izbrisati trajnu pohranu zapisivača?"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index aeb0ddb..bb0a337 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -97,7 +97,7 @@
     <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"कनेक्‍ट केल्यावर पेअरींग तुमचे संपर्क आणि कॉल इतिहास यामध्ये अॅक्सेस देते."</string>
     <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> शी जोडू शकलो नाही."</string>
     <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"अयोग्य पिन किंवा पासकीमुळे <xliff:g id="DEVICE_NAME">%1$s</xliff:g> सह जोडू शकलो नाही."</string>
-    <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> शी संप्रेषण करू शकत नाही."</string>
+    <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> शी संवाद प्रस्थापित करू शकत नाही."</string>
     <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> द्वारे पेअरींग नाकारले."</string>
     <string name="bluetooth_talkback_computer" msgid="4875089335641234463">"संगणक"</string>
     <string name="bluetooth_talkback_headset" msgid="5140152177885220949">"हेडसेट"</string>
diff --git a/packages/SettingsLib/res/values/styles_support_preference.xml b/packages/SettingsLib/res/values/styles_support_preference.xml
index d7032b8..cf9f3c6 100644
--- a/packages/SettingsLib/res/values/styles_support_preference.xml
+++ b/packages/SettingsLib/res/values/styles_support_preference.xml
@@ -20,14 +20,73 @@
 
     <dimen name="preference_no_icon_padding_start">72dp</dimen>
 
+    <!-- Fragment style -->
+    <style name="PreferenceFragmentStyle.SettingsBase" parent="@*android:style/PreferenceFragment.Material">
+        <item name="allowDividerAfterLastItem">false</item>
+    </style>
+
+    <!-- Preferences -->
+    <style name="Preference.SettingsBase" parent="@style/Preference.Material">
+        <item name="allowDividerAbove">false</item>
+        <item name="allowDividerBelow">true</item>
+        <item name="singleLineTitle">false</item>
+        <item name="iconSpaceReserved">true</item>
+    </style>
+
+    <!-- Preference category -->
+    <style name="Preference.Category.SettingsBase" parent="@style/Preference.Category.Material">
+        <item name="allowDividerAbove">true</item>
+        <item name="allowDividerBelow">true</item>
+        <item name="android:layout">@layout/preference_category_material_settings</item>
+    </style>
+
+    <!-- Preference screen -->
+    <style name="Preference.Screen.SettingsBase" parent="@style/Preference.PreferenceScreen.Material">
+        <item name="allowDividerAbove">false</item>
+        <item name="allowDividerBelow">true</item>
+        <item name="iconSpaceReserved">true</item>
+    </style>
+
     <!-- Footer Preferences -->
-    <style name="Preference.FooterPreference.SettingsBase" parent="@style/Preference.Material">
+    <style name="Preference.FooterPreference.SettingsBase" parent="Preference.SettingsBase">
         <item name="android:layout">@layout/preference_footer</item>
         <item name="allowDividerAbove">true</item>
     </style>
 
+    <!-- Dropdown Preferences -->
+    <style name="Preference.DropdownPreference.SettingsBase" parent="Preference.SettingsBase">
+        <item name="android:layout">@layout/preference_dropdown_material_settings</item>
+    </style>
+
+    <!-- Switch Preferences -->
+    <style name="Preference.SwitchPreference.SettingsBase" parent="@style/Preference.SwitchPreference.Material">
+        <item name="allowDividerAbove">false</item>
+        <item name="allowDividerBelow">true</item>
+        <item name="iconSpaceReserved">true</item>
+        <item name="singleLineTitle">false</item>
+    </style>
+
+    <!-- EditText Preferences -->
+    <style name="Preference.EditTextPreference.SettingsBase"
+           parent="@style/Preference.DialogPreference.EditTextPreference.Material">
+        <item name="allowDividerAbove">false</item>
+        <item name="allowDividerBelow">true</item>
+        <item name="iconSpaceReserved">true</item>
+        <item name="singleLineTitle">false</item>
+    </style>
+
     <style name="PreferenceThemeOverlay.SettingsBase" parent="@style/PreferenceThemeOverlay.v14.Material">
+        <!-- Parent path frameworks/support/v14/preference/res/values/themes.xml -->
+        <item name="android:scrollbars">vertical</item>
+        <item name="preferenceFragmentStyle">@style/PreferenceFragmentStyle.SettingsBase</item>
+        <item name="preferenceCategoryStyle">@style/Preference.Category.SettingsBase</item>
+        <item name="preferenceScreenStyle">@style/Preference.Screen.SettingsBase</item>
+        <item name="preferenceStyle">@style/Preference.SettingsBase</item>
+        <item name="dialogPreferenceStyle">@style/Preference.SettingsBase</item>
+        <item name="editTextPreferenceStyle">@style/Preference.EditTextPreference.SettingsBase</item>
         <item name="footerPreferenceStyle">@style/Preference.FooterPreference.SettingsBase</item>
+        <item name="switchPreferenceStyle">@style/Preference.SwitchPreference.SettingsBase</item>
+        <item name="dropdownPreferenceStyle">@style/Preference.DropdownPreference.SettingsBase</item>
     </style>
 
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
index 2c26410..8055caa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
@@ -227,7 +227,7 @@
                 // cache the version code
                 PackageInfo info = context.getPackageManager().getPackageInfo(
                         context.getPackageName(), 0);
-                sCachedVersionCode = Integer.toString(info.versionCode);
+                sCachedVersionCode = Long.toString(info.getLongVersionCode());
 
                 // append the version code to the uri
                 builder.appendQueryParameter(PARAM_VERSION, sCachedVersionCode);
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index eb33842..3c46d99 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -14,8 +14,10 @@
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
+import android.location.LocationManager;
 import android.net.ConnectivityManager;
 import android.os.BatteryManager;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.print.PrintManager;
 import android.provider.Settings;
@@ -26,6 +28,10 @@
 import java.text.NumberFormat;
 
 public class Utils {
+
+    private static final String CURRENT_MODE_KEY = "CURRENT_MODE";
+    private static final String NEW_MODE_KEY = "NEW_MODE";
+
     private static Signature[] sSystemSignature;
     private static String sPermissionControllerPackageName;
     private static String sServicesSystemSharedLibPackageName;
@@ -39,6 +45,16 @@
         com.android.internal.R.drawable.ic_wifi_signal_4
     };
 
+    public static boolean updateLocationMode(Context context, int oldMode, int newMode, int userId) {
+        Intent intent = new Intent(LocationManager.MODE_CHANGING_ACTION);
+        intent.putExtra(CURRENT_MODE_KEY, oldMode);
+        intent.putExtra(NEW_MODE_KEY, newMode);
+        context.sendBroadcastAsUser(
+                intent, UserHandle.of(userId), android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        return Settings.Secure.putIntForUser(
+                context.getContentResolver(), Settings.Secure.LOCATION_MODE, newMode, userId);
+    }
+
     /**
      * Return string resource that best describes combination of tethering
      * options available on this device.
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 5b39ee4..c3ff617 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -118,7 +118,7 @@
     public synchronized void clearNonBondedDevices() {
         for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
             CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
-            if (cachedDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
+            if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE) {
                 mCachedDevices.remove(i);
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
index a966e82..3a03644 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
@@ -75,23 +75,27 @@
      * Note: the returned list serves as a read-only list. If tiles needs to be added or removed
      * from the actual tiles list, it should be done through {@link #addTile}, {@link #removeTile}.
      */
-    public List<Tile> getTiles() {
-        return Collections.unmodifiableList(mTiles);
+    public synchronized List<Tile> getTiles() {
+        final List<Tile> result = new ArrayList<>(mTiles.size());
+        for (Tile tile : mTiles) {
+            result.add(tile);
+        }
+        return result;
     }
 
-    public void addTile(Tile tile) {
+    public synchronized void addTile(Tile tile) {
         mTiles.add(tile);
     }
 
-    public void addTile(int n, Tile tile) {
+    public synchronized void addTile(int n, Tile tile) {
         mTiles.add(n, tile);
     }
 
-    public void removeTile(Tile tile) {
+    public synchronized void removeTile(Tile tile) {
         mTiles.remove(tile);
     }
 
-    public void removeTile(int n) {
+    public synchronized void removeTile(int n) {
         mTiles.remove(n);
     }
 
@@ -103,7 +107,7 @@
         return mTiles.get(n);
     }
 
-    public boolean containsComponent(ComponentName component) {
+    public synchronized boolean containsComponent(ComponentName component) {
         for (Tile tile : mTiles) {
             if (TextUtils.equals(tile.intent.getComponent().getClassName(),
                     component.getClassName())) {
@@ -129,7 +133,7 @@
     /**
      * Sort priority value and package name for tiles in this category.
      */
-    public void sortTiles(String skipPackageName) {
+    public synchronized void sortTiles(String skipPackageName) {
         // Sort mTiles based on [priority, package within priority]
         Collections.sort(mTiles, (tile1, tile2) -> {
             final String package1 = tile1.intent.getComponent().getPackageName();
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
new file mode 100644
index 0000000..e3e27ce1
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java
@@ -0,0 +1,292 @@
+/*
+ * 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.settingslib.license;
+
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * The utility class that generate a license html file from xml files.
+ * All the HTML snippets and logic are copied from build/make/tools/generate-notice-files.py.
+ *
+ * TODO: Remove duplicate codes once backward support ends.
+ */
+class LicenseHtmlGeneratorFromXml {
+    private static final String TAG = "LicenseHtmlGeneratorFromXml";
+
+    private static final String TAG_ROOT = "licenses";
+    private static final String TAG_FILE_NAME = "file-name";
+    private static final String TAG_FILE_CONTENT = "file-content";
+    private static final String ATTR_CONTENT_ID = "contentId";
+
+    private static final String HTML_HEAD_STRING =
+            "<html><head>\n"
+            + "<style type=\"text/css\">\n"
+            + "body { padding: 0; font-family: sans-serif; }\n"
+            + ".same-license { background-color: #eeeeee;\n"
+            + "                border-top: 20px solid white;\n"
+            + "                padding: 10px; }\n"
+            + ".label { font-weight: bold; }\n"
+            + ".file-list { margin-left: 1em; color: blue; }\n"
+            + "</style>\n"
+            + "</head>"
+            + "<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n"
+            + "<div class=\"toc\">\n"
+            + "<ul>";
+
+    private static final String HTML_MIDDLE_STRING =
+            "</ul>\n"
+            + "</div><!-- table of contents -->\n"
+            + "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">";
+
+    private static final String HTML_REAR_STRING =
+            "</table></body></html>";
+
+    private final List<File> mXmlFiles;
+
+    /*
+     * A map from a file name to a content id (MD5 sum of file content) for its license.
+     * For example, "/system/priv-app/TeleService/TeleService.apk" maps to
+     * "9645f39e9db895a4aa6e02cb57294595". Here "9645f39e9db895a4aa6e02cb57294595" is a MD5 sum
+     * of the content of packages/services/Telephony/MODULE_LICENSE_APACHE2.
+     */
+    private final Map<String, String> mFileNameToContentIdMap = new HashMap();
+
+    /*
+     * A map from a content id (MD5 sum of file content) to a license file content.
+     * For example, "9645f39e9db895a4aa6e02cb57294595" maps to the content string of
+     * packages/services/Telephony/MODULE_LICENSE_APACHE2. Here "9645f39e9db895a4aa6e02cb57294595"
+     * is a MD5 sum of the file content.
+     */
+    private final Map<String, String> mContentIdToFileContentMap = new HashMap();
+
+    static class ContentIdAndFileNames {
+        final String mContentId;
+        final List<String> mFileNameList = new ArrayList();
+
+        ContentIdAndFileNames(String contentId) {
+            mContentId = contentId;
+        }
+    }
+
+    private LicenseHtmlGeneratorFromXml(List<File> xmlFiles) {
+        mXmlFiles = xmlFiles;
+    }
+
+    public static boolean generateHtml(List<File> xmlFiles, File outputFile) {
+        LicenseHtmlGeneratorFromXml genertor = new LicenseHtmlGeneratorFromXml(xmlFiles);
+        return genertor.generateHtml(outputFile);
+    }
+
+    private boolean generateHtml(File outputFile) {
+        for (File xmlFile : mXmlFiles) {
+            parse(xmlFile);
+        }
+
+        if (mFileNameToContentIdMap.isEmpty() || mContentIdToFileContentMap.isEmpty()) {
+            return false;
+        }
+
+        PrintWriter writer = null;
+        try {
+            writer = new PrintWriter(outputFile);
+
+            generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer);
+
+            writer.flush();
+            writer.close();
+            return true;
+        } catch (FileNotFoundException | SecurityException e) {
+            Log.e(TAG, "Failed to generate " + outputFile, e);
+
+            if (writer != null) {
+                writer.close();
+            }
+            return false;
+        }
+    }
+
+    private void parse(File xmlFile) {
+        if (xmlFile == null || !xmlFile.exists() || xmlFile.length() == 0) {
+            return;
+        }
+
+        InputStreamReader in = null;
+        try {
+            if (xmlFile.getName().endsWith(".gz")) {
+                in = new InputStreamReader(new GZIPInputStream(new FileInputStream(xmlFile)));
+            } else {
+                in = new FileReader(xmlFile);
+            }
+
+            parse(in, mFileNameToContentIdMap, mContentIdToFileContentMap);
+
+            in.close();
+        } catch (XmlPullParserException | IOException e) {
+            Log.e(TAG, "Failed to parse " + xmlFile, e);
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException ie) {
+                    Log.w(TAG, "Failed to close " + xmlFile);
+                }
+            }
+        }
+    }
+
+    /*
+     * Parses an input stream and fills a map from a file name to a content id for its license
+     * and a map from a content id to a license file content.
+     *
+     * Following xml format is expected from the input stream.
+     *
+     *     <licenses>
+     *     <file-name contentId="content_id_of_license1">file1</file-name>
+     *     <file-name contentId="content_id_of_license2">file2</file-name>
+     *     ...
+     *     <file-content contentId="content_id_of_license1">license1 file contents</file-content>
+     *     <file-content contentId="content_id_of_license2">license2 file contents</file-content>
+     *     ...
+     *     </licenses>
+     */
+    @VisibleForTesting
+    static void parse(InputStreamReader in, Map<String, String> outFileNameToContentIdMap,
+            Map<String, String> outContentIdToFileContentMap)
+                    throws XmlPullParserException, IOException {
+        Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
+        Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(in);
+        parser.nextTag();
+
+        parser.require(XmlPullParser.START_TAG, "", TAG_ROOT);
+
+        int state = parser.getEventType();
+        while (state != XmlPullParser.END_DOCUMENT) {
+            if (state == XmlPullParser.START_TAG) {
+                if (TAG_FILE_NAME.equals(parser.getName())) {
+                    String contentId = parser.getAttributeValue("", ATTR_CONTENT_ID);
+                    if (!TextUtils.isEmpty(contentId)) {
+                        String fileName = readText(parser).trim();
+                        if (!TextUtils.isEmpty(fileName)) {
+                            fileNameToContentIdMap.put(fileName, contentId);
+                        }
+                    }
+                } else if (TAG_FILE_CONTENT.equals(parser.getName())) {
+                    String contentId = parser.getAttributeValue("", ATTR_CONTENT_ID);
+                    if (!TextUtils.isEmpty(contentId)
+                            && !outContentIdToFileContentMap.containsKey(contentId)
+                            && !contentIdToFileContentMap.containsKey(contentId)) {
+                        String fileContent = readText(parser);
+                        if (!TextUtils.isEmpty(fileContent)) {
+                            contentIdToFileContentMap.put(contentId, fileContent);
+                        }
+                    }
+                }
+            }
+
+            state = parser.next();
+        }
+        outFileNameToContentIdMap.putAll(fileNameToContentIdMap);
+        outContentIdToFileContentMap.putAll(contentIdToFileContentMap);
+    }
+
+    private static String readText(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        StringBuffer result = new StringBuffer();
+        int state = parser.next();
+        while (state == XmlPullParser.TEXT) {
+            result.append(parser.getText());
+            state = parser.next();
+        }
+        return result.toString();
+    }
+
+    @VisibleForTesting
+    static void generateHtml(Map<String, String> fileNameToContentIdMap,
+            Map<String, String> contentIdToFileContentMap, PrintWriter writer) {
+        List<String> fileNameList = new ArrayList();
+        fileNameList.addAll(fileNameToContentIdMap.keySet());
+        Collections.sort(fileNameList);
+
+        writer.println(HTML_HEAD_STRING);
+
+        int count = 0;
+        Map<String, Integer> contentIdToOrderMap = new HashMap();
+        List<ContentIdAndFileNames> contentIdAndFileNamesList = new ArrayList();
+
+        // Prints all the file list with a link to its license file content.
+        for (String fileName : fileNameList) {
+            String contentId = fileNameToContentIdMap.get(fileName);
+            // Assigns an id to a newly referred license file content.
+            if (!contentIdToOrderMap.containsKey(contentId)) {
+                contentIdToOrderMap.put(contentId, count);
+
+                // An index in contentIdAndFileNamesList is the order of each element.
+                contentIdAndFileNamesList.add(new ContentIdAndFileNames(contentId));
+                count++;
+            }
+
+            int id = contentIdToOrderMap.get(contentId);
+            contentIdAndFileNamesList.get(id).mFileNameList.add(fileName);
+            writer.format("<li><a href=\"#id%d\">%s</a></li>\n", id, fileName);
+        }
+
+        writer.println(HTML_MIDDLE_STRING);
+
+        count = 0;
+        // Prints all contents of the license files in order of id.
+        for (ContentIdAndFileNames contentIdAndFileNames : contentIdAndFileNamesList) {
+            writer.format("<tr id=\"id%d\"><td class=\"same-license\">\n", count);
+            writer.println("<div class=\"label\">Notices for file(s):</div>");
+            writer.println("<div class=\"file-list\">");
+            for (String fileName : contentIdAndFileNames.mFileNameList) {
+                writer.format("%s <br/>\n", fileName);
+            }
+            writer.println("</div><!-- file-list -->");
+            writer.println("<pre class=\"license-text\">");
+            writer.println(contentIdToFileContentMap.get(
+                    contentIdAndFileNames.mContentId));
+            writer.println("</pre><!-- license-text -->");
+            writer.println("</td></tr><!-- same-license -->");
+
+            count++;
+        }
+
+        writer.println(HTML_REAR_STRING);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
new file mode 100644
index 0000000..a9fb20c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java
@@ -0,0 +1,110 @@
+/*
+ * 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.settingslib.license;
+
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import com.android.settingslib.utils.AsyncLoader;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * LicenseHtmlLoader is a loader which loads a license html file from default license xml files.
+ */
+public class LicenseHtmlLoader extends AsyncLoader<File> {
+    private static final String TAG = "LicenseHtmlLoader";
+
+    private static final String[] DEFAULT_LICENSE_XML_PATHS = {
+            "/system/etc/NOTICE.xml.gz",
+            "/vendor/etc/NOTICE.xml.gz",
+            "/odm/etc/NOTICE.xml.gz",
+            "/oem/etc/NOTICE.xml.gz"};
+    private static final String NOTICE_HTML_FILE_NAME = "NOTICE.html";
+
+    private Context mContext;
+
+    public LicenseHtmlLoader(Context context) {
+        super(context);
+        mContext = context;
+    }
+
+    @Override
+    public File loadInBackground() {
+        return generateHtmlFromDefaultXmlFiles();
+    }
+
+    @Override
+    protected void onDiscardResult(File f) {
+    }
+
+    private File generateHtmlFromDefaultXmlFiles() {
+        final List<File> xmlFiles = getVaildXmlFiles();
+        if (xmlFiles.isEmpty()) {
+            Log.e(TAG, "No notice file exists.");
+            return null;
+        }
+
+        File cachedHtmlFile = getCachedHtmlFile();
+        if (!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile)
+                || generateHtmlFile(xmlFiles, cachedHtmlFile)) {
+            return cachedHtmlFile;
+        }
+
+        return null;
+    }
+
+    @VisibleForTesting
+    List<File> getVaildXmlFiles() {
+        final List<File> xmlFiles = new ArrayList();
+        for (final String xmlPath : DEFAULT_LICENSE_XML_PATHS) {
+            File file = new File(xmlPath);
+            if (file.exists() && file.length() != 0) {
+                xmlFiles.add(file);
+            }
+        }
+        return xmlFiles;
+    }
+
+    @VisibleForTesting
+    File getCachedHtmlFile() {
+        return new File(mContext.getCacheDir(), NOTICE_HTML_FILE_NAME);
+    }
+
+    @VisibleForTesting
+    boolean isCachedHtmlFileOutdated(List<File> xmlFiles, File cachedHtmlFile) {
+        boolean outdated = true;
+        if (cachedHtmlFile.exists() && cachedHtmlFile.length() != 0) {
+            outdated = false;
+            for (File file : xmlFiles) {
+                if (cachedHtmlFile.lastModified() < file.lastModified()) {
+                    outdated = true;
+                    break;
+                }
+            }
+        }
+        return outdated;
+    }
+
+    @VisibleForTesting
+    boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) {
+        return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoader.java b/packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoader.java
new file mode 100644
index 0000000..06770ac
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/AsyncLoader.java
@@ -0,0 +1,110 @@
+/*
+ * 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.settingslib.utils;
+
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+
+/**
+ * This class fills in some boilerplate for AsyncTaskLoader to actually load things.
+ *
+ * Subclasses need to implement {@link AsyncLoader#loadInBackground()} to perform the actual
+ * background task, and {@link AsyncLoader#onDiscardResult(T)} to clean up previously loaded
+ * results.
+ *
+ * This loader is based on the MailAsyncTaskLoader from the AOSP EmailUnified repo.
+ *
+ * @param <T> the data type to be loaded.
+ */
+public abstract class AsyncLoader<T> extends AsyncTaskLoader<T> {
+    private T mResult;
+
+    public AsyncLoader(final Context context) {
+        super(context);
+    }
+
+    @Override
+    protected void onStartLoading() {
+        if (mResult != null) {
+            deliverResult(mResult);
+        }
+
+        if (takeContentChanged() || mResult == null) {
+            forceLoad();
+        }
+    }
+
+    @Override
+    protected void onStopLoading() {
+        cancelLoad();
+    }
+
+    @Override
+    public void deliverResult(final T data) {
+        if (isReset()) {
+            if (data != null) {
+                onDiscardResult(data);
+            }
+            return;
+        }
+
+        final T oldResult = mResult;
+        mResult = data;
+
+        if (isStarted()) {
+            super.deliverResult(data);
+        }
+
+        if (oldResult != null && oldResult != mResult) {
+            onDiscardResult(oldResult);
+        }
+    }
+
+    @Override
+    protected void onReset() {
+        super.onReset();
+
+        onStopLoading();
+
+        if (mResult != null) {
+            onDiscardResult(mResult);
+        }
+        mResult = null;
+    }
+
+    @Override
+    public void onCanceled(final T data) {
+        super.onCanceled(data);
+
+        if (data != null) {
+            onDiscardResult(data);
+        }
+    }
+
+    /**
+     * Called when discarding the load results so subclasses can take care of clean-up or
+     * recycling tasks. This is not called if the same result (by way of pointer equality) is
+     * returned again by a subsequent call to loadInBackground, or if result is null.
+     *
+     * Note that this may be called concurrently with loadInBackground(), and in some circumstances
+     * may be called more than once for a given object.
+     *
+     * @param result The value returned from {@link AsyncLoader#loadInBackground()} which
+     *               is to be discarded.
+     */
+    protected abstract void onDiscardResult(T result);
+}
diff --git a/packages/SettingsLib/tests/integ/Android.mk b/packages/SettingsLib/tests/integ/Android.mk
index 7ace048..3c8c8ac 100644
--- a/packages/SettingsLib/tests/integ/Android.mk
+++ b/packages/SettingsLib/tests/integ/Android.mk
@@ -27,6 +27,8 @@
 LOCAL_PACKAGE_NAME := SettingsLibTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
 
+LOCAL_USE_AAPT2 := true
+
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
     espresso-core \
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
index 820231e..85b04c8 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
@@ -159,14 +159,14 @@
         for (String pkg : defaultImes) {
             final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
             final InputMethodInfo inputMethodInfo = new InputMethodInfo(
-                    ri, false, null, null, 0, true, true);
+                    ri, false, null, null, 0, true, true, false);
             inputMethods.add(inputMethodInfo);
             addInstalledApp(ri);
         }
         for (String pkg : otherImes) {
             final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
             final InputMethodInfo inputMethodInfo = new InputMethodInfo(
-                    ri, false, null, null, 0, false, true);
+                    ri, false, null, null, 0, false, true, false);
             inputMethods.add(inputMethodInfo);
             addInstalledApp(ri);
         }
diff --git a/packages/SettingsLib/tests/robotests/Android.mk b/packages/SettingsLib/tests/robotests/Android.mk
index bc1a834..2738027 100644
--- a/packages/SettingsLib/tests/robotests/Android.mk
+++ b/packages/SettingsLib/tests/robotests/Android.mk
@@ -28,6 +28,8 @@
 LOCAL_RESOURCE_DIR := \
     $(LOCAL_PATH)/res
 
+LOCAL_USE_AAPT2 := true
+
 include frameworks/base/packages/SettingsLib/common.mk
 
 include $(BUILD_PACKAGE)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index c6cfdb8..976bbee 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -15,20 +15,45 @@
  */
 package com.android.settingslib;
 
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.location.LocationManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
+import android.text.TextUtils;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.ArgumentMatchers;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.res.Resources;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowSettings;
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(
+        manifest = TestConfig.MANIFEST_PATH,
+        sdk = TestConfig.SDK_VERSION,
+        shadows = {UtilsTest.ShadowSecure.class})
 public class UtilsTest {
     private static final double[] TEST_PERCENTAGES = {0, 0.4, 0.5, 0.6, 49, 49.3, 49.8, 50, 100};
     private static final String PERCENTAGE_0 = "0%";
@@ -37,6 +62,29 @@
     private static final String PERCENTAGE_50 = "50%";
     private static final String PERCENTAGE_100 = "100%";
 
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = spy(RuntimeEnvironment.application);
+        ShadowSecure.reset();
+    }
+
+    @Test
+    public void testUpdateLocationMode_sendBroadcast() {
+        int currentUserId = ActivityManager.getCurrentUser();
+        Utils.updateLocationMode(
+                mContext,
+                Secure.LOCATION_MODE_OFF,
+                Secure.LOCATION_MODE_HIGH_ACCURACY,
+                currentUserId);
+
+        verify(mContext).sendBroadcastAsUser(
+                argThat(actionMatches(LocationManager.MODE_CHANGING_ACTION)),
+                ArgumentMatchers.eq(UserHandle.of(currentUserId)),
+                ArgumentMatchers.eq(WRITE_SECURE_SETTINGS));
+    }
+
     @Test
     public void testFormatPercentage_RoundTrue_RoundUpIfPossible() {
         final String[] expectedPercentages = {PERCENTAGE_0, PERCENTAGE_0, PERCENTAGE_1,
@@ -74,4 +122,23 @@
                 .thenReturn(60);
         assertThat(Utils.getDefaultStorageManagerDaysToRetain(resources)).isEqualTo(60);
     }
+
+    private static ArgumentMatcher<Intent> actionMatches(String expected) {
+        return intent -> TextUtils.equals(expected, intent.getAction());
+    }
+
+    @Implements(value = Settings.Secure.class)
+    public static class ShadowSecure extends ShadowSettings.ShadowSecure {
+        private static Map<String, Integer> map = new HashMap<>();
+
+        @Implementation
+        public static boolean putIntForUser(ContentResolver cr, String name, int value, int userHandle) {
+            map.put(name, value);
+            return true;
+        }
+
+        public static void reset() {
+            map.clear();
+        }
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
new file mode 100644
index 0000000..c7e9262
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.settingslib.license;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class LicenseHtmlGeneratorFromXmlTest {
+    private static final String VALILD_XML_STRING =
+            "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+            + "<licenses>\n"
+            + "<file-name contentId=\"0\">/file0</file-name>\n"
+            + "<file-name contentId=\"0\">/file1</file-name>\n"
+            + "<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n"
+            + "</licenses>";
+
+    private static final String INVALILD_XML_STRING =
+            "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+            + "<licenses2>\n"
+            + "<file-name contentId=\"0\">/file0</file-name>\n"
+            + "<file-name contentId=\"0\">/file1</file-name>\n"
+            + "<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n"
+            + "</licenses2>";
+
+    private static final String EXPECTED_HTML_STRING =
+            "<html><head>\n"
+            + "<style type=\"text/css\">\n"
+            + "body { padding: 0; font-family: sans-serif; }\n"
+            + ".same-license { background-color: #eeeeee;\n"
+            + "                border-top: 20px solid white;\n"
+            + "                padding: 10px; }\n"
+            + ".label { font-weight: bold; }\n"
+            + ".file-list { margin-left: 1em; color: blue; }\n"
+            + "</style>\n"
+            + "</head>"
+            + "<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n"
+            + "<div class=\"toc\">\n"
+            + "<ul>\n"
+            + "<li><a href=\"#id0\">/file0</a></li>\n"
+            + "<li><a href=\"#id0\">/file1</a></li>\n"
+            + "</ul>\n"
+            + "</div><!-- table of contents -->\n"
+            + "<table cellpadding=\"0\" cellspacing=\"0\" border=\"0\">\n"
+            + "<tr id=\"id0\"><td class=\"same-license\">\n"
+            + "<div class=\"label\">Notices for file(s):</div>\n"
+            + "<div class=\"file-list\">\n"
+            + "/file0 <br/>\n"
+            + "/file1 <br/>\n"
+            + "</div><!-- file-list -->\n"
+            + "<pre class=\"license-text\">\n"
+            + "license content #0\n"
+            + "</pre><!-- license-text -->\n"
+            + "</td></tr><!-- same-license -->\n"
+            + "</table></body></html>\n";
+
+    @Test
+    public void testParseValidXmlStream() throws XmlPullParserException, IOException {
+        Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
+        Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+
+        LicenseHtmlGeneratorFromXml.parse(
+                new InputStreamReader(new ByteArrayInputStream(VALILD_XML_STRING.getBytes())),
+                fileNameToContentIdMap, contentIdToFileContentMap);
+        assertThat(fileNameToContentIdMap.size()).isEqualTo(2);
+        assertThat(fileNameToContentIdMap.get("/file0")).isEqualTo("0");
+        assertThat(fileNameToContentIdMap.get("/file1")).isEqualTo("0");
+        assertThat(contentIdToFileContentMap.size()).isEqualTo(1);
+        assertThat(contentIdToFileContentMap.get("0")).isEqualTo("license content #0");
+    }
+
+    @Test(expected = XmlPullParserException.class)
+    public void testParseInvalidXmlStream() throws XmlPullParserException, IOException {
+        Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
+        Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+
+        LicenseHtmlGeneratorFromXml.parse(
+                new InputStreamReader(new ByteArrayInputStream(INVALILD_XML_STRING.getBytes())),
+                fileNameToContentIdMap, contentIdToFileContentMap);
+    }
+
+    @Test
+    public void testGenerateHtml() {
+        Map<String, String> fileNameToContentIdMap = new HashMap<String, String>();
+        Map<String, String> contentIdToFileContentMap = new HashMap<String, String>();
+
+        fileNameToContentIdMap.put("/file0", "0");
+        fileNameToContentIdMap.put("/file1", "0");
+        contentIdToFileContentMap.put("0", "license content #0");
+
+        StringWriter output = new StringWriter();
+        LicenseHtmlGeneratorFromXml.generateHtml(
+                fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output));
+        assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderTest.java
new file mode 100644
index 0000000..1a6f30c
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.settingslib.license;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import java.io.File;
+import java.util.ArrayList;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class LicenseHtmlLoaderTest {
+    @Mock
+    private Context mContext;
+
+    LicenseHtmlLoader newLicenseHtmlLoader(ArrayList<File> xmlFiles,
+            File cachedHtmlFile, boolean isCachedHtmlFileOutdated,
+            boolean generateHtmlFileSucceeded) {
+        LicenseHtmlLoader loader = spy(new LicenseHtmlLoader(mContext));
+        doReturn(xmlFiles).when(loader).getVaildXmlFiles();
+        doReturn(cachedHtmlFile).when(loader).getCachedHtmlFile();
+        doReturn(isCachedHtmlFileOutdated).when(loader).isCachedHtmlFileOutdated(any(), any());
+        doReturn(generateHtmlFileSucceeded).when(loader).generateHtmlFile(any(), any());
+        return loader;
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testLoadInBackground() {
+        ArrayList<File> xmlFiles = new ArrayList();
+        xmlFiles.add(new File("test.xml"));
+        File cachedHtmlFile = new File("test.html");
+
+        LicenseHtmlLoader loader = newLicenseHtmlLoader(xmlFiles, cachedHtmlFile, true, true);
+
+        assertThat(loader.loadInBackground()).isEqualTo(cachedHtmlFile);
+        verify(loader).generateHtmlFile(any(), any());
+    }
+
+    @Test
+    public void testLoadInBackgroundWithNoVaildXmlFiles() {
+        ArrayList<File> xmlFiles = new ArrayList();
+        File cachedHtmlFile = new File("test.html");
+
+        LicenseHtmlLoader loader = newLicenseHtmlLoader(xmlFiles, cachedHtmlFile, true, true);
+
+        assertThat(loader.loadInBackground()).isNull();
+        verify(loader, never()).generateHtmlFile(any(), any());
+    }
+
+    @Test
+    public void testLoadInBackgroundWithNonOutdatedCachedHtmlFile() {
+        ArrayList<File> xmlFiles = new ArrayList();
+        xmlFiles.add(new File("test.xml"));
+        File cachedHtmlFile = new File("test.html");
+
+        LicenseHtmlLoader loader = newLicenseHtmlLoader(xmlFiles, cachedHtmlFile, false, true);
+
+        assertThat(loader.loadInBackground()).isEqualTo(cachedHtmlFile);
+        verify(loader, never()).generateHtmlFile(any(), any());
+    }
+
+    @Test
+    public void testLoadInBackgroundWithGenerateHtmlFileFailed() {
+        ArrayList<File> xmlFiles = new ArrayList();
+        xmlFiles.add(new File("test.xml"));
+        File cachedHtmlFile = new File("test.html");
+
+        LicenseHtmlLoader loader = newLicenseHtmlLoader(xmlFiles, cachedHtmlFile, true, false);
+
+        assertThat(loader.loadInBackground()).isNull();
+        verify(loader).generateHtmlFile(any(), any());
+    }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 7fb6ede..f4ec936 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -174,13 +174,10 @@
             Settings.NameValueTable.VALUE
     };
 
-    public static final int SETTINGS_TYPE_GLOBAL = 0;
-    public static final int SETTINGS_TYPE_SYSTEM = 1;
-    public static final int SETTINGS_TYPE_SECURE = 2;
-    public static final int SETTINGS_TYPE_SSAID = 3;
-
-    public static final int SETTINGS_TYPE_MASK = 0xF0000000;
-    public static final int SETTINGS_TYPE_SHIFT = 28;
+    public static final int SETTINGS_TYPE_GLOBAL = SettingsState.SETTINGS_TYPE_GLOBAL;
+    public static final int SETTINGS_TYPE_SYSTEM = SettingsState.SETTINGS_TYPE_SYSTEM;
+    public static final int SETTINGS_TYPE_SECURE = SettingsState.SETTINGS_TYPE_SECURE;
+    public static final int SETTINGS_TYPE_SSAID = SettingsState.SETTINGS_TYPE_SSAID;
 
     private static final Bundle NULL_SETTING_BUNDLE = Bundle.forPair(
             Settings.NameValueTable.VALUE, null);
@@ -278,40 +275,23 @@
     private volatile IPackageManager mPackageManager;
 
     public static int makeKey(int type, int userId) {
-        return (type << SETTINGS_TYPE_SHIFT) | userId;
+        return SettingsState.makeKey(type, userId);
     }
 
     public static int getTypeFromKey(int key) {
-        return key >>> SETTINGS_TYPE_SHIFT;
+        return SettingsState.getTypeFromKey(key);
     }
 
     public static int getUserIdFromKey(int key) {
-        return key & ~SETTINGS_TYPE_MASK;
+        return SettingsState.getUserIdFromKey(key);
     }
 
     public static String settingTypeToString(int type) {
-        switch (type) {
-            case SETTINGS_TYPE_GLOBAL: {
-                return "SETTINGS_GLOBAL";
-            }
-            case SETTINGS_TYPE_SECURE: {
-                return "SETTINGS_SECURE";
-            }
-            case SETTINGS_TYPE_SYSTEM: {
-                return "SETTINGS_SYSTEM";
-            }
-            case SETTINGS_TYPE_SSAID: {
-                return "SETTINGS_SSAID";
-            }
-            default: {
-                return "UNKNOWN";
-            }
-        }
+        return SettingsState.settingTypeToString(type);
     }
 
     public static String keyToString(int key) {
-        return "Key[user=" + getUserIdFromKey(key) + ";type="
-                + settingTypeToString(getTypeFromKey(key)) + "]";
+        return SettingsState.keyToString(key);
     }
 
     @Override
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index f901bca..a8a67ab 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -33,6 +33,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.providers.settings.GlobalSettingsProto;
 import android.providers.settings.SettingsOperationProto;
 import android.text.TextUtils;
@@ -46,6 +47,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
 import com.android.server.LocalServices;
 
 import libcore.io.IoUtils;
@@ -195,6 +197,51 @@
     @GuardedBy("mLock")
     private int mNextHistoricalOpIdx;
 
+    public static final int SETTINGS_TYPE_GLOBAL = 0;
+    public static final int SETTINGS_TYPE_SYSTEM = 1;
+    public static final int SETTINGS_TYPE_SECURE = 2;
+    public static final int SETTINGS_TYPE_SSAID = 3;
+
+    public static final int SETTINGS_TYPE_MASK = 0xF0000000;
+    public static final int SETTINGS_TYPE_SHIFT = 28;
+
+    public static int makeKey(int type, int userId) {
+        return (type << SETTINGS_TYPE_SHIFT) | userId;
+    }
+
+    public static int getTypeFromKey(int key) {
+        return key >>> SETTINGS_TYPE_SHIFT;
+    }
+
+    public static int getUserIdFromKey(int key) {
+        return key & ~SETTINGS_TYPE_MASK;
+    }
+
+    public static String settingTypeToString(int type) {
+        switch (type) {
+            case SETTINGS_TYPE_GLOBAL: {
+                return "SETTINGS_GLOBAL";
+            }
+            case SETTINGS_TYPE_SECURE: {
+                return "SETTINGS_SECURE";
+            }
+            case SETTINGS_TYPE_SYSTEM: {
+                return "SETTINGS_SYSTEM";
+            }
+            case SETTINGS_TYPE_SSAID: {
+                return "SETTINGS_SSAID";
+            }
+            default: {
+                return "UNKNOWN";
+            }
+        }
+    }
+
+    public static String keyToString(int key) {
+        return "Key[user=" + getUserIdFromKey(key) + ";type="
+                + settingTypeToString(getTypeFromKey(key)) + "]";
+    }
+
     public SettingsState(Context context, Object lock, File file, int key,
             int maxBytesPerAppPackage, Looper looper) {
         // It is important that we use the same lock as the settings provider
@@ -604,6 +651,13 @@
                 for (int i = 0; i < settingCount; i++) {
                     Setting setting = settings.valueAt(i);
 
+                    if (setting.isTransient()) {
+                        if (DEBUG_PERSISTENCE) {
+                            Slog.i(LOG_TAG, "[SKIPPED PERSISTING]" + setting.getName());
+                        }
+                        continue;
+                    }
+
                     writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(),
                             setting.getValue(), setting.getDefaultValue(), setting.getPackageName(),
                             setting.getTag(), setting.isDefaultFromSystem());
@@ -914,6 +968,14 @@
             return update(this.defaultValue, false, packageName, null, true);
         }
 
+        public boolean isTransient() {
+            switch (getTypeFromKey(getKey())) {
+                case SETTINGS_TYPE_GLOBAL:
+                    return ArrayUtils.contains(Global.TRANSIENT_SETTINGS, getName());
+            }
+            return false;
+        }
+
         public boolean update(String value, boolean setDefault, String packageName, String tag,
                 boolean forceNonSystemPackage) {
             if (NULL_VALUE.equals(value)) {
diff --git a/packages/SettingsProvider/test/Android.mk b/packages/SettingsProvider/test/Android.mk
index a9707d4..902f1c7 100644
--- a/packages/SettingsProvider/test/Android.mk
+++ b/packages/SettingsProvider/test/Android.mk
@@ -12,7 +12,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 LOCAL_PACKAGE_NAME := SettingsProviderTest
 
diff --git a/packages/Shell/res/values-fa/strings.xml b/packages/Shell/res/values-fa/strings.xml
index 75e7760..d4bb3c6 100644
--- a/packages/Shell/res/values-fa/strings.xml
+++ b/packages/Shell/res/values-fa/strings.xml
@@ -23,8 +23,8 @@
     <string name="bugreport_updating_title" msgid="4423539949559634214">"اضافه کردن جزئیات به گزارش اشکال"</string>
     <string name="bugreport_updating_wait" msgid="3322151947853929470">"لطفاً منتظر بمانید..."</string>
     <string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"گزارش مشکل به‌زودی در تلفن نشان داده می‌شود"</string>
-    <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"برای به اشتراک گذاشتن گزارش اشکالتان، انتخاب کنید"</string>
-    <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"برای به اشتراک گذاشتن گزارش اشکال، ضربه بزنید"</string>
+    <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"برای هم‌رسانی گزارش اشکالتان، انتخاب کنید"</string>
+    <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"برای هم‌رسانی گزارش اشکال، ضربه بزنید"</string>
     <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"انتخاب کنید تا گزارش اشکالتان بدون عکس صفحه‌نمایش به اشتراک گذاشته شود یا منتظر بمانید گرفتن عکس از صفحه‌نمایش تمام شود"</string>
     <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"برای اشتراک‌گذاری گزارش مشکل بدون عکس صفحه‌نمایش، ضربه بزنید یا صبر کنید تا عکس صفحه‌نمایش گرفته شود."</string>
     <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"برای اشتراک‌گذاری گزارش مشکل بدون عکس صفحه‌نمایش، ضربه بزنید یا صبر کنید تا عکس صفحه‌نمایش گرفته شود."</string>
diff --git a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
index 4f6fcb9..70d13e6 100644
--- a/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pt-rPT/strings.xml
@@ -59,8 +59,8 @@
     <string name="kg_wrong_password" msgid="4580683060277329277">"Palavra-passe incorreta"</string>
     <string name="kg_wrong_pin" msgid="4785660766909463466">"PIN incorreto"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
-      <item quantity="one">Tente novamente dentro de 1 segundo.</item>
       <item quantity="other">Tente novamente dentro de <xliff:g id="NUMBER">%d</xliff:g> segundos.</item>
+      <item quantity="one">Tente novamente dentro de 1 segundo.</item>
     </plurals>
     <string name="kg_pattern_instructions" msgid="5547646893001491340">"Desenhe o seu padrão"</string>
     <string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Introduza o PIN do cartão SIM."</string>
@@ -97,13 +97,13 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="8364140853305528449">"Desenhou a sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%1$d</xliff:g> vezes. Após mais <xliff:g id="NUMBER_1">%2$d</xliff:g> tentativas sem êxito, ser-lhe-á pedido para desbloquear o telemóvel através de uma conta de email.\n\n Tente novamente dentro de <xliff:g id="NUMBER_2">%3$d</xliff:g> segundos."</string>
     <string name="kg_password_wrong_pin_code_pukked" msgid="3389829202093674267">"Código PIN do cartão SIM incorreto. Tem de contactar o seu operador para desbloquear o dispositivo."</string>
     <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="4314341367727055967">
-      <item quantity="one">Código PIN do cartão SIM incorreto. Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de precisar de contactar o seu operador para desbloquear o dispositivo.</item>
       <item quantity="other">Código PIN do cartão SIM incorreto. Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas.</item>
+      <item quantity="one">Código PIN do cartão SIM incorreto. Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de precisar de contactar o seu operador para desbloquear o dispositivo.</item>
     </plurals>
     <string name="kg_password_wrong_puk_code_dead" msgid="3329017604125179374">"Cartão SIM inutilizável. Contacte o seu operador."</string>
     <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="2287504898931957513">
-      <item quantity="one">Código PUK do cartão SIM incorreto. Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de o cartão SIM ficar permanentemente inutilizável.</item>
       <item quantity="other">Código PUK do cartão SIM incorreto. Tem mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas antes de o cartão SIM ficar permanentemente inutilizável.</item>
+      <item quantity="one">Código PUK do cartão SIM incorreto. Tem mais <xliff:g id="NUMBER_0">%d</xliff:g> tentativa antes de o cartão SIM ficar permanentemente inutilizável.</item>
     </plurals>
     <string name="kg_password_pin_failed" msgid="8769990811451236223">"Falha ao introduzir o PIN do cartão SIM!"</string>
     <string name="kg_password_puk_failed" msgid="1331621440873439974">"Falha ao introduzir o PUK do cartão SIM!"</string>
@@ -123,16 +123,16 @@
     <string name="kg_prompt_reason_device_admin" msgid="3452168247888906179">"Dispositivo bloqueado pelo gestor"</string>
     <string name="kg_prompt_reason_user_request" msgid="8236951765212462286">"O dispositivo foi bloqueado manualmente"</string>
     <plurals name="kg_prompt_reason_time_pattern" formatted="false" msgid="71299470072448533">
-      <item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirme o padrão.</item>
       <item quantity="other">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirme o padrão.</item>
+      <item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirme o padrão.</item>
     </plurals>
     <plurals name="kg_prompt_reason_time_pin" formatted="false" msgid="34586942088144385">
-      <item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirme o PIN.</item>
       <item quantity="other">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirme o PIN.</item>
+      <item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirme o PIN.</item>
     </plurals>
     <plurals name="kg_prompt_reason_time_password" formatted="false" msgid="257297696215346527">
-      <item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirme a palavra-passe.</item>
       <item quantity="other">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_1">%d</xliff:g> horas. Confirme a palavra-passe.</item>
+      <item quantity="one">O dispositivo não é desbloqueado há <xliff:g id="NUMBER_0">%d</xliff:g> hora. Confirme a palavra-passe.</item>
     </plurals>
     <string name="fingerprint_not_recognized" msgid="348813995267914625">"Não reconhecido"</string>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 3dd0e6c..3a41681 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -367,4 +367,20 @@
     <!-- Fingerprint hint message when finger was not recognized.-->
     <string name="fingerprint_not_recognized">Not recognized</string>
 
+    <!-- Instructions telling the user remaining times when enter SIM PIN view.  -->
+    <plurals name="kg_password_default_pin_message">
+        <item quantity="one">Enter SIM PIN, you have <xliff:g id="number">%d</xliff:g> remaining
+attempt before you must contact your carrier to unlock your device.</item>
+        <item quantity="other">Enter SIM PIN, you have <xliff:g id="number">%d</xliff:g> remaining
+attempts.</item>
+    </plurals>
+
+    <!-- Instructions telling the user remaining times when enter SIM PUK view.  -->
+    <plurals name="kg_password_default_puk_message">
+        <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="
+number">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact carrier for details.</item>
+        <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id="
+number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact carrier for details.</item>
+    </plurals>
+
 </resources>
diff --git a/packages/SystemUI/res/drawable/instant_icon.xml b/packages/SystemUI/res/drawable/instant_icon.xml
index 0039c81..8554daa 100644
--- a/packages/SystemUI/res/drawable/instant_icon.xml
+++ b/packages/SystemUI/res/drawable/instant_icon.xml
@@ -14,17 +14,13 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="40dp"
-        android:height="40dp"
-        android:viewportWidth="2.2"
-        android:viewportHeight="2.2">
+        android:height="24dp"
+        android:width="24dp"
+        android:viewportHeight="48"
+        android:viewportWidth="48">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M.1,1.1
-        c0,.55  .45,1   1,1
-        c.55,0  1,-.45  1,-1
-        c0,-.55 -.45,-1 -1,-1
-        c-.55,0 -1,.45  -1,1z
-        M1.15,.95 l.5,0 l-.7,1 l0.1,-.7 l-.5,0 l.7,-1 z"/>
-</vector>
+        android:pathData="M35.8,18.9l-9.8,0.1l0.2,-19l-14.1,29.3l9.9,0l0,18.7z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 863f17b..fac254a 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<RelativeLayout
+<com.android.systemui.HardwareUiLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -45,4 +45,4 @@
         </LinearLayout>
 
     </RelativeLayout>
-</RelativeLayout>
+</com.android.systemui.HardwareUiLayout>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 581ac73..5770779 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -34,14 +34,14 @@
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"বিজ্ঞপ্তিগুলি"</string>
     <string name="battery_low_title" msgid="6456385927409742437">"ব্যাটারি কম"</string>
     <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> অবশিষ্ট আছে"</string>
-    <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"<xliff:g id="PERCENTAGE">%s</xliff:g> অবশিষ্ট আছে৷ ব্যাটারি সাশ্রয়কারী চালু আছে৷"</string>
+    <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> অবশিষ্ট আছে। ব্যাটারি সেভার চালু আছে।"</string>
     <string name="invalid_charger" msgid="4549105996740522523">"USB চার্জিং সমর্থিত নয়৷\nকেবলমাত্র সরবহারকৃত চার্জার ব্যবহার করুন৷"</string>
     <string name="invalid_charger_title" msgid="3515740382572798460">"USB চার্জিং সমর্থিত নয়।"</string>
     <string name="invalid_charger_text" msgid="5474997287953892710">"শুধুমাত্র সরবরাহকৃত চার্জার ব্যবহার করুন।"</string>
     <string name="battery_low_why" msgid="4553600287639198111">"সেটিংস"</string>
-    <string name="battery_saver_confirmation_title" msgid="5299585433050361634">"ব্যাটারি সঞ্চয়কারী চালু করবেন?"</string>
+    <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"ব্যাটারি সেভার চালু করবেন?"</string>
     <string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"চালু করুন"</string>
-    <string name="battery_saver_start_action" msgid="5576697451677486320">"ব্যাটারি সঞ্চয়কারী চালু"</string>
+    <string name="battery_saver_start_action" msgid="8187820911065797519">"ব্যাটারি সেভার চালু করুন"</string>
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"সেটিংস"</string>
     <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"ওয়াই-ফাই"</string>
     <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"স্বতঃ-ঘূর্ণায়মান স্ক্রিন"</string>
@@ -243,8 +243,6 @@
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ডেট বিরতি দেওয়া হয়েছে"</string>
     <string name="data_usage_disabled_dialog" msgid="4919541636934603816">"আপনার সেট করা ডেটার সীমা ফুরিয়ে গেছে। আপনি এখন আর মোবাইল ডেটা ব্যবহার করতে পারবেন না।\n\nযদি আপনি আবার শুরু করেন, ডেটা ব্যবহারের জন্য মূল্য প্রযোজ্য হতে পারে।"</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"পুনঃসূচনা করুন"</string>
-    <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"কোনো ইন্টারনেট সংযোগ নেই"</string>
-    <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"ওয়াই-ফাই সংযুক্ত হয়েছে"</string>
     <string name="gps_notification_searching_text" msgid="8574247005642736060">"GPS এর জন্য অনুসন্ধান করা হচ্ছে"</string>
     <string name="gps_notification_found_text" msgid="4619274244146446464">"GPS এর দ্বারা সেট করা অবস্থান"</string>
     <string name="accessibility_location_active" msgid="2427290146138169014">"অবস্থান অনুরোধ সক্রিয় রয়েছে"</string>
@@ -401,9 +399,9 @@
     <string name="user_remove_user_title" msgid="4681256956076895559">"ব্যবহারকারী সরাবেন?"</string>
     <string name="user_remove_user_message" msgid="1453218013959498039">"এই ব্যবহারকারীর সমস্ত অ্যাপ্লিকেশান ও ডেটা মুছে ফেলা হবে।"</string>
     <string name="user_remove_user_remove" msgid="7479275741742178297">"সরান"</string>
-    <string name="battery_saver_notification_title" msgid="237918726750955859">"ব্যাটারি সেভার চালু রয়েছে"</string>
+    <string name="battery_saver_notification_title" msgid="8614079794522291840">"ব্যাটারি সেভার চালু আছে"</string>
     <string name="battery_saver_notification_text" msgid="820318788126672692">"কার্য-সম্পাদনা ও পশ্চাদপট ডেটাকে কমিয়ে দেয়"</string>
-    <string name="battery_saver_notification_action_text" msgid="109158658238110382">"ব্যাটারি সঞ্চয়কারী বন্ধ করুন"</string>
+    <string name="battery_saver_notification_action_text" msgid="132118784269455533">"ব্যাটারি সেভার বন্ধ করুন"</string>
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> আপনার স্ক্রীনে দেখানো সব কিছু ক্যাপচার করা শুরু করবে।"</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"আর দেখাবেন না"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"সবকিছু সাফ করুন"</string>
@@ -582,8 +580,8 @@
       <item quantity="other">%d মিনিট</item>
     </plurals>
     <string name="battery_panel_title" msgid="7944156115535366613">"ব্যাটারির ব্যবহার"</string>
-    <string name="battery_detail_charging_summary" msgid="4055327085770378335">"চার্জ করার সময় ব্যাটারি সেভার কাজ করে না"</string>
-    <string name="battery_detail_switch_title" msgid="8763441006881907058">"ব্যাটারি সেভার"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"চার্জ করার সময় ব্যাটারি সেভার উপলব্ধ নয়"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"ব্যাটারি সেভার"</string>
     <string name="battery_detail_switch_summary" msgid="9049111149407626804">"কার্য-সম্পাদনা ও পশ্চাদপট ডেটাকে কমিয়ে দেয়"</string>
     <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g> বোতাম"</string>
     <string name="keyboard_key_home" msgid="2243500072071305073">"হোম"</string>
@@ -732,10 +730,10 @@
     <string name="pip_skip_to_prev" msgid="1955311326688637914">"পিছনে যাওয়ার জন্য এড়িয়ে যান"</string>
     <string name="thermal_shutdown_title" msgid="4458304833443861111">"আপনার ফোন গরম হওয়ার জন্য বন্ধ হয়ে গেছে"</string>
     <string name="thermal_shutdown_message" msgid="9006456746902370523">"আপনার ফোন এখন ঠিক-ঠাক চলছে"</string>
-    <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"আপনার ফোন খুব বেশি গরম হয়েছিল বলে ঠান্ডা হওয়ার জন্য বন্ধ হয়ে গেছে। আপনার ফোন ঠিক-ঠাক ভাবে চলছে না।\n\nআপনার ফোন খুব বেশি গরম হয়ে যাবে যদি আপনি:\n	•এমন অ্যাপ ব্যবহার করলে যেটি আপনার ডিভাইসের রিসোর্স বেশি ব্যবহার করে (যেমন গেমিং, ভিডিও বা নেভিগেশন অ্যাপ)\n	• বড় ফাইল ডাউনলোড বা আপলোড করলে\n	• বেশি তাপমাত্রায় আপনার ফোন ব্যবহার করলে"</string>
+    <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"আপনার ফোন খুব বেশি গরম হয়েছিল বলে ঠাণ্ডা হওয়ার জন্য বন্ধ হয়ে গেছে। আপনার ফোন ঠিক-ঠাক ভাবে চলছে না।\n\nআপনার ফোন খুব বেশি গরম হয়ে যাবে যদি আপনি:\n	•এমন অ্যাপ ব্যবহার করলে যেটি আপনার ডিভাইসের রিসোর্স বেশি ব্যবহার করে (যেমন গেমিং, ভিডিও বা নেভিগেশন অ্যাপ)\n	• বড় ফাইল ডাউনলোড বা আপলোড করলে\n	• বেশি তাপমাত্রায় আপনার ফোন ব্যবহার করলে"</string>
     <string name="high_temp_title" msgid="4589508026407318374">"ফোনটি গরম হচ্ছে"</string>
-    <string name="high_temp_notif_message" msgid="5642466103153429279">"ফোনটি ঠান্ডা হওয়ার সময় কিছু বৈশিষ্ট্য সীমিত হতে পারে"</string>
-    <string name="high_temp_dialog_message" msgid="6840700639374113553">"আপনার ফোনটি নিজে থেকেই ঠান্ডা হওয়ার চেষ্টা করবে৷ আপনি তবুও আপনার ফোন ব্যবহার করতে পারেন, কিন্তু এটি একটু ধীরে চলতে পারে৷\n\nআপনার ফোনটি পুরোপুরি ঠান্ডা হয়ে গেলে এটি স্বাভাবিকভাবে চলবে৷"</string>
+    <string name="high_temp_notif_message" msgid="5642466103153429279">"ফোনটি ঠাণ্ডা হওয়ার সময় কিছু বৈশিষ্ট্য সীমিত হতে পারে"</string>
+    <string name="high_temp_dialog_message" msgid="6840700639374113553">"আপনার ফোনটি নিজে থেকেই ঠাণ্ডা হওয়ার চেষ্টা করবে৷ আপনি তবুও আপনার ফোন ব্যবহার করতে পারেন, কিন্তু এটি একটু ধীরে চলতে পারে৷\n\nআপনার ফোনটি পুরোপুরি ঠাণ্ডা হয়ে গেলে এটি স্বাভাবিকভাবে চলবে৷"</string>
     <string name="lockscreen_shortcut_left" msgid="2182769107618938629">"বাঁদিকের শর্টকাট"</string>
     <string name="lockscreen_shortcut_right" msgid="3328683699505226536">"ডানদিকের শর্টকাট"</string>
     <string name="lockscreen_unlock_left" msgid="2043092136246951985">"বাঁদিকের শর্টকাট দিয়েও আনলক করা যায়"</string>
@@ -758,7 +756,8 @@
     <string name="instant_apps" msgid="6647570248119804907">"ঝটপট অ্যাপ"</string>
     <string name="instant_apps_message" msgid="8116608994995104836">"ঝটপট অ্যাপ ইনস্টল করার প্রয়োজন হয় না।"</string>
     <string name="app_info" msgid="6856026610594615344">"অ্যাপের তথ্য"</string>
-    <string name="go_to_web" msgid="1106022723459948514">"ওয়েবে যান"</string>
+    <!-- no translation found for go_to_web (2650669128861626071) -->
+    <skip />
     <string name="mobile_data" msgid="7094582042819250762">"মোবাইল ডেটা"</string>
     <string name="wifi_is_off" msgid="1838559392210456893">"ওয়াই ফাই বন্ধ আছে"</string>
     <string name="bt_is_off" msgid="2640685272289706392">"ব্লুটুথ বন্ধ আছে"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 601d8e3..530c1a8 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -339,9 +339,9 @@
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"Podjela po horizontali"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"Podjela po vertikali"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"Prilagođena podjela"</string>
-    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Podijeli ekran nagore"</string>
-    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Podijeli ekran nalijevo"</string>
-    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Podijeli ekran nadesno"</string>
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"Dijeli ekran nagore"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"Dijeli ekran nalijevo"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"Dijeli ekran nadesno"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"Napunjeno"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"Punjenje"</string>
     <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Do kraja punjenja preostalo <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
@@ -764,7 +764,8 @@
     <string name="instant_apps" msgid="6647570248119804907">"Instant-aplikacije"</string>
     <string name="instant_apps_message" msgid="8116608994995104836">"Za instant aplikacije nije potrebna instalacija"</string>
     <string name="app_info" msgid="6856026610594615344">"Informacije o aplikaciji"</string>
-    <string name="go_to_web" msgid="1106022723459948514">"Idite na internet"</string>
+    <!-- no translation found for go_to_web (2650669128861626071) -->
+    <skip />
     <string name="mobile_data" msgid="7094582042819250762">"Mobilni podaci"</string>
     <string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi veza je isključena"</string>
     <string name="bt_is_off" msgid="2640685272289706392">"Bluetooth je isključen"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index cd1f057..d107fea 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -756,7 +756,8 @@
     <string name="instant_apps" msgid="6647570248119804907">"برنامه‌های فوری"</string>
     <string name="instant_apps_message" msgid="8116608994995104836">"برنامه‌های فوری نیاز به نصب ندارند."</string>
     <string name="app_info" msgid="6856026610594615344">"اطلاعات برنامه"</string>
-    <string name="go_to_web" msgid="1106022723459948514">"رفتن به وب"</string>
+    <!-- no translation found for go_to_web (2650669128861626071) -->
+    <skip />
     <string name="mobile_data" msgid="7094582042819250762">"داده تلفن همراه"</string>
     <string name="wifi_is_off" msgid="1838559392210456893">"‏Wi-Fi خاموش است"</string>
     <string name="bt_is_off" msgid="2640685272289706392">"بلوتوث خاموش است"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 075edf1..b4709bb 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -756,7 +756,8 @@
     <string name="instant_apps" msgid="6647570248119804907">"Aplikasi Instan"</string>
     <string name="instant_apps_message" msgid="8116608994995104836">"Aplikasi instan tidak perlu diinstal."</string>
     <string name="app_info" msgid="6856026610594615344">"Info aplikasi"</string>
-    <string name="go_to_web" msgid="1106022723459948514">"Buka di web"</string>
+    <!-- no translation found for go_to_web (2650669128861626071) -->
+    <skip />
     <string name="mobile_data" msgid="7094582042819250762">"Data seluler"</string>
     <string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi nonaktif"</string>
     <string name="bt_is_off" msgid="2640685272289706392">"Bluetooth nonaktif"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 1b80410..e871c6a 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -756,7 +756,8 @@
     <string name="instant_apps" msgid="6647570248119804907">"მყისიერი აპები"</string>
     <string name="instant_apps_message" msgid="8116608994995104836">"მყისიერი აპები ინსტალაციას არ საჭიროებს."</string>
     <string name="app_info" msgid="6856026610594615344">"აპის შესახებ"</string>
-    <string name="go_to_web" msgid="1106022723459948514">"ვებზე გადასვლა"</string>
+    <!-- no translation found for go_to_web (2650669128861626071) -->
+    <skip />
     <string name="mobile_data" msgid="7094582042819250762">"მობილური ინტერნეტი"</string>
     <string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi გამორთულია"</string>
     <string name="bt_is_off" msgid="2640685272289706392">"Bluetooth გამორთულია"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index ec4e082..b5b3475 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -26,8 +26,8 @@
     <string name="status_bar_no_recent_apps" msgid="7374907845131203189">"Os ecrãs recentes aparecem aqui"</string>
     <string name="status_bar_accessibility_dismiss_recents" msgid="4576076075226540105">"Ignorar aplicações recentes"</string>
     <plurals name="status_bar_accessibility_recent_apps" formatted="false" msgid="9138535907802238759">
-      <item quantity="one">1 ecrã na Vista geral</item>
       <item quantity="other">%d ecrãs na Vista geral</item>
+      <item quantity="one">1 ecrã na Vista geral</item>
     </plurals>
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Sem notificações"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em curso"</string>
@@ -249,8 +249,8 @@
     <string name="accessibility_clear_all" msgid="5235938559247164925">"Limpar todas as notificações."</string>
     <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
     <plurals name="notification_group_overflow_description" formatted="false" msgid="4579313201268495404">
-      <item quantity="one">Mais <xliff:g id="NUMBER_0">%s</xliff:g> notificação no grupo.</item>
       <item quantity="other">Mais <xliff:g id="NUMBER_1">%s</xliff:g> notificações no grupo.</item>
+      <item quantity="one">Mais <xliff:g id="NUMBER_0">%s</xliff:g> notificação no grupo.</item>
     </plurals>
     <string name="status_bar_notification_inspect_item_title" msgid="5668348142410115323">"Definições de notificação"</string>
     <string name="status_bar_notification_app_settings_title" msgid="5525260160341558869">"Definições do <xliff:g id="APP_NAME">%s</xliff:g>"</string>
@@ -551,13 +551,13 @@
     <string name="notification_default_channel_desc" msgid="2506053815870808359">"Esta aplicação não tem categorias de notificação"</string>
     <string name="notification_unblockable_desc" msgid="3561016061737896906">"Não é possível desativar as notificações desta aplicação"</string>
     <plurals name="notification_num_channels_desc" formatted="false" msgid="5492793452274077663">
-      <item quantity="one">1 de <xliff:g id="NUMBER_0">%s</xliff:g> categoria de notificação desta aplicação</item>
       <item quantity="other">1 de <xliff:g id="NUMBER_1">%s</xliff:g> categorias de notificação desta aplicação</item>
+      <item quantity="one">1 de <xliff:g id="NUMBER_0">%s</xliff:g> categoria de notificação desta aplicação</item>
     </plurals>
     <string name="notification_channels_list_desc_2" msgid="6214732715833946441">"<xliff:g id="CHANNEL_NAME_1">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2">%2$s</xliff:g>"</string>
     <plurals name="notification_channels_list_desc_2_and_others" formatted="false" msgid="2747813553355336157">
-      <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> e mais <xliff:g id="NUMBER_2">%3$d</xliff:g></item>
       <item quantity="other"><xliff:g id="CHANNEL_NAME_1_3">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_4">%2$s</xliff:g> e mais <xliff:g id="NUMBER_5">%3$d</xliff:g></item>
+      <item quantity="one"><xliff:g id="CHANNEL_NAME_1_0">%1$s</xliff:g>, <xliff:g id="CHANNEL_NAME_2_1">%2$s</xliff:g> e mais <xliff:g id="NUMBER_2">%3$d</xliff:g></item>
     </plurals>
     <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"Controlos de notificações da aplicação <xliff:g id="APP_NAME">%1$s</xliff:g> abertos"</string>
     <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"Controlos de notificações da aplicação <xliff:g id="APP_NAME">%1$s</xliff:g> fechados"</string>
@@ -572,12 +572,12 @@
     <string name="snooze_undo" msgid="6074877317002985129">"ANULAR"</string>
     <string name="snoozed_for_time" msgid="2390718332980204462">"Suspensa por <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
     <plurals name="snoozeHourOptions" formatted="false" msgid="2124335842674413030">
-      <item quantity="one">%d hora</item>
       <item quantity="other">%d horas</item>
+      <item quantity="one">%d hora</item>
     </plurals>
     <plurals name="snoozeMinuteOptions" formatted="false" msgid="4127251700591510196">
-      <item quantity="one">%d minuto</item>
       <item quantity="other">%d minutos</item>
+      <item quantity="one">%d minuto</item>
     </plurals>
     <string name="battery_panel_title" msgid="7944156115535366613">"Utiliz. da bateria"</string>
     <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Poupança de bateria não disponível durante o carregamento"</string>
@@ -756,7 +756,8 @@
     <string name="instant_apps" msgid="6647570248119804907">"Aplicações instantâneas"</string>
     <string name="instant_apps_message" msgid="8116608994995104836">"As Aplicações instantâneas não requerem instalação."</string>
     <string name="app_info" msgid="6856026610594615344">"Informações da aplicação"</string>
-    <string name="go_to_web" msgid="1106022723459948514">"Aceder à Web"</string>
+    <!-- no translation found for go_to_web (2650669128861626071) -->
+    <skip />
     <string name="mobile_data" msgid="7094582042819250762">"Dados móveis"</string>
     <string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi desativado"</string>
     <string name="bt_is_off" msgid="2640685272289706392">"Bluetooth desativado"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 5b9ff78..95cd2d0 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -770,7 +770,8 @@
     <string name="instant_apps" msgid="6647570248119804907">"Додатки з миттєвим запуском"</string>
     <string name="instant_apps_message" msgid="8116608994995104836">"Додатки з миттєвим запуском не потрібно встановлювати."</string>
     <string name="app_info" msgid="6856026610594615344">"Про додаток"</string>
-    <string name="go_to_web" msgid="1106022723459948514">"Перейти на веб-сайт"</string>
+    <!-- no translation found for go_to_web (2650669128861626071) -->
+    <skip />
     <string name="mobile_data" msgid="7094582042819250762">"Мобільний трафік"</string>
     <string name="wifi_is_off" msgid="1838559392210456893">"Wi-Fi вимкнено"</string>
     <string name="bt_is_off" msgid="2640685272289706392">"Bluetooth вимкнено"</string>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ca85445..659c5a8 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1989,8 +1989,8 @@
     <!-- Action label for launching app info on the specified app [CHAR LIMIT=20] -->
     <string name="app_info">App info</string>
 
-    <!-- Action label for switching to web for an instant app [CHAR LIMIT=20] -->
-    <string name="go_to_web">Go to web</string>
+    <!-- Action label for switching to a browser for an instant app [CHAR LIMIT=20] -->
+    <string name="go_to_web">Go to browser</string>
 
     <!-- Quick settings tile for toggling mobile data [CHAR LIMIT=20] -->
     <string name="mobile_data">Mobile data</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
index 17cb0d8..7db3ac6 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java
@@ -43,6 +43,9 @@
     public void onActivityDismissingDockedStack() { }
     public void onActivityLaunchOnSecondaryDisplayFailed() { }
     public void onTaskProfileLocked(int taskId, int userId) { }
+    public void onTaskRemoved(int taskId) { }
+    public void onTaskMovedToFront(int taskId) { }
+    public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { }
 
     /**
      * Checks that the current user matches the process. Since
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index 81c37a9..857e0ea 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -145,6 +145,23 @@
         mHandler.obtainMessage(H.ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget();
     }
 
+    @Override
+    public void onTaskRemoved(int taskId) throws RemoteException {
+        mHandler.obtainMessage(H.ON_TASK_REMOVED, taskId, 0).sendToTarget();
+    }
+
+    @Override
+    public void onTaskMovedToFront(int taskId) throws RemoteException {
+        mHandler.obtainMessage(H.ON_TASK_MOVED_TO_FRONT, taskId, 0).sendToTarget();
+    }
+
+    @Override
+    public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation)
+            throws RemoteException {
+        mHandler.obtainMessage(H.ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE, taskId,
+                requestedOrientation).sendToTarget();
+    }
+
     private final class H extends Handler {
         private static final int ON_TASK_STACK_CHANGED = 1;
         private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
@@ -157,6 +174,10 @@
         private static final int ON_PINNED_STACK_ANIMATION_STARTED = 9;
         private static final int ON_ACTIVITY_UNPINNED = 10;
         private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED = 11;
+        private static final int ON_TASK_REMOVED = 12;
+        private static final int ON_TASK_MOVED_TO_FRONT = 13;
+        private static final int ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE = 14;
+
 
         public H(Looper looper) {
             super(looper);
@@ -241,6 +262,25 @@
                         }
                         break;
                     }
+                    case ON_TASK_REMOVED: {
+                        for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+                            mTaskStackListeners.get(i).onTaskRemoved(msg.arg1);
+                        }
+                        break;
+                    }
+                    case ON_TASK_MOVED_TO_FRONT: {
+                        for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+                            mTaskStackListeners.get(i).onTaskMovedToFront(msg.arg1);
+                        }
+                        break;
+                    }
+                    case ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE: {
+                        for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+                            mTaskStackListeners.get(i).onActivityRequestedOrientationChanged(
+                                    msg.arg1, msg.arg2);
+                        }
+                        break;
+                    }
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index 775b9e8..a980413 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -16,8 +16,8 @@
 
 package com.android.keyguard;
 
-import static com.android.keyguard.LatencyTracker.ACTION_CHECK_CREDENTIAL;
-import static com.android.keyguard.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
 
 import android.content.Context;
 import android.os.AsyncTask;
@@ -29,6 +29,7 @@
 import android.view.View;
 import android.widget.LinearLayout;
 
+import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternChecker;
 import com.android.internal.widget.LockPatternUtils;
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index ec5f356..d636316 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -15,8 +15,8 @@
  */
 package com.android.keyguard;
 
-import static com.android.keyguard.LatencyTracker.ACTION_CHECK_CREDENTIAL;
-import static com.android.keyguard.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
+import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
 
 import android.content.Context;
 import android.graphics.Rect;
@@ -33,6 +33,7 @@
 import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
 
+import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternChecker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternView;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index 432b406..6e0b56e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -53,8 +53,13 @@
     private ProgressDialog mSimUnlockProgressDialog = null;
     private CheckSimPin mCheckSimPinThread;
 
+    // Below flag is set to true during power-up or when a new SIM card inserted on device.
+    // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would
+    // be displayed to inform user about the number of remaining PIN attempts left.
+    private boolean mShowDefaultMessage = true;
+    private int mRemainingAttempts = -1;
     private AlertDialog mRemainingAttemptsDialog;
-    private int mSubId;
+    private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private ImageView mSimImageView;
 
     KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
@@ -91,34 +96,71 @@
     public void resetState() {
         super.resetState();
         if (DEBUG) Log.v(TAG, "Resetting state");
-        KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
-        mSubId = monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED);
-        boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
-        if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
-            int count = TelephonyManager.getDefault().getSimCount();
-            Resources rez = getResources();
-            String msg;
-            int color = Color.WHITE;
-            if (count < 2) {
-                msg = rez.getString(R.string.kg_sim_pin_instructions);
-            } else {
-                SubscriptionInfo info = monitor.getSubscriptionInfoForSubId(mSubId);
-                CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash
-                msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
-                if (info != null) {
-                    color = info.getIconTint();
-                }
-            }
-            if (isEsimLocked) {
-                msg = msg + " " + rez.getString(R.string.kg_sim_lock_instructions_esim);
-            }
-            mSecurityMessageDisplay.setMessage(msg);
-            mSimImageView.setImageTintList(ColorStateList.valueOf(color));
+        handleSubInfoChangeIfNeeded();
+        if (mShowDefaultMessage) {
+            showDefaultMessage();
         }
+        boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
+
         KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
         esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
     }
 
+    private void showDefaultMessage() {
+        if (mRemainingAttempts >= 0) {
+            mSecurityMessageDisplay.setMessage(getPinPasswordErrorMessage(
+                    mRemainingAttempts, true));
+            return;
+        }
+
+        boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
+        int count = TelephonyManager.getDefault().getSimCount();
+        Resources rez = getResources();
+        String msg;
+        int color = Color.WHITE;
+        if (count < 2) {
+            msg = rez.getString(R.string.kg_sim_pin_instructions);
+        } else {
+            SubscriptionInfo info = KeyguardUpdateMonitor.getInstance(mContext).
+                    getSubscriptionInfoForSubId(mSubId);
+            CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash
+            msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName);
+            if (info != null) {
+                color = info.getIconTint();
+            }
+        }
+
+        if (isEsimLocked) {
+            msg = msg + " " + rez.getString(R.string.kg_sim_lock_instructions_esim);
+        }
+
+        mSecurityMessageDisplay.setMessage(msg);
+        mSimImageView.setImageTintList(ColorStateList.valueOf(color));
+
+        // Sending empty PIN here to query the number of remaining PIN attempts
+        new CheckSimPin("", mSubId) {
+            void onSimCheckResponse(final int result, final int attemptsRemaining) {
+                Log.d(LOG_TAG, "onSimCheckResponse " + " dummy One result" + result +
+                        " attemptsRemaining=" + attemptsRemaining);
+                if (attemptsRemaining >= 0) {
+                    mRemainingAttempts = attemptsRemaining;
+                    mSecurityMessageDisplay.setMessage(
+                            getPinPasswordErrorMessage(attemptsRemaining, true));
+                }
+            }
+        }.start();
+    }
+
+    private void handleSubInfoChangeIfNeeded() {
+        KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
+        int subId = monitor.getNextSubIdForState(IccCardConstants.State.PIN_REQUIRED);
+        if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
+            mSubId = subId;
+            mShowDefaultMessage = true;
+            mRemainingAttempts = -1;
+        }
+    }
+
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
@@ -131,17 +173,19 @@
         return 0;
     }
 
-    private String getPinPasswordErrorMessage(int attemptsRemaining) {
+    private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
         String displayMessage;
-
+        int msgId;
         if (attemptsRemaining == 0) {
             displayMessage = getContext().getString(R.string.kg_password_wrong_pin_code_pukked);
         } else if (attemptsRemaining > 0) {
+            msgId = isDefault ? R.plurals.kg_password_default_pin_message :
+                     R.plurals.kg_password_wrong_pin_code;
             displayMessage = getContext().getResources()
-                    .getQuantityString(R.plurals.kg_password_wrong_pin_code, attemptsRemaining,
-                            attemptsRemaining);
+                    .getQuantityString(msgId, attemptsRemaining, attemptsRemaining);
         } else {
-            displayMessage = getContext().getString(R.string.kg_password_pin_failed);
+            msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed;
+            displayMessage = getContext().getString(msgId);
         }
         if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:"
                 + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
@@ -252,7 +296,7 @@
     }
 
     private Dialog getSimRemainingAttemptsDialog(int remaining) {
-        String msg = getPinPasswordErrorMessage(remaining);
+        String msg = getPinPasswordErrorMessage(remaining, false);
         if (mRemainingAttemptsDialog == null) {
             Builder builder = new AlertDialog.Builder(mContext);
             builder.setMessage(msg);
@@ -288,6 +332,7 @@
                     post(new Runnable() {
                         @Override
                         public void run() {
+                            mRemainingAttempts = attemptsRemaining;
                             if (mSimUnlockProgressDialog != null) {
                                 mSimUnlockProgressDialog.hide();
                             }
@@ -296,8 +341,13 @@
                             if (result == PhoneConstants.PIN_RESULT_SUCCESS) {
                                 KeyguardUpdateMonitor.getInstance(getContext())
                                         .reportSimUnlocked(mSubId);
-                                mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+                                mRemainingAttempts = -1;
+                                mShowDefaultMessage = true;
+                                if (mCallback != null) {
+                                    mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+                                }
                             } else {
+                                mShowDefaultMessage = false;
                                 if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) {
                                     if (attemptsRemaining <= 2) {
                                         // this is getting critical - show dialog
@@ -305,7 +355,7 @@
                                     } else {
                                         // show message
                                         mSecurityMessageDisplay.setMessage(
-                                                getPinPasswordErrorMessage(attemptsRemaining));
+                                                getPinPasswordErrorMessage(attemptsRemaining, false));
                                     }
                                 } else {
                                     // "PIN operation failed!" - no idea what this was and no way to
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 7f79008..876d170 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -52,11 +52,17 @@
 
     private ProgressDialog mSimUnlockProgressDialog = null;
     private CheckSimPuk mCheckSimPukThread;
+
+    // Below flag is set to true during power-up or when a new SIM card inserted on device.
+    // When this is true and when SIM card is PUK locked state, on PIN lock screen, message would
+    // be displayed to inform user about the number of remaining PUK attempts left.
+    private boolean mShowDefaultMessage = true;
+    private int mRemainingAttempts = -1;
     private String mPukText;
     private String mPinText;
     private StateMachine mStateMachine = new StateMachine();
     private AlertDialog mRemainingAttemptsDialog;
-    private int mSubId;
+    private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private ImageView mSimImageView;
 
     KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
@@ -132,34 +138,17 @@
             }
         }
 
+
         void reset() {
             mPinText="";
             mPukText="";
             state = ENTER_PUK;
-            KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
-            mSubId = monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED);
-            boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
-            if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
-                int count = TelephonyManager.getDefault().getSimCount();
-                Resources rez = getResources();
-                String msg;
-                int color = Color.WHITE;
-                if (count < 2) {
-                    msg = rez.getString(R.string.kg_puk_enter_puk_hint);
-                } else {
-                    SubscriptionInfo info = monitor.getSubscriptionInfoForSubId(mSubId);
-                    CharSequence displayName = info != null ? info.getDisplayName() : "";
-                    msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName);
-                    if (info != null) {
-                        color = info.getIconTint();
-                    }
-                }
-                if (isEsimLocked) {
-                    msg = msg + " " + rez.getString(R.string.kg_sim_lock_instructions_esim);
-                }
-                mSecurityMessageDisplay.setMessage(msg);
-                mSimImageView.setImageTintList(ColorStateList.valueOf(color));
+            handleSubInfoChangeIfNeeded();
+            if (mShowDefaultMessage) {
+                showDefaultMessage();
             }
+            boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
+
             KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
             esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
             mPasswordEntry.requestFocus();
@@ -168,23 +157,79 @@
 
     }
 
+    private void showDefaultMessage() {
+        if (mRemainingAttempts >= 0) {
+            mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage(
+                    mRemainingAttempts, true));
+            return;
+        }
+
+        boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId);
+        int count = TelephonyManager.getDefault().getSimCount();
+        Resources rez = getResources();
+        String msg;
+        int color = Color.WHITE;
+        if (count < 2) {
+            msg = rez.getString(R.string.kg_puk_enter_puk_hint);
+        } else {
+            SubscriptionInfo info = KeyguardUpdateMonitor.getInstance(mContext).
+                    getSubscriptionInfoForSubId(mSubId);
+            CharSequence displayName = info != null ? info.getDisplayName() : "";
+            msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName);
+            if (info != null) {
+                color = info.getIconTint();
+            }
+        }
+        if (isEsimLocked) {
+            msg = msg + " " + rez.getString(R.string.kg_sim_lock_instructions_esim);
+        }
+        mSecurityMessageDisplay.setMessage(msg);
+        mSimImageView.setImageTintList(ColorStateList.valueOf(color));
+
+        // Sending empty PUK here to query the number of remaining PIN attempts
+        new CheckSimPuk("", "", mSubId) {
+            void onSimLockChangedResponse(final int result, final int attemptsRemaining) {
+                Log.d(LOG_TAG, "onSimCheckResponse " + " dummy One result" + result +
+                        " attemptsRemaining=" + attemptsRemaining);
+                if (attemptsRemaining >= 0) {
+                    mRemainingAttempts = attemptsRemaining;
+                    mSecurityMessageDisplay.setMessage(
+                            getPukPasswordErrorMessage(attemptsRemaining, true));
+                }
+            }
+        }.start();
+    }
+
+    private void handleSubInfoChangeIfNeeded() {
+        KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
+        int subId = monitor.getNextSubIdForState(IccCardConstants.State.PUK_REQUIRED);
+        if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) {
+            mSubId = subId;
+            mShowDefaultMessage = true;
+            mRemainingAttempts = -1;
+        }
+    }
+
     @Override
     protected int getPromtReasonStringRes(int reason) {
         // No message on SIM Puk
         return 0;
     }
 
-    private String getPukPasswordErrorMessage(int attemptsRemaining) {
+    private String getPukPasswordErrorMessage(int attemptsRemaining, boolean isDefault) {
         String displayMessage;
 
         if (attemptsRemaining == 0) {
             displayMessage = getContext().getString(R.string.kg_password_wrong_puk_code_dead);
         } else if (attemptsRemaining > 0) {
+            int msgId = isDefault ? R.plurals.kg_password_default_puk_message :
+                    R.plurals.kg_password_wrong_puk_code;
             displayMessage = getContext().getResources()
-                    .getQuantityString(R.plurals.kg_password_wrong_puk_code, attemptsRemaining,
-                            attemptsRemaining);
+                    .getQuantityString(msgId, attemptsRemaining, attemptsRemaining);
         } else {
-            displayMessage = getContext().getString(R.string.kg_password_puk_failed);
+            int msgId = isDefault ? R.string.kg_puk_enter_puk_hint :
+                    R.string.kg_password_puk_failed;
+            displayMessage = getContext().getString(msgId);
         }
         if (DEBUG) Log.d(LOG_TAG, "getPukPasswordErrorMessage:"
                 + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage);
@@ -303,7 +348,7 @@
     }
 
     private Dialog getPukRemainingAttemptsDialog(int remaining) {
-        String msg = getPukPasswordErrorMessage(remaining);
+        String msg = getPukPasswordErrorMessage(remaining, false);
         if (mRemainingAttemptsDialog == null) {
             AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
             builder.setMessage(msg);
@@ -359,16 +404,25 @@
                             if (result == PhoneConstants.PIN_RESULT_SUCCESS) {
                                 KeyguardUpdateMonitor.getInstance(getContext())
                                         .reportSimUnlocked(mSubId);
-                                mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+                                mRemainingAttempts = -1;
+                                mShowDefaultMessage = true;
+                                if (mCallback != null) {
+                                    mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+                                }
                             } else {
+                                mShowDefaultMessage = false;
                                 if (result == PhoneConstants.PIN_PASSWORD_INCORRECT) {
+                                    // show message
+                                    mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage(
+                                            attemptsRemaining, false));
                                     if (attemptsRemaining <= 2) {
                                         // this is getting critical - show dialog
                                         getPukRemainingAttemptsDialog(attemptsRemaining).show();
                                     } else {
                                         // show message
                                         mSecurityMessageDisplay.setMessage(
-                                                getPukPasswordErrorMessage(attemptsRemaining));
+                                                getPukPasswordErrorMessage(
+                                                attemptsRemaining, false));
                                     }
                                 } else {
                                     mSecurityMessageDisplay.setMessage(getContext().getString(
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index 0219db3..12f75bb 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -307,9 +307,8 @@
 
     void sendAccessibilityEventTypeViewTextChanged(String beforeText, int fromIndex,
                                                    int removedCount, int addedCount) {
-        if (AccessibilityManager.getInstance(mContext).isObservedEventType(
-                    AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)
-                && (isFocused() || isSelected() && isShown())) {
+        if (AccessibilityManager.getInstance(mContext).isEnabled() &&
+                (isFocused() || isSelected() && isShown())) {
             AccessibilityEvent event =
                     AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
             event.setFromIndex(fromIndex);
diff --git a/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags b/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
index d4149ea..9c847be 100644
--- a/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
+++ b/packages/SystemUI/src/com/android/systemui/EventLogTags.logtags
@@ -61,8 +61,3 @@
 ##       4: SYSTEM_REGISTER_USER     System sysui registers user's callbacks
 ##       5: SYSTEM_UNREGISTER_USER   System sysui unregisters user's callbacks (after death)
 36060 sysui_recents_connection (type|1),(user|1)
-
-# ---------------------------
-# LatencyTracker.java
-# ---------------------------
-36070 sysui_latency (action|1|5),(latency|1|3)
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index 1d55ee5..cbb69ee 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -25,7 +25,7 @@
 import android.os.SystemClock;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.LatencyTracker;
+import com.android.internal.util.LatencyTracker;
 import com.android.systemui.statusbar.phone.FingerprintUnlockController;
 import com.android.systemui.statusbar.phone.StatusBar;
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
index debda21..cc2244a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
@@ -30,8 +30,6 @@
 
 import com.android.systemui.R;
 
-import java.util.Arrays;
-
 /**
  * Class to store the policy for AOD, which comes from
  * {@link android.provider.Settings.Global}
@@ -102,20 +100,6 @@
         mSettingsObserver.observe();
     }
 
-    private int[] parseIntArray(final String key, final int[] defaultArray) {
-        final String value = mParser.getString(key, null);
-        if (value != null) {
-            try {
-                return Arrays.stream(value.split(":")).map(String::trim).mapToInt(
-                        Integer::parseInt).toArray();
-            } catch (NumberFormatException e) {
-                return defaultArray;
-            }
-        } else {
-            return defaultArray;
-        }
-    }
-
     private final class SettingsObserver extends ContentObserver {
         private final Uri ALWAYS_ON_DISPLAY_CONSTANTS_URI
                 = Settings.Global.getUriFor(Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS);
@@ -154,10 +138,10 @@
                         DEFAULT_PROX_COOLDOWN_TRIGGER_MS);
                 proxCooldownPeriodMs = mParser.getLong(KEY_PROX_COOLDOWN_PERIOD_MS,
                         DEFAULT_PROX_COOLDOWN_PERIOD_MS);
-                screenBrightnessArray = parseIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY,
+                screenBrightnessArray = mParser.getIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY,
                         resources.getIntArray(
                                 R.array.config_doze_brightness_sensor_to_brightness));
-                dimmingScrimArray = parseIntArray(KEY_DIMMING_SCRIM_ARRAY,
+                dimmingScrimArray = mParser.getIntArray(KEY_DIMMING_SCRIM_ARRAY,
                         resources.getIntArray(
                                 R.array.config_doze_brightness_sensor_to_scrim_opacity));
             }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 00e8b1a..3b54e11 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManager;
 import android.app.Dialog;
 import android.app.WallpaperManager;
+import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -112,11 +113,13 @@
     private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
     private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
     private static final String GLOBAL_ACTION_KEY_RESTART = "restart";
+    private static final String GLOBAL_ACTION_KEY_LOGOUT = "logout";
 
     private final Context mContext;
     private final GlobalActionsManager mWindowManagerFuncs;
     private final AudioManager mAudioManager;
     private final IDreamManager mDreamManager;
+    private final DevicePolicyManager mDevicePolicyManager;
 
     private ArrayList<Action> mItems;
     private ActionsDialog mDialog;
@@ -132,6 +135,7 @@
     private boolean mIsWaitingForEcmExit = false;
     private boolean mHasTelephony;
     private boolean mHasVibrator;
+    private boolean mHasLogoutButton;
     private final boolean mShowSilentToggle;
     private final EmergencyAffordanceManager mEmergencyAffordanceManager;
 
@@ -144,6 +148,8 @@
         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
         mDreamManager = IDreamManager.Stub.asInterface(
                 ServiceManager.getService(DreamService.DREAM_SERVICE));
+        mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -289,6 +295,7 @@
                 R.array.config_globalActionsList);
 
         ArraySet<String> addedKeys = new ArraySet<String>();
+        mHasLogoutButton = false;
         for (int i = 0; i < defaultActions.length; i++) {
             String actionKey = defaultActions[i];
             if (addedKeys.contains(actionKey)) {
@@ -325,6 +332,12 @@
                 mItems.add(getAssistAction());
             } else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
                 mItems.add(new RestartAction());
+            } else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
+                if (mDevicePolicyManager.isLogoutButtonEnabled()
+                        && getCurrentUser().id != UserHandle.USER_SYSTEM) {
+                    mItems.add(new LogoutAction());
+                    mHasLogoutButton = true;
+                }
             } else {
                 Log.e(TAG, "Invalid global action key " + actionKey);
             }
@@ -490,6 +503,37 @@
         }
     }
 
+    private final class LogoutAction extends SinglePressAction {
+        private LogoutAction() {
+            super(R.drawable.ic_logout, R.string.global_action_logout);
+        }
+
+        @Override
+        public boolean showDuringKeyguard() {
+            return true;
+        }
+
+        @Override
+        public boolean showBeforeProvisioning() {
+            return false;
+        }
+
+        @Override
+        public void onPress() {
+            // Add a little delay before executing, to give the dialog a chance to go away before
+            // switching user
+            mHandler.postDelayed(() -> {
+                try {
+                    ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
+                    ActivityManager.getService().stopUser(getCurrentUser().id, true /*force*/,
+                            null);
+                } catch (RemoteException re) {
+                    Log.e(TAG, "Couldn't logout user " + re);
+                }
+            }, 500);
+        }
+    }
+
     private Action getSettingsAction() {
         return new SinglePressAction(R.drawable.ic_settings,
                 R.string.global_action_settings) {
@@ -764,7 +808,10 @@
         public View getView(int position, View convertView, ViewGroup parent) {
             Action action = getItem(position);
             View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
-            if (position == 2) {
+            // When there is no logout button, only power off and restart should be in white
+            // background, thus setting division view at third item; with logout button being the
+            // third item, set the division view at fourth item instead.
+            if (position == (mHasLogoutButton ? 3 : 2)) {
                 HardwareUiLayout.get(parent).setDivisionView(view);
             }
             return view;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c92acd0..1faf981 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -25,7 +25,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
 
-import android.app.Activity;
+
 import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.app.NotificationManager;
@@ -53,7 +53,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.provider.Settings.System;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.EventLog;
@@ -77,20 +76,16 @@
 import com.android.keyguard.KeyguardSecurityView;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.keyguard.LatencyTracker;
+import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.Dependency;
 import com.android.systemui.SystemUI;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.classifier.FalsingManager;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.statusbar.phone.FingerprintUnlockController;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarWindowManager;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 51175d1..2b48e0f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -387,9 +387,7 @@
             }
             case MotionEvent.ACTION_HOVER_ENTER:
             case MotionEvent.ACTION_HOVER_MOVE: {
-                if (mAccessibilityManager.isObservedEventType(
-                                AccessibilityEvent.TYPE_VIEW_HOVER_ENTER)
-                        && !mSendingHoverAccessibilityEvents) {
+                if (mAccessibilityManager.isEnabled() && !mSendingHoverAccessibilityEvents) {
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
                     event.setImportantForAccessibility(true);
@@ -402,9 +400,7 @@
                 break;
             }
             case MotionEvent.ACTION_HOVER_EXIT: {
-                if (mAccessibilityManager.isObservedEventType(
-                                AccessibilityEvent.TYPE_VIEW_HOVER_EXIT)
-                        && mSendingHoverAccessibilityEvents) {
+                if (mAccessibilityManager.isEnabled() && mSendingHoverAccessibilityEvents) {
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
                     event.setImportantForAccessibility(true);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 5ffd785..8d50d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -45,6 +45,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.settingslib.Utils;
 import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.systemui.Dependency;
@@ -431,7 +432,7 @@
     @Override
     public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
         if (picture != null &&
-                UserManager.get(mContext).isGuestUser(ActivityManager.getCurrentUser()) &&
+                UserManager.get(mContext).isGuestUser(KeyguardUpdateMonitor.getCurrentUser()) &&
                 !(picture instanceof UserIconDrawable)) {
             picture = picture.getConstantState().newDrawable(mContext.getResources()).mutate();
             picture.setColorFilter(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 4a91ee0..cdf0c0f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -63,6 +63,7 @@
     private QSContainerImpl mContainer;
     private int mLayoutDirection;
     private QSFooter mFooter;
+    private float mLastQSExpansion = -1;
 
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -227,6 +228,7 @@
     public void setKeyguardShowing(boolean keyguardShowing) {
         if (DEBUG) Log.d(TAG, "setKeyguardShowing " + keyguardShowing);
         mKeyguardShowing = keyguardShowing;
+        mLastQSExpansion = -1;
 
         if (mQSAnimator != null) {
             mQSAnimator.setOnKeyguard(keyguardShowing);
@@ -268,6 +270,10 @@
             getView().setTranslationY(mKeyguardShowing ? (translationScaleY * height)
                     : headerTranslation);
         }
+        if (expansion == mLastQSExpansion) {
+            return;
+        }
+        mLastQSExpansion = expansion;
         mHeader.setExpansion(mKeyguardShowing ? 1 : expansion);
         mFooter.setExpansion(mKeyguardShowing ? 1 : expansion);
         int heightDiff = mQSPanel.getBottom() - mHeader.getBottom() + mHeader.getPaddingBottom()
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 9aecc68..ca9a553 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -46,7 +46,7 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.keyguard.LatencyTracker;
+import com.android.internal.util.LatencyTracker;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index b3d6e32..db19d2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -80,10 +80,21 @@
     private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
         @Override
         public void getOutline(View view, Outline outline) {
-            Path clipPath = getClipPath();
-            if (clipPath != null && clipPath.isConvex()) {
-                // The path might not be convex in border cases where the view is small and clipped
-                outline.setConvexPath(clipPath);
+            if (!mCustomOutline && mCurrentTopRoundness == 0.0f
+                    && mCurrentBottomRoundness == 0.0f && !mAlwaysRoundBothCorners) {
+                int translation = mShouldTranslateContents ? (int) getTranslation() : 0;
+                int left = Math.max(translation + mCurrentSidePaddings, mCurrentSidePaddings);
+                int top = mClipTopAmount + mBackgroundTop;
+                int right = getWidth() - mCurrentSidePaddings + Math.min(translation, 0);
+                int bottom = Math.max(getActualHeight() - mClipBottomAmount, top);
+                outline.setRect(left, top, right, bottom);
+            } else {
+                Path clipPath = getClipPath();
+                if (clipPath != null && clipPath.isConvex()) {
+                    // The path might not be convex in border cases where the view is small and
+                    // clipped
+                    outline.setConvexPath(clipPath);
+                }
             }
             outline.setAlpha(mOutlineAlpha);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
new file mode 100644
index 0000000..6bcd174
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -0,0 +1,129 @@
+/*
+ * 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.statusbar;
+
+import static com.android.systemui.statusbar.RemoteInputController.processForRemoteInput;
+import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
+import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_CHILD_NOTIFICATIONS;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
+
+/**
+ * This class handles listening to notification updates and passing them along to
+ * NotificationPresenter to be displayed to the user.
+ */
+public class NotificationListener extends NotificationListenerWithPlugins {
+    private static final String TAG = "NotificationListener";
+
+    private final NotificationPresenter mPresenter;
+    private final Context mContext;
+
+    public NotificationListener(NotificationPresenter presenter, Context context) {
+        mPresenter = presenter;
+        mContext = context;
+    }
+
+    @Override
+    public void onListenerConnected() {
+        if (DEBUG) Log.d(TAG, "onListenerConnected");
+        onPluginConnected();
+        final StatusBarNotification[] notifications = getActiveNotifications();
+        if (notifications == null) {
+            Log.w(TAG, "onListenerConnected unable to get active notifications.");
+            return;
+        }
+        final RankingMap currentRanking = getCurrentRanking();
+        mPresenter.getHandler().post(() -> {
+            for (StatusBarNotification sbn : notifications) {
+                mPresenter.addNotification(sbn, currentRanking);
+            }
+        });
+    }
+
+    @Override
+    public void onNotificationPosted(final StatusBarNotification sbn,
+            final RankingMap rankingMap) {
+        if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
+        if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
+            mPresenter.getHandler().post(() -> {
+                processForRemoteInput(sbn.getNotification(), mContext);
+                String key = sbn.getKey();
+                mPresenter.getKeysKeptForRemoteInput().remove(key);
+                boolean isUpdate = mPresenter.getNotificationData().get(key) != null;
+                // In case we don't allow child notifications, we ignore children of
+                // notifications that have a summary, since` we're not going to show them
+                // anyway. This is true also when the summary is canceled,
+                // because children are automatically canceled by NoMan in that case.
+                if (!ENABLE_CHILD_NOTIFICATIONS
+                        && mPresenter.getGroupManager().isChildInGroupWithSummary(sbn)) {
+                    if (DEBUG) {
+                        Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
+                    }
+
+                    // Remove existing notification to avoid stale data.
+                    if (isUpdate) {
+                        mPresenter.removeNotification(key, rankingMap);
+                    } else {
+                        mPresenter.getNotificationData().updateRanking(rankingMap);
+                    }
+                    return;
+                }
+                if (isUpdate) {
+                    mPresenter.updateNotification(sbn, rankingMap);
+                } else {
+                    mPresenter.addNotification(sbn, rankingMap);
+                }
+            });
+        }
+    }
+
+    @Override
+    public void onNotificationRemoved(StatusBarNotification sbn,
+            final RankingMap rankingMap) {
+        if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
+        if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
+            final String key = sbn.getKey();
+            mPresenter.getHandler().post(() -> mPresenter.removeNotification(key, rankingMap));
+        }
+    }
+
+    @Override
+    public void onNotificationRankingUpdate(final RankingMap rankingMap) {
+        if (DEBUG) Log.d(TAG, "onRankingUpdate");
+        if (rankingMap != null) {
+            RankingMap r = onPluginRankingUpdate(rankingMap);
+            mPresenter.getHandler().post(() -> mPresenter.updateNotificationRanking(r));
+        }
+    }
+
+    public void register() {
+        try {
+            registerAsSystemService(mContext,
+                    new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
+                    UserHandle.USER_ALL);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to register notification listener", e);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index 8670887..4eca241 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -19,6 +19,8 @@
 import android.os.Handler;
 import android.service.notification.NotificationListenerService;
 
+import java.util.Set;
+
 /**
  * An abstraction of something that presents notifications, e.g. StatusBar. Contains methods
  * for both querying the state of the system (some modularised piece of functionality may
@@ -26,7 +28,8 @@
  * for affecting the state of the system (e.g. starting an intent, given that the presenter may
  * want to perform some action before doing so).
  */
-public interface NotificationPresenter {
+public interface NotificationPresenter extends NotificationUpdateHandler,
+        NotificationData.Environment {
 
     /**
      * Returns true if the presenter is not visible. For example, it may not be necessary to do
@@ -66,12 +69,6 @@
      */
     void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation);
 
-    // TODO: Create NotificationUpdateHandler and move this method to there.
-    /**
-     * Removes a notification.
-     */
-    void removeNotification(String key, NotificationListenerService.RankingMap ranking);
-
     // TODO: Create NotificationEntryManager and move this method to there.
     /**
      * Gets the latest ranking map.
@@ -84,6 +81,14 @@
     void onWorkChallengeChanged();
 
     /**
+     * Notifications in this set are kept around when they were canceled in response to a remote
+     * input interaction. This allows us to show what you replied and allows you to continue typing
+     * into it.
+     */
+    // TODO: Create NotificationEntryManager and move this method to there.
+    Set<String> getKeysKeptForRemoteInput();
+
+    /**
      * Called when the current user changes.
      * @param newUserId new user id
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index b7a00eb..75321fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -82,9 +82,6 @@
     private boolean mNoAnimationsInThisFrame;
     private boolean mAnimationsEnabled = true;
     private boolean mShowNotificationShelf;
-    private boolean mVibrationOnAnimation;
-    private boolean mUserTouchingScreen;
-    private boolean mTouchActive;
     private float mFirstElementRoundness;
 
     public NotificationShelf(Context context, AttributeSet attrs) {
@@ -102,9 +99,6 @@
         setClipChildren(false);
         setClipToPadding(false);
         mShelfIcons.setShowAllIcons(false);
-        mVibrationOnAnimation = mContext.getResources().getBoolean(
-                R.bool.config_vibrateOnIconAnimation);
-        updateVibrationOnAnimation();
         mViewInvertHelper = new ViewInvertHelper(mShelfIcons,
                 NotificationPanelView.DOZE_ANIMATION_DURATION);
         mShelfState = new ShelfState();
@@ -112,15 +106,6 @@
         initDimens();
     }
 
-    private void updateVibrationOnAnimation() {
-        mShelfIcons.setVibrateOnAnimation(mVibrationOnAnimation && mTouchActive);
-    }
-
-    public void setTouchActive(boolean touchActive) {
-        mTouchActive = touchActive;
-        updateVibrationOnAnimation();
-    }
-
     public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout) {
         mAmbientState = ambientState;
         mHostLayout = hostLayout;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
index 492ab44..aea0127 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
@@ -16,7 +16,6 @@
  */
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
@@ -234,7 +233,7 @@
 
         final int defaultSnooze = mParser.getInt(KEY_DEFAULT_SNOOZE,
                 resources.getInteger(R.integer.config_notification_snooze_time_default));
-        final int[] snoozeTimes = parseIntArray(KEY_OPTIONS,
+        final int[] snoozeTimes = mParser.getIntArray(KEY_OPTIONS,
                 resources.getIntArray(R.array.config_notification_snooze_times));
 
         for (int i = 0; i < snoozeTimes.length && i < sAccessibilityActions.length; i++) {
@@ -248,21 +247,6 @@
         return options;
     }
 
-    @VisibleForTesting
-    int[] parseIntArray(final String key, final int[] defaultArray) {
-        final String value = mParser.getString(key, null);
-        if (value != null) {
-            try {
-                return Arrays.stream(value.split(":")).map(String::trim).mapToInt(
-                        Integer::parseInt).toArray();
-            } catch (NumberFormatException e) {
-                return defaultArray;
-            }
-        } else {
-            return defaultArray;
-        }
-    }
-
     private SnoozeOption createOption(int minutes, int accessibilityActionId) {
         Resources res = getResources();
         boolean showInHours = minutes >= 60;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUpdateHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUpdateHandler.java
new file mode 100644
index 0000000..0044194
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUpdateHandler.java
@@ -0,0 +1,58 @@
+/*
+ * 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.statusbar;
+
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+
+/**
+ * Interface for accepting notification updates from {@link NotificationListener}.
+ */
+public interface NotificationUpdateHandler {
+    /**
+     * Add a new notification and update the current notification ranking map.
+     *
+     * @param notification Notification to add
+     * @param ranking RankingMap to update with
+     */
+    void addNotification(StatusBarNotification notification,
+            NotificationListenerService.RankingMap ranking);
+
+    /**
+     * Remove a notification and update the current notification ranking map.
+     *
+     * @param key Key identifying the notification to remove
+     * @param ranking RankingMap to update with
+     */
+    void removeNotification(String key, NotificationListenerService.RankingMap ranking);
+
+    /**
+     * Update a given notification and the current notification ranking map.
+     *
+     * @param notification Updated notification
+     * @param ranking RankingMap to update with
+     */
+    void updateNotification(StatusBarNotification notification,
+            NotificationListenerService.RankingMap ranking);
+
+    /**
+     * Update with a new notification ranking map.
+     *
+     * @param ranking RankingMap to update with
+     */
+    void updateNotificationRanking(NotificationListenerService.RankingMap ranking);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index ff6c775..97e3d22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -21,17 +21,24 @@
 import com.android.systemui.statusbar.phone.StatusBarWindowManager;
 import com.android.systemui.statusbar.policy.RemoteInputView;
 
+import android.app.Notification;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.os.SystemProperties;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Pair;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Keeps track of the currently active {@link RemoteInputView}s.
  */
 public class RemoteInputController {
+    private static final boolean ENABLE_REMOTE_INPUT =
+            SystemProperties.getBoolean("debug.enable_remote_input", true);
 
     private final ArrayList<Pair<WeakReference<NotificationData.Entry>, Object>> mOpen
             = new ArrayList<>();
@@ -45,6 +52,53 @@
     }
 
     /**
+     * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this
+     * via first-class API.
+     *
+     * TODO: Remove once enough apps specify remote inputs on their own.
+     */
+    public static void processForRemoteInput(Notification n, Context context) {
+        if (!ENABLE_REMOTE_INPUT) {
+            return;
+        }
+
+        if (n.extras != null && n.extras.containsKey("android.wearable.EXTENSIONS") &&
+                (n.actions == null || n.actions.length == 0)) {
+            Notification.Action viableAction = null;
+            Notification.WearableExtender we = new Notification.WearableExtender(n);
+
+            List<Notification.Action> actions = we.getActions();
+            final int numActions = actions.size();
+
+            for (int i = 0; i < numActions; i++) {
+                Notification.Action action = actions.get(i);
+                if (action == null) {
+                    continue;
+                }
+                RemoteInput[] remoteInputs = action.getRemoteInputs();
+                if (remoteInputs == null) {
+                    continue;
+                }
+                for (RemoteInput ri : remoteInputs) {
+                    if (ri.getAllowFreeFormInput()) {
+                        viableAction = action;
+                        break;
+                    }
+                }
+                if (viableAction != null) {
+                    break;
+                }
+            }
+
+            if (viableAction != null) {
+                Notification.Builder rebuilder = Notification.Builder.recoverBuilder(context, n);
+                rebuilder.setActions(viableAction);
+                rebuilder.build(); // will rewrite n
+            }
+        }
+    }
+
+    /**
      * Adds a currently active remote input.
      *
      * @param entry the entry for which a remote input is now active.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index af393c9..7e2336c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -61,11 +61,6 @@
         return sLocationOffset[1] - sLocationBase[1];
     }
 
-    public static boolean isHapticFeedbackDisabled(Context context) {
-        return Settings.System.getIntForUser(context.getContentResolver(),
-                Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) == 0;
-    }
-
     /**
      * @param dimenId the dimen to look up
      * @return the font scaled dimen as if it were in sp but doesn't shrink sizes below dp
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index 80d4061..a2b1013 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -26,7 +26,7 @@
 import com.android.keyguard.KeyguardConstants;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.keyguard.LatencyTracker;
+import com.android.internal.util.LatencyTracker;
 import com.android.systemui.Dependency;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.ScreenLifecycle;
@@ -266,7 +266,6 @@
     }
 
     private void showBouncer() {
-        mScrimController.transitionTo(ScrimState.BOUNCER);
         mStatusBarKeyguardViewManager.animateCollapsePanels(
                 FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
         mPendingShowBouncer = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 99debee..c281379 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -102,7 +102,7 @@
             return;
         }
 
-        final int activeUserId = ActivityManager.getCurrentUser();
+        final int activeUserId = KeyguardUpdateMonitor.getCurrentUser();
         final boolean isSystemUser =
                 UserManager.isSplitSystemUser() && activeUserId == UserHandle.USER_SYSTEM;
         final boolean allowDismissKeyguard = !isSystemUser && activeUserId == keyguardUserId;
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 6d3bc1d..61b007f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -67,7 +67,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.keyguard.LatencyTracker;
+import com.android.internal.util.LatencyTracker;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 836efff..a1b49c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.statusbar.notification.NotificationUtils.isHapticFeedbackDisabled;
-
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Canvas;
@@ -120,8 +118,6 @@
     private float mVisualOverflowAdaption;
     private boolean mDisallowNextAnimation;
     private boolean mAnimationsEnabled = true;
-    private boolean mVibrateOnAnimation;
-    private Vibrator mVibrator;
     private ArrayMap<String, ArrayList<StatusBarIcon>> mReplacingIcons;
     private int mDarkOffsetX;
 
@@ -129,7 +125,6 @@
         super(context, attrs);
         initDimens();
         setWillNotDraw(!DEBUG);
-        mVibrator = mContext.getSystemService(Vibrator.class);
     }
 
     private void initDimens() {
@@ -497,10 +492,6 @@
         return width - (getWidth() - getActualPaddingStart() - getActualPaddingEnd()) > 0;
     }
 
-    public void setVibrateOnAnimation(boolean vibrateOnAnimation) {
-        mVibrateOnAnimation = vibrateOnAnimation;
-    }
-
     public int getIconSize() {
         return mIconSize;
     }
@@ -608,39 +599,14 @@
                 } else {
                     super.applyToView(view);
                 }
-                boolean wasInShelf = icon.isInShelf();
                 boolean inShelf = iconAppearAmount == 1.0f;
                 icon.setIsInShelf(inShelf);
-                if (shouldVibrateChange(wasInShelf != inShelf)) {
-                    AsyncTask.execute(
-                            () -> mVibrator.vibrate(VibrationEffect.get(
-                                    VibrationEffect.EFFECT_TICK)));
-                }
             }
             justAdded = false;
             justReplaced = false;
             needsCannedAnimation = false;
         }
 
-        private boolean shouldVibrateChange(boolean inShelfChanged) {
-            if (!mVibrateOnAnimation) {
-                return false;
-            }
-            if (justAdded) {
-                return false;
-            }
-            if (!mAnimationsEnabled) {
-                return false;
-            }
-            if (!inShelfChanged) {
-                return false;
-            }
-            if (isHapticFeedbackDisabled(mContext)) {
-                return false;
-            }
-            return true;
-        }
-
         public boolean hasCustomTransformHeight() {
             return isLastExpandIcon && customTransformHeight != NO_VALUE;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index afe5c91..2fc22ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.statusbar.notification.NotificationUtils.isHapticFeedbackDisabled;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -25,7 +23,9 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.os.AsyncTask;
+import android.os.Handler;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.VibrationEffect;
@@ -42,7 +42,7 @@
 import android.widget.FrameLayout;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.keyguard.LatencyTracker;
+import com.android.internal.util.LatencyTracker;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -50,7 +50,6 @@
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.io.FileDescriptor;
@@ -66,6 +65,7 @@
     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
     private boolean mPanelUpdateWhenAnimatorEnds;
     private boolean mVibrateOnOpening;
+    private boolean mVibrationEnabled;
 
     private final void logf(String fmt, Object... args) {
         Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
@@ -108,6 +108,12 @@
     private FlingAnimationUtils mFlingAnimationUtilsDismissing;
     private FalsingManager mFalsingManager;
     private final Vibrator mVibrator;
+    final private ContentObserver mVibrationObserver = new ContentObserver(Handler.getMain()) {
+        @Override
+        public void onChange(boolean selfChange) {
+            updateHapticFeedBackEnabled();
+        }
+    };
 
     /**
      * Whether an instant expand request is currently pending and we are just waiting for layout.
@@ -212,6 +218,15 @@
         mVibrator = mContext.getSystemService(Vibrator.class);
         mVibrateOnOpening = mContext.getResources().getBoolean(
                 R.bool.config_vibrateOnIconAnimation);
+        mContext.getContentResolver().registerContentObserver(
+                Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_ENABLED), true,
+                mVibrationObserver);
+        mVibrationObserver.onChange(false /* selfChange */);
+    }
+
+    public void updateHapticFeedBackEnabled() {
+        mVibrationEnabled = Settings.System.getIntForUser(mContext.getContentResolver(),
+                Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) != 0;
     }
 
     protected void loadDimens() {
@@ -403,7 +418,7 @@
         runPeekAnimation(INITIAL_OPENING_PEEK_DURATION, getOpeningHeight(),
                 false /* collapseWhenFinished */);
         notifyBarPanelExpansionChanged();
-        if (mVibrateOnOpening && !isHapticFeedbackDisabled(mContext)) {
+        if (mVibrateOnOpening && mVibrationEnabled) {
             AsyncTask.execute(() ->
                     mVibrator.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_TICK, false)));
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 09fe579..f41cb29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -615,6 +615,7 @@
                     .addCategory(Intent.CATEGORY_BROWSABLE)
                     .addCategory("unique:" + System.currentTimeMillis())
                     .putExtra(Intent.EXTRA_PACKAGE_NAME, appInfo.packageName)
+                    .putExtra(Intent.EXTRA_VERSION_CODE, (int) (appInfo.versionCode & 0x7fffffff))
                     .putExtra(Intent.EXTRA_VERSION_CODE, appInfo.versionCode)
                     .putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent);
 
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 7c33437..80bab72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -200,6 +200,7 @@
 import com.android.systemui.statusbar.NotificationData.Entry;
 import com.android.systemui.statusbar.NotificationGutsManager;
 import com.android.systemui.statusbar.NotificationInfo;
+import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationPresenter;
@@ -249,6 +250,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.Stack;
 
 public class StatusBar extends SystemUI implements DemoMode,
@@ -649,15 +651,6 @@
             .Callback() {
         @Override
         public void onFinished() {
-            notifyKeyguardState();
-        }
-
-        @Override
-        public void onCancelled() {
-            notifyKeyguardState();
-        }
-
-        private void notifyKeyguardState() {
             if (mStatusBarKeyguardViewManager == null) {
                 Log.w(TAG, "Tried to notify keyguard visibility when "
                         + "mStatusBarKeyguardViewManager was null");
@@ -665,6 +658,12 @@
             }
             mStatusBarKeyguardViewManager.onKeyguardFadedAway();
         }
+
+        @Override
+        public void onCancelled() {
+            // Transition was cancelled because another one took over.
+            // Nothing to do in here but wait.
+        }
     };
 
     private NotificationMessagingUtil mMessagingUtil;
@@ -819,14 +818,8 @@
         }
 
         // Set up the initial notification state.
-        try {
-            mNotificationListener.registerAsSystemService(mContext,
-                    new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
-                    UserHandle.USER_ALL);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Unable to register notification listener", e);
-        }
-
+        mNotificationListener = new NotificationListener(this, mContext);
+        mNotificationListener.register();
 
         if (DEBUG) {
             Log.d(TAG, String.format(
@@ -1519,13 +1512,19 @@
         SystemServicesProxy.getInstance(mContext).awakenDreamsAsync();
     }
 
-    public void addNotification(StatusBarNotification notification, RankingMap ranking)
-            throws InflationException {
+    @Override
+    public void addNotification(StatusBarNotification notification, RankingMap ranking) {
         String key = notification.getKey();
         if (DEBUG) Log.d(TAG, "addNotification key=" + key);
 
         mNotificationData.updateRanking(ranking);
-        Entry shadeEntry = createNotificationViews(notification);
+        Entry shadeEntry = null;
+        try {
+            shadeEntry = createNotificationViews(notification);
+        } catch (InflationException e) {
+            handleInflationException(notification, e);
+            return;
+        }
         boolean isHeadsUped = shouldPeek(shadeEntry);
         if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
             if (shouldSuppressFullScreenIntent(key)) {
@@ -1539,11 +1538,11 @@
                             + key);
                 }
             } else {
-                // Stop screensaver if the notification has a full-screen intent.
+                // Stop screensaver if the notification has a fullscreen intent.
                 // (like an incoming phone call)
                 awakenDreams();
 
-                // not immersive & a full-screen alert should be shown
+                // not immersive & a fullscreen alert should be shown
                 if (DEBUG)
                     Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
                 try {
@@ -1620,7 +1619,8 @@
         }
     }
 
-    protected void updateNotificationRanking(RankingMap ranking) {
+    @Override
+    public void updateNotificationRanking(RankingMap ranking) {
         mNotificationData.updateRanking(ranking);
         updateNotifications();
     }
@@ -1673,7 +1673,7 @@
                     newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
             boolean updated = false;
             try {
-                updateNotification(newSbn, null);
+                updateNotificationInternal(newSbn, null);
                 updated = true;
             } catch (InflationException e) {
                 deferRemoval = false;
@@ -3697,23 +3697,35 @@
      * See also StatusBar.setPanelExpanded for another place where we attempt to do this.
      */
     private void handleVisibleToUserChangedImpl(boolean visibleToUser) {
-        try {
-            if (visibleToUser) {
-                boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
-                boolean clearNotificationEffects =
-                        !isPresenterFullyCollapsed() &&
-                        (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED);
-                int notificationLoad = mNotificationData.getActiveNotifications().size();
-                if (pinnedHeadsUp && isPresenterFullyCollapsed())  {
-                    notificationLoad = 1;
-                }
-                mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad);
-            } else {
-                mBarService.onPanelHidden();
+        if (visibleToUser) {
+            boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
+            boolean clearNotificationEffects =
+                    !isPresenterFullyCollapsed() &&
+                            (mState == StatusBarState.SHADE
+                                    || mState == StatusBarState.SHADE_LOCKED);
+            int notificationLoad = mNotificationData.getActiveNotifications().size();
+            if (pinnedHeadsUp && isPresenterFullyCollapsed()) {
+                notificationLoad = 1;
             }
-        } catch (RemoteException ex) {
-            // Won't fail unless the world has ended.
+            final int finalNotificationLoad = notificationLoad;
+            mUiOffloadThread.submit(() -> {
+                try {
+                    mBarService.onPanelRevealed(clearNotificationEffects,
+                            finalNotificationLoad);
+                } catch (RemoteException ex) {
+                    // Won't fail unless the world has ended.
+                }
+            });
+        } else {
+            mUiOffloadThread.submit(() -> {
+                try {
+                    mBarService.onPanelHidden();
+                } catch (RemoteException ex) {
+                    // Won't fail unless the world has ended.
+                }
+            });
         }
+
     }
 
     private void stopNotificationLogging() {
@@ -3749,21 +3761,23 @@
                 newlyVisible.toArray(new NotificationVisibility[newlyVisible.size()]);
         NotificationVisibility[] noLongerVisibleAr =
                 noLongerVisible.toArray(new NotificationVisibility[noLongerVisible.size()]);
-        try {
-            mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
-        } catch (RemoteException e) {
-            // Ignore.
-        }
-
-        final int N = newlyVisible.size();
-        if (N > 0) {
-            String[] newlyVisibleKeyAr = new String[N];
-            for (int i = 0; i < N; i++) {
-                newlyVisibleKeyAr[i] = newlyVisibleAr[i].key;
+        mUiOffloadThread.submit(() -> {
+            try {
+                mBarService.onNotificationVisibilityChanged(newlyVisibleAr, noLongerVisibleAr);
+            } catch (RemoteException e) {
+                // Ignore.
             }
 
-            setNotificationsShown(newlyVisibleKeyAr);
-        }
+            final int N = newlyVisible.size();
+            if (N > 0) {
+                String[] newlyVisibleKeyAr = new String[N];
+                for (int i = 0; i < N; i++) {
+                    newlyVisibleKeyAr[i] = newlyVisibleAr[i].key;
+                }
+
+                setNotificationsShown(newlyVisibleKeyAr);
+            }
+        });
     }
 
     // State logging
@@ -5112,6 +5126,7 @@
 
     public void notifyFpAuthModeChanged() {
         updateDozing();
+        updateScrimController();
     }
 
     private void updateDozing() {
@@ -5693,90 +5708,7 @@
         }
     };
 
-    private final NotificationListenerWithPlugins mNotificationListener =
-            new NotificationListenerWithPlugins() {
-        @Override
-        public void onListenerConnected() {
-            if (DEBUG) Log.d(TAG, "onListenerConnected");
-            onPluginConnected();
-            final StatusBarNotification[] notifications = getActiveNotifications();
-            if (notifications == null) {
-                Log.w(TAG, "onListenerConnected unable to get active notifications.");
-                return;
-            }
-            final RankingMap currentRanking = getCurrentRanking();
-            mHandler.post(() -> {
-                for (StatusBarNotification sbn : notifications) {
-                    try {
-                        addNotification(sbn, currentRanking);
-                    } catch (InflationException e) {
-                        handleInflationException(sbn, e);
-                    }
-                }
-            });
-        }
-
-        @Override
-        public void onNotificationPosted(final StatusBarNotification sbn,
-                final RankingMap rankingMap) {
-            if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
-            if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
-                mHandler.post(() -> {
-                    processForRemoteInput(sbn.getNotification());
-                    String key = sbn.getKey();
-                    mKeysKeptForRemoteInput.remove(key);
-                    boolean isUpdate = mNotificationData.get(key) != null;
-                    // In case we don't allow child notifications, we ignore children of
-                    // notifications that have a summary, since we're not going to show them
-                    // anyway. This is true also when the summary is canceled,
-                    // because children are automatically canceled by NoMan in that case.
-                    if (!ENABLE_CHILD_NOTIFICATIONS
-                            && mGroupManager.isChildInGroupWithSummary(sbn)) {
-                        if (DEBUG) {
-                            Log.d(TAG, "Ignoring group child due to existing summary: " + sbn);
-                        }
-
-                        // Remove existing notification to avoid stale data.
-                        if (isUpdate) {
-                            removeNotification(key, rankingMap);
-                        } else {
-                            mNotificationData.updateRanking(rankingMap);
-                        }
-                        return;
-                    }
-                    try {
-                        if (isUpdate) {
-                            updateNotification(sbn, rankingMap);
-                        } else {
-                            addNotification(sbn, rankingMap);
-                        }
-                    } catch (InflationException e) {
-                        handleInflationException(sbn, e);
-                    }
-                });
-            }
-        }
-
-        @Override
-        public void onNotificationRemoved(StatusBarNotification sbn,
-                final RankingMap rankingMap) {
-            if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
-            if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
-                final String key = sbn.getKey();
-                mHandler.post(() -> removeNotification(key, rankingMap));
-            }
-        }
-
-        @Override
-        public void onNotificationRankingUpdate(final RankingMap rankingMap) {
-            if (DEBUG) Log.d(TAG, "onRankingUpdate");
-            if (rankingMap != null) {
-                RankingMap r = onPluginRankingUpdate(rankingMap);
-                mHandler.post(() -> updateNotificationRanking(r));
-            }
-        }
-
-    };
+    protected NotificationListener mNotificationListener;
 
     protected void notifyUserAboutHiddenNotifications() {
         if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
@@ -6063,51 +5995,6 @@
         row.updateNotification(entry);
     }
 
-    /**
-     * Adds RemoteInput actions from the WearableExtender; to be removed once more apps support this
-     * via first-class API.
-     *
-     * TODO: Remove once enough apps specify remote inputs on their own.
-     */
-    private void processForRemoteInput(Notification n) {
-        if (!ENABLE_REMOTE_INPUT) return;
-
-        if (n.extras != null && n.extras.containsKey("android.wearable.EXTENSIONS") &&
-                (n.actions == null || n.actions.length == 0)) {
-            Notification.Action viableAction = null;
-            Notification.WearableExtender we = new Notification.WearableExtender(n);
-
-            List<Notification.Action> actions = we.getActions();
-            final int numActions = actions.size();
-
-            for (int i = 0; i < numActions; i++) {
-                Notification.Action action = actions.get(i);
-                if (action == null) {
-                    continue;
-                }
-                RemoteInput[] remoteInputs = action.getRemoteInputs();
-                if (remoteInputs == null) {
-                    continue;
-                }
-                for (RemoteInput ri : remoteInputs) {
-                    if (ri.getAllowFreeFormInput()) {
-                        viableAction = action;
-                        break;
-                    }
-                }
-                if (viableAction != null) {
-                    break;
-                }
-            }
-
-            if (viableAction != null) {
-                Notification.Builder rebuilder = Notification.Builder.recoverBuilder(mContext, n);
-                rebuilder.setActions(viableAction);
-                rebuilder.build(); // will rewrite n
-            }
-        }
-    }
-
     public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
         if (!isDeviceProvisioned()) return;
 
@@ -6496,8 +6383,9 @@
         mScrimController.setNotificationCount(mStackScroller.getNotGoneChildCount());
     }
 
-    public void updateNotification(StatusBarNotification notification, RankingMap ranking)
-            throws InflationException {
+    // TODO: Move this to NotificationEntryManager once it is created.
+    private void updateNotificationInternal(StatusBarNotification notification,
+            RankingMap ranking) throws InflationException {
         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
 
         final String key = notification.getKey();
@@ -6547,6 +6435,15 @@
         setAreThereNotifications();
     }
 
+    @Override
+    public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
+        try {
+            updateNotificationInternal(notification, ranking);
+        } catch (InflationException e) {
+            handleInflationException(notification, e);
+        }
+    }
+
     protected void notifyHeadsUpGoingToSleep() {
         maybeEscalateHeadsUp();
     }
@@ -6743,6 +6640,11 @@
         return mLatestRankingMap;
     }
 
+    @Override
+    public Set<String> getKeysKeptForRemoteInput() {
+        return mKeysKeptForRemoteInput;
+    }
+
     private final NotificationInfo.CheckSaveListener mCheckSaveListener =
             (Runnable saveImportance, StatusBarNotification sbn) -> {
                 // If the user has security enabled, show challenge if the setting is changed.
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 ef05bbb..dacd3d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -33,7 +33,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.keyguard.LatencyTracker;
+import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dependency;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index d7f11f7..4accd86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -248,7 +248,6 @@
 
     public void setTouchActive(boolean touchActive) {
         mTouchActive = touchActive;
-        mStackScrollLayout.setTouchActive(touchActive);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 874f0d9..4ee4ef4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -39,6 +39,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import static com.android.settingslib.Utils.updateLocationMode;
+
 /**
  * A controller to manage changes of location related states and update the views accordingly.
  */
@@ -106,12 +108,13 @@
         final ContentResolver cr = mContext.getContentResolver();
         // When enabling location, a user consent dialog will pop up, and the
         // setting won't be fully enabled until the user accepts the agreement.
+        int currentMode = Settings.Secure.getIntForUser(cr, Settings.Secure.LOCATION_MODE, 
+                Settings.Secure.LOCATION_MODE_OFF, currentUserId);
         int mode = enabled
                 ? Settings.Secure.LOCATION_MODE_PREVIOUS : Settings.Secure.LOCATION_MODE_OFF;
         // QuickSettings always runs as the owner, so specifically set the settings
         // for the current foreground user.
-        return Settings.Secure
-                .putIntForUser(cr, Settings.Secure.LOCATION_MODE, mode, currentUserId);
+        return updateLocationMode(mContext, currentMode, mode, currentUserId);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
index 722874b..f258fb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockController.java
@@ -24,6 +24,7 @@
     boolean isRotationLockAffordanceVisible();
     boolean isRotationLocked();
     void setRotationLocked(boolean locked);
+    void setRotationLockedAtAngle(boolean locked, int rotation);
 
     public interface RotationLockControllerCallback {
         void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
index 4f96496..5418dc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java
@@ -63,6 +63,10 @@
         RotationPolicy.setRotationLock(mContext, locked);
     }
 
+    public void setRotationLockedAtAngle(boolean locked, int rotation){
+        RotationPolicy.setRotationLockAtAngle(mContext, locked, rotation);
+    }
+
     public boolean isRotationLockAffordanceVisible() {
         return RotationPolicy.isRotationLockToggleVisible(mContext);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index a3d2423..0f2a2c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -4443,10 +4443,6 @@
                 mAmbientState.getScrollY()));
     }
 
-    public void setTouchActive(boolean touchActive) {
-        mShelf.setTouchActive(touchActive);
-    }
-
     /**
      * A listener that is notified when some child locations might have changed.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index f16d7b8..0d41e20 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -19,9 +19,6 @@
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
 
-import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
-import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
-import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
 import static com.android.systemui.volume.Events.DISMISS_REASON_TOUCH_OUTSIDE;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
@@ -45,7 +42,6 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
-import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.util.Log;
 import android.util.Slog;
@@ -71,7 +67,6 @@
 
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
-import com.android.systemui.HardwareBgDrawable;
 import com.android.systemui.HardwareUiLayout;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -79,7 +74,6 @@
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.plugins.VolumeDialogController.State;
 import com.android.systemui.plugins.VolumeDialogController.StreamState;
-import com.android.systemui.util.leak.RotationUtils;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -103,13 +97,9 @@
     private final VolumeDialogController mController;
 
     private Window mWindow;
-    //private HardwareUiLayout mHardwareLayout;
+    private HardwareUiLayout mHardwareLayout;
     private CustomDialog mDialog;
     private ViewGroup mDialogView;
-    private boolean mEdgeBleed;
-    private boolean mRoundedDivider;
-    private HardwareBgDrawable mBackground;
-    private int mRotation = ROTATION_NONE;
     private ViewGroup mDialogRowsView;
     private ViewGroup mDialogContentView;
     private final List<VolumeRow> mRows = new ArrayList<>();
@@ -121,8 +111,6 @@
     private final Accessibility mAccessibility = new Accessibility();
     private final ColorStateList mActiveSliderTint;
     private final ColorStateList mInactiveSliderTint;
-    private static final String EDGE_BLEED = "sysui_hwui_edge_bleed";
-    private static final String ROUNDED_DIVIDER = "sysui_hwui_rounded_divider";
 
     private boolean mShowing;
     private boolean mShowA11yStream;
@@ -193,16 +181,8 @@
                 return true;
             }
         });
-
-        mEdgeBleed = Settings.Secure.getInt(mContext.getContentResolver(),
-                EDGE_BLEED, 0) != 0;
-        mRoundedDivider = Settings.Secure.getInt(mContext.getContentResolver(),
-                ROUNDED_DIVIDER, 1) != 0;
-        updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding());
-        mBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed, mContext);
-        mDialogView.setBackground(mBackground);
-        //mHardwareLayout = HardwareUiLayout.get(mDialogView);
-        //mHardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE));
+        mHardwareLayout = HardwareUiLayout.get(mDialogView);
+        mHardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE));
 
         mDialogContentView = mDialog.findViewById(R.id.volume_dialog_content);
         mDialogRowsView = mDialogContentView.findViewById(R.id.volume_dialog_rows);
@@ -230,25 +210,6 @@
         updateRowsH(getActiveRow());
     }
 
-    private int getEdgePadding() {
-        return mContext.getResources().getDimensionPixelSize(R.dimen.edge_margin);
-    }
-
-    private void updateEdgeMargin(int edge) {
-        if (mDialogView != null) {
-            mRotation = RotationUtils.getRotation(mContext);
-            ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mDialogView.getLayoutParams();
-            if (mRotation == ROTATION_LANDSCAPE) {
-                params.topMargin = edge;
-            } else if (mRotation == ROTATION_SEASCAPE) {
-                params.bottomMargin = edge;
-            } else {
-                params.rightMargin = edge;
-            }
-            mDialogView.setLayoutParams(params);
-        }
-    }
-
     private ColorStateList loadColorStateList(int colorResId) {
         return ColorStateList.valueOf(mContext.getColor(colorResId));
     }
@@ -428,11 +389,11 @@
         rescheduleTimeoutH();
         if (mShowing) return;
         mShowing = true;
-        mDialogView.setTranslationY(getAnimTranslation());
-        mDialogView.setAlpha(0);
-        mDialogView.animate()
+        mHardwareLayout.setTranslationX(getAnimTranslation());
+        mHardwareLayout.setAlpha(0);
+        mHardwareLayout.animate()
                 .alpha(1)
-                .translationY(0)
+                .translationX(0)
                 .setDuration(300)
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .withEndAction(() -> {
@@ -471,17 +432,16 @@
         mHandler.removeMessages(H.SHOW);
         if (!mShowing) return;
         mShowing = false;
-        mDialogView.setTranslationX(0);
-        mDialogView.setAlpha(1);
-        mDialogView.animate()
+        mHardwareLayout.setTranslationX(0);
+        mHardwareLayout.setAlpha(1);
+        mHardwareLayout.animate()
                 .alpha(0)
                 .translationX(getAnimTranslation())
                 .setDuration(300)
                 .withEndAction(() -> mDialog.dismiss())
                 .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
                 .start();
-        if (mAccessibilityMgr.isObservedEventType(
-                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)) {
+        if (mAccessibilityMgr.isEnabled()) {
             AccessibilityEvent event =
                     AccessibilityEvent.obtain(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
             event.setPackageName(mContext.getPackageName());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
new file mode 100644
index 0000000..6ecfe3e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.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 com.android.systemui.statusbar;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationListenerTest extends SysuiTestCase {
+    private static final String TEST_PACKAGE_NAME = "test";
+    private static final int TEST_UID = 0;
+
+    private NotificationPresenter mPresenter;
+    private Handler mHandler;
+    private NotificationListener mListener;
+    private StatusBarNotification mSbn;
+    private NotificationListenerService.RankingMap mRanking;
+    private Set<String> mKeysKeptForRemoteInput;
+    private NotificationData mNotificationData;
+
+    @Before
+    public void setUp() {
+        mHandler = new Handler(Looper.getMainLooper());
+        mPresenter = mock(NotificationPresenter.class);
+        mNotificationData = mock(NotificationData.class);
+        mRanking = mock(NotificationListenerService.RankingMap.class);
+        mKeysKeptForRemoteInput = new HashSet<>();
+
+        when(mPresenter.getHandler()).thenReturn(mHandler);
+        when(mPresenter.getNotificationData()).thenReturn(mNotificationData);
+        when(mPresenter.getKeysKeptForRemoteInput()).thenReturn(mKeysKeptForRemoteInput);
+
+        mListener = new NotificationListener(mPresenter, mContext);
+        mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
+                new Notification(), UserHandle.CURRENT, null, 0);
+    }
+
+    @Test
+    public void testNotificationAddCallsAddNotification() {
+        mListener.onNotificationPosted(mSbn, mRanking);
+        waitForIdleSync(mHandler);
+        verify(mPresenter).addNotification(mSbn, mRanking);
+    }
+
+    @Test
+    public void testPostNotificationRemovesKeyKeptForRemoteInput() {
+        mKeysKeptForRemoteInput.add(mSbn.getKey());
+        mListener.onNotificationPosted(mSbn, mRanking);
+        waitForIdleSync(mHandler);
+        assertTrue(mKeysKeptForRemoteInput.isEmpty());
+    }
+
+    @Test
+    public void testNotificationUpdateCallsUpdateNotification() {
+        when(mNotificationData.get(mSbn.getKey())).thenReturn(new NotificationData.Entry(mSbn));
+        mListener.onNotificationPosted(mSbn, mRanking);
+        waitForIdleSync(mHandler);
+        verify(mPresenter).updateNotification(mSbn, mRanking);
+    }
+
+    @Test
+    public void testNotificationRemovalCallsRemoveNotification() {
+        mListener.onNotificationRemoved(mSbn, mRanking);
+        waitForIdleSync(mHandler);
+        verify(mPresenter).removeNotification(mSbn.getKey(), mRanking);
+    }
+
+    @Test
+    public void testRankingUpdateCallsNotificationRankingUpdate() {
+        mListener.onNotificationRankingUpdate(mRanking);
+        waitForIdleSync(mHandler);
+        // RankingMap may be modified by plugins.
+        verify(mPresenter).updateNotificationRanking(any());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java
index 6b31c96..756bb1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java
@@ -62,57 +62,6 @@
     }
 
     @Test
-    public void testParseIntArrayNull() throws Exception {
-        when(mMockParser.getString(anyString(), isNull())).thenReturn(null);
-        mNotificationSnooze.setKeyValueListParser(mMockParser);
-
-        int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
-        assertEquals(RES_OPTIONS, result);
-    }
-
-    @Test
-    public void testParseIntArrayLeadingSep() throws Exception {
-        when(mMockParser.getString(anyString(), isNull())).thenReturn(":4:5:6");
-        mNotificationSnooze.setKeyValueListParser(mMockParser);
-
-        int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
-        assertEquals(RES_OPTIONS, result);
-    }
-
-    @Test
-    public void testParseIntArrayEmptyItem() throws Exception {
-        when(mMockParser.getString(anyString(), isNull())).thenReturn("4::6");
-        mNotificationSnooze.setKeyValueListParser(mMockParser);
-
-        int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
-        assertEquals(RES_OPTIONS, result);
-    }
-
-    @Test
-    public void testParseIntArrayTrailingSep() throws Exception {
-        when(mMockParser.getString(anyString(), isNull())).thenReturn("4:5:6:");
-        mNotificationSnooze.setKeyValueListParser(mMockParser);
-
-        int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
-        assertEquals(3, result.length);
-        assertEquals(4, result[0]);  // respect order
-        assertEquals(5, result[1]);
-        assertEquals(6, result[2]);
-    }
-
-    @Test
-    public void testParseIntArrayGoodData() throws Exception {
-        when(mMockParser.getString(anyString(), isNull())).thenReturn("4:5:6");
-        mNotificationSnooze.setKeyValueListParser(mMockParser);
-
-        int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
-        assertEquals(3, result.length);
-        assertEquals(4, result[0]);  // respect order
-        assertEquals(5, result[1]);
-        assertEquals(6, result[2]);
-    }
-
-    @Test
     public void testGetOptionsWithNoConfig() throws Exception {
         ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
         assertEquals(3, result.size());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 713f7843..5ff90d91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -104,6 +104,7 @@
     PowerManager mPowerManager;
     SystemServicesProxy mSystemServicesProxy;
     NotificationPanelView mNotificationPanelView;
+    ScrimController mScrimController;
     IStatusBarService mBarService;
     ArrayList<Entry> mNotificationList;
     private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
@@ -131,6 +132,7 @@
         mNotificationPanelView = mock(NotificationPanelView.class);
         when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0));
         mNotificationList = mock(ArrayList.class);
+        mScrimController = mock(ScrimController.class);
         IPowerManager powerManagerService = mock(IPowerManager.class);
         HandlerThread handlerThread = new HandlerThread("TestThread");
         handlerThread.start();
@@ -143,7 +145,7 @@
         mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
                 mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
                 mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView,
-                mBarService);
+                mBarService, mScrimController);
         mStatusBar.mContext = mContext;
         mStatusBar.mComponents = mContext.getComponents();
         doAnswer(invocation -> {
@@ -428,6 +430,7 @@
     public void testLogHidden() {
         try {
             mStatusBar.handleVisibleToUserChanged(false);
+            waitForUiOffloadThread();
             verify(mBarService, times(1)).onPanelHidden();
             verify(mBarService, never()).onPanelRevealed(anyBoolean(), anyInt());
         } catch (RemoteException e) {
@@ -445,7 +448,7 @@
 
         try {
             mStatusBar.handleVisibleToUserChanged(true);
-
+            waitForUiOffloadThread();
             verify(mBarService, never()).onPanelHidden();
             verify(mBarService, times(1)).onPanelRevealed(false, 1);
         } catch (RemoteException e) {
@@ -464,7 +467,7 @@
 
         try {
             mStatusBar.handleVisibleToUserChanged(true);
-
+            waitForUiOffloadThread();
             verify(mBarService, never()).onPanelHidden();
             verify(mBarService, times(1)).onPanelRevealed(true, 5);
         } catch (RemoteException e) {
@@ -483,7 +486,7 @@
 
         try {
             mStatusBar.handleVisibleToUserChanged(true);
-
+            waitForUiOffloadThread();
             verify(mBarService, never()).onPanelHidden();
             verify(mBarService, times(1)).onPanelRevealed(false, 5);
         } catch (RemoteException e) {
@@ -532,12 +535,21 @@
         mStatusBar.updateKeyguardState(false, false);
     }
 
+    @Test
+    public void testFingerprintNotification_UpdatesScrims() {
+        mStatusBar.mStatusBarWindowManager = mock(StatusBarWindowManager.class);
+        mStatusBar.mFingerprintUnlockController = mock(FingerprintUnlockController.class);
+        mStatusBar.mDozeScrimController = mock(DozeScrimController.class);
+        mStatusBar.notifyFpAuthModeChanged();
+        verify(mScrimController).transitionTo(any(), any());
+    }
+
     static class TestableStatusBar extends StatusBar {
         public TestableStatusBar(StatusBarKeyguardViewManager man,
                 UnlockMethodCache unlock, KeyguardIndicationController key,
                 NotificationStackScrollLayout stack, HeadsUpManager hum, NotificationData nd,
                 PowerManager pm, SystemServicesProxy ssp, NotificationPanelView panelView,
-                IStatusBarService barService) {
+                IStatusBarService barService, ScrimController scrimController) {
             mStatusBarKeyguardViewManager = man;
             mUnlockMethodCache = unlock;
             mKeyguardIndicationController = key;
@@ -550,7 +562,7 @@
             mNotificationPanel = panelView;
             mBarService = barService;
             mWakefulnessLifecycle = createAwakeWakefulnessLifecycle();
-            mScrimController = mock(ScrimController.class);
+            mScrimController = scrimController;
         }
 
         private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
index d60fe78..be11024 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
@@ -49,4 +49,9 @@
     public void setRotationLocked(boolean locked) {
 
     }
+
+    @Override
+    public void setRotationLockedAtAngle(boolean locked, int rotation) {
+
+    }
 }
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 4f1f4d7..ba6bb23 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4138,6 +4138,19 @@
     // OS: O
     FIELD_NOTIFICATION_GROUP_SUMMARY = 947;
 
+    // An app attempted to forge a different component name in the AssisStructure that would be
+    // passed to the autofill service.
+    // OS: O (security patch)
+    // Package: Real package of the app being autofilled
+    // Tag FIELD_AUTOFILL_SERVICE: Package of the autofill service that processed the request
+    // TAG FIELD_AUTOFILL_FORGED_COMPONENT_NAME: Component name being forged
+    AUTOFILL_FORGED_COMPONENT_ATTEMPT = 948;
+
+    // FIELD - The component that an app tried tro forged.
+    // Type: string
+    // OS: O (security patch)
+    FIELD_AUTOFILL_FORGED_COMPONENT_NAME = 949;
+
     // ---- End O Constants, all O constants go above this line ----
 
     // OPEN: Settings > System > Languages & input > Advanced > Lift to open camera
@@ -5023,8 +5036,38 @@
     // OS: P
     CONNECTION_DEVICE_ADVANCED = 1264;
 
-    // ---- End P Constants, all P constants go above this line ----
+    // OPEN: Settings > Security > Screen lock gear icon
+    // CATEGORY: SETTINGS
+    // OS: P
+    SCREEN_LOCK_SETTINGS = 1265;
 
+    // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Delete rule (trash can icon)
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_DELETE_RULE_DIALOG = 1266;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Select rule ("Event") > Rule name > OK
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_MODE_RULE_NAME_CHANGE_OK = 1267;
+
+    // OPEN: Settings > Sound > Do Not Disturb > TURN ON NOW/TURN OFF NOW
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_TOGGLE_DND_BUTTON = 1268;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Add rule > Event/Time
+    // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Select rule ("Event") > Rule name
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_RULE_NAME_DIALOG = 1269;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Add rule
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_RULE_SELECTION_DIALOG = 1270;
+
+    // ---- 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/Android.bp b/services/Android.bp
new file mode 100644
index 0000000..84c45fe
--- /dev/null
+++ b/services/Android.bp
@@ -0,0 +1,8 @@
+// native library
+// =============================================================
+
+cc_library_shared {
+    name: "libandroid_servers",
+    defaults: ["libservices.core-libs"],
+    whole_static_libs: ["libservices.core"],
+}
diff --git a/services/Android.mk b/services/Android.mk
index ed2ba1f..81d8181 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -52,23 +52,6 @@
 
 include $(BUILD_JAVA_LIBRARY)
 
-# native library
-# =============================================================
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES :=
-LOCAL_SHARED_LIBRARIES :=
-
-# include all the jni subdirs to collect their sources
-include $(wildcard $(LOCAL_PATH)/*/jni/Android.mk)
-
-LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
-
-LOCAL_MODULE:= libandroid_servers
-
-include $(BUILD_SHARED_LIBRARY)
-
 # =============================================================
 
 ifeq (,$(ONE_SHOT_MAKEFILE))
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
index 22d922b..7e94d7b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
@@ -20,7 +20,9 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 
+import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.GestureDescription;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.annotation.NonNull;
@@ -47,6 +49,7 @@
 import android.view.View;
 import android.view.accessibility.AccessibilityCache;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
@@ -62,7 +65,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -104,7 +106,7 @@
 
     int mFeedbackType;
 
-    final Set<String> mPackageNames = new HashSet<>();
+    Set<String> mPackageNames = new HashSet<>();
 
     boolean mIsDefault;
 
@@ -282,98 +284,40 @@
         return true;
     }
 
-    boolean setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
-        boolean somethingChanged = false;
-
-        if (mEventTypes != info.eventTypes) {
-            mEventTypes = info.eventTypes;
-            somethingChanged = true;
+    public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
+        mEventTypes = info.eventTypes;
+        mFeedbackType = info.feedbackType;
+        String[] packageNames = info.packageNames;
+        if (packageNames != null) {
+            mPackageNames.addAll(Arrays.asList(packageNames));
         }
-
-        if (mFeedbackType != info.feedbackType) {
-            mFeedbackType = info.feedbackType;
-            somethingChanged = true;
-        }
-
-        final String[] oldPackageNames = mPackageNames.toArray(new String[mPackageNames.size()]);
-        if (!Arrays.equals(oldPackageNames, info.packageNames)) {
-            mPackageNames.clear();
-            if (info.packageNames != null) {
-                Collections.addAll(mPackageNames, info.packageNames);
-            }
-            somethingChanged = true;
-        }
-
-        if (mNotificationTimeout != info.notificationTimeout) {
-            mNotificationTimeout = info.notificationTimeout;
-            somethingChanged = true;
-        }
-
-        final boolean newIsDefault = (info.flags & DEFAULT) != 0;
-        if (mIsDefault != newIsDefault) {
-            mIsDefault = newIsDefault;
-            somethingChanged = true;
-        }
+        mNotificationTimeout = info.notificationTimeout;
+        mIsDefault = (info.flags & DEFAULT) != 0;
 
         if (supportsFlagForNotImportantViews(info)) {
-            somethingChanged |= updateFetchFlag(info.flags,
-                    AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS);
-        }
-
-        somethingChanged |= updateFetchFlag(info.flags,
-                AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS);
-
-        final boolean newRequestTouchExplorationMode = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
-        if (mRequestTouchExplorationMode != newRequestTouchExplorationMode) {
-            mRequestTouchExplorationMode = newRequestTouchExplorationMode;
-            somethingChanged = true;
-        }
-
-        final boolean newRequestFilterKeyEvents = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
-        if (mRequestFilterKeyEvents != newRequestFilterKeyEvents) {
-            mRequestFilterKeyEvents = newRequestFilterKeyEvents;
-            somethingChanged = true;
-        }
-
-        final boolean newRetrieveInteractiveWindows = (info.flags
-                & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
-        if (mRetrieveInteractiveWindows != newRetrieveInteractiveWindows) {
-            mRetrieveInteractiveWindows = newRetrieveInteractiveWindows;
-            somethingChanged = true;
-        }
-
-        final boolean newCaptureFingerprintGestures = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES) != 0;
-        if (mCaptureFingerprintGestures != newCaptureFingerprintGestures) {
-            mCaptureFingerprintGestures = newCaptureFingerprintGestures;
-            somethingChanged = true;
-        }
-
-        final boolean newRequestAccessibilityButton = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
-        if (mRequestAccessibilityButton != newRequestAccessibilityButton) {
-            mRequestAccessibilityButton = newRequestAccessibilityButton;
-            somethingChanged = true;
-        }
-
-        return somethingChanged;
-    }
-
-    private boolean updateFetchFlag(int allFlags, int flagToUpdate) {
-        if ((allFlags & flagToUpdate) != 0) {
-            if ((mFetchFlags & flagToUpdate) == 0) {
-                mFetchFlags |= flagToUpdate;
-                return true;
+            if ((info.flags & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0) {
+                mFetchFlags |= AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+            } else {
+                mFetchFlags &= ~AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
             }
+        }
+
+        if ((info.flags & AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS) != 0) {
+            mFetchFlags |= AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
         } else {
-            if ((mFetchFlags & flagToUpdate) != 0) {
-                mFetchFlags &= ~flagToUpdate;
-                return true;
-            }
+            mFetchFlags &= ~AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
         }
-        return false;
+
+        mRequestTouchExplorationMode = (info.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
+        mRequestFilterKeyEvents = (info.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
+        mRetrieveInteractiveWindows = (info.flags
+                & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
+        mCaptureFingerprintGestures = (info.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES) != 0;
+        mRequestAccessibilityButton = (info.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
     }
 
     protected boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
@@ -405,15 +349,14 @@
                 // If the XML manifest had data to configure the service its info
                 // should be already set. In such a case update only the dynamically
                 // configurable properties.
-                final boolean serviceInfoChanged;
                 AccessibilityServiceInfo oldInfo = mAccessibilityServiceInfo;
                 if (oldInfo != null) {
                     oldInfo.updateDynamicallyConfigurableProperties(info);
-                    serviceInfoChanged = setDynamicallyConfigurableProperties(oldInfo);
+                    setDynamicallyConfigurableProperties(oldInfo);
                 } else {
-                    serviceInfoChanged = setDynamicallyConfigurableProperties(info);
+                    setDynamicallyConfigurableProperties(info);
                 }
-                mSystemSupport.onClientChange(serviceInfoChanged);
+                mSystemSupport.onClientChange(true);
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8b5c85a7..0a21b9e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -100,7 +100,8 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IntPair;
 import com.android.server.LocalServices;
-import com.android.server.policy.AccessibilityShortcutController;
+import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.accessibility.AccessibilityShortcutController.ToggleableFrameworkFeatureInfo;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -1897,8 +1898,11 @@
         if (userState.mServiceToEnableWithShortcut == null) {
             return;
         }
-        boolean shortcutServiceIsInstalled = false;
-        for (int i = 0; i < userState.mInstalledServices.size(); i++) {
+        boolean shortcutServiceIsInstalled =
+                AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
+                        .containsKey(userState.mServiceToEnableWithShortcut);
+        for (int i = 0; !shortcutServiceIsInstalled && (i < userState.mInstalledServices.size());
+                i++) {
             if (userState.mInstalledServices.get(i).getComponentName()
                     .equals(userState.mServiceToEnableWithShortcut)) {
                 shortcutServiceIsInstalled = true;
@@ -1909,7 +1913,8 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                        Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, null, userState.mUserId);
+                        Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, null,
+                        userState.mUserId);
 
                 Settings.Secure.putIntForUser(mContext.getContentResolver(),
                         Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 0, userState.mUserId);
@@ -2117,12 +2122,26 @@
             throw new SecurityException(
                     "performAccessibilityShortcut requires the WRITE_SECURE_SETTINGS permission");
         }
+        final Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureMap =
+                AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
         synchronized(mLock) {
-            UserState userState = getUserStateLocked(mCurrentUserId);
-            ComponentName serviceName = userState.mServiceToEnableWithShortcut;
+            final UserState userState = getUserStateLocked(mCurrentUserId);
+            final ComponentName serviceName = userState.mServiceToEnableWithShortcut;
             if (serviceName == null) {
                 return;
             }
+            if (frameworkFeatureMap.containsKey(serviceName)) {
+                // Toggle the requested framework feature
+                ToggleableFrameworkFeatureInfo featureInfo = frameworkFeatureMap.get(serviceName);
+                SettingStringHelper setting = new SettingStringHelper(mContext.getContentResolver(),
+                        featureInfo.getSettingKey(), mCurrentUserId);
+                // Assuming that the default state will be to have the feature off
+                if (!TextUtils.equals(featureInfo.getSettingOnValue(), setting.read())) {
+                    setting.write(featureInfo.getSettingOnValue());
+                } else {
+                    setting.write(featureInfo.getSettingOffValue());
+                }
+            }
             final long identity = Binder.clearCallingIdentity();
             try {
                 if (userState.mComponentNameToServiceMap.get(serviceName) == null) {
@@ -2400,8 +2419,7 @@
         private void announceNewUserIfNeeded() {
             synchronized (mLock) {
                 UserState userState = getCurrentUserStateLocked();
-                if (userState.isHandlingAccessibilityEvents()
-                        && userState.isObservedEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT)) {
+                if (userState.isHandlingAccessibilityEvents()) {
                     UserManager userManager = (UserManager) mContext.getSystemService(
                             Context.USER_SERVICE);
                     String message = mContext.getString(R.string.user_switched,
@@ -3158,21 +3176,13 @@
             if (mWindowsForAccessibilityCallback == null) {
                 return;
             }
-            final int userId;
-            synchronized (mLock) {
-                userId = mCurrentUserId;
-                final UserState userState = getUserStateLocked(userId);
-                if (!userState.isObservedEventType(AccessibilityEvent.TYPE_WINDOWS_CHANGED)) {
-                    return;
-                }
-            }
             final long identity = Binder.clearCallingIdentity();
             try {
                 // Let the client know the windows changed.
                 AccessibilityEvent event = AccessibilityEvent.obtain(
                         AccessibilityEvent.TYPE_WINDOWS_CHANGED);
                 event.setEventTime(SystemClock.uptimeMillis());
-                sendAccessibilityEvent(event, userId);
+                sendAccessibilityEvent(event, mCurrentUserId);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -3377,10 +3387,6 @@
             mUserId = userId;
         }
 
-        public boolean isObservedEventType(@AccessibilityEvent.EventType int type) {
-            return (mLastSentRelevantEventTypes & type) != 0;
-        }
-
         public int getClientState() {
             int clientState = 0;
             final boolean a11yEnabled = (mUiAutomationManager.isUiAutomationRunningLocked()
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 62017e8..3419b80 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -791,7 +791,7 @@
      */
     private void sendAccessibilityEvent(int type) {
         AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
-        if (accessibilityManager.isObservedEventType(type)) {
+        if (accessibilityManager.isEnabled()) {
             AccessibilityEvent event = AccessibilityEvent.obtain(type);
             event.setWindowId(mAms.getActiveWindowId());
             accessibilityManager.sendAccessibilityEvent(event);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 6c15438..b446209 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -2506,6 +2506,8 @@
             info.widgetCategory = sa.getInt(
                     com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory,
                     AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
+            info.widgetFeatures = sa.getInt(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_widgetFeatures, 0);
 
             sa.recycle();
         } catch (IOException | PackageManager.NameNotFoundException | XmlPullParserException e) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 0291276..690c45b 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -610,6 +610,21 @@
         }
 
         @Override
+        public boolean isFieldClassificationEnabled() throws RemoteException {
+            UserHandle user = getCallingUserHandle();
+            int uid = getCallingUid();
+
+            synchronized (mLock) {
+                AutofillManagerServiceImpl service = peekServiceForUserLocked(user.getIdentifier());
+                if (service != null) {
+                    return service.isFieldClassificationEnabled();
+                }
+            }
+
+            return false;
+        }
+
+        @Override
         public boolean restoreSession(int sessionId, IBinder activityToken, IBinder appCallback)
                 throws RemoteException {
             activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 8b6dc20..b2a0ce5 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -32,6 +32,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ServiceInfo;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -43,10 +44,12 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.service.autofill.AutofillService;
 import android.service.autofill.AutofillServiceInfo;
+import android.service.autofill.FieldClassification.Match;
 import android.service.autofill.FillEventHistory;
 import android.service.autofill.FillEventHistory.Event;
 import android.service.autofill.FillResponse;
@@ -74,6 +77,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Random;
 
 /**
@@ -463,6 +467,8 @@
             sessionId = sRandom.nextInt();
         } while (sessionId == NO_SESSION || mSessions.indexOfKey(sessionId) >= 0);
 
+        assertCallerLocked(componentName);
+
         final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock,
                 sessionId, uid, activityToken, appCallbackToken, hasCallback,
                 mUiLatencyHistory, mInfo.getServiceInfo().getComponentName(), componentName);
@@ -472,6 +478,34 @@
     }
 
     /**
+     * Asserts the component is owned by the caller.
+     */
+    private void assertCallerLocked(@NonNull ComponentName componentName) {
+        final PackageManager pm = mContext.getPackageManager();
+        final int callingUid = Binder.getCallingUid();
+        final int packageUid;
+        try {
+            packageUid = pm.getPackageUidAsUser(componentName.getPackageName(),
+                    UserHandle.getCallingUserId());
+        } catch (NameNotFoundException e) {
+            throw new SecurityException("Could not verify UID for " + componentName);
+        }
+        if (callingUid != packageUid) {
+            final String[] packages = pm.getPackagesForUid(callingUid);
+            final String callingPackage = packages != null ? packages[0] : "uid-" + callingUid;
+            Slog.w(TAG, "App (package=" + callingPackage + ", UID=" + callingUid
+                    + ") passed component (" + componentName + ") owned by UID " + packageUid);
+            mMetricsLogger.write(
+                    Helper.newLogMaker(MetricsEvent.AUTOFILL_FORGED_COMPONENT_ATTEMPT,
+                            callingPackage, getServicePackageName())
+                    .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FORGED_COMPONENT_NAME,
+                            componentName == null ? "null" : componentName.flattenToShortString()));
+
+            throw new SecurityException("Invalid component: " + componentName);
+        }
+    }
+
+    /**
      * Restores a session after an activity was temporarily destroyed.
      *
      * @param sessionId The id of the session to restore
@@ -626,7 +660,7 @@
             if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
                 mEventHistory.addEvent(
                         new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null,
-                                null, null, null, null, null, -1));
+                                null, null, null, null, null, null));
             }
         }
     }
@@ -640,7 +674,7 @@
             if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) {
                 mEventHistory.addEvent(
                         new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
-                                clientState, null, null, null, null, null, null, null, -1));
+                                clientState, null, null, null, null, null, null, null, null));
             }
         }
     }
@@ -652,7 +686,7 @@
         synchronized (mLock) {
             if (isValidEventLocked("logSaveShown()", sessionId)) {
                 mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null,
-                        null, null, null, null, null, null, -1));
+                        null, null, null, null, null, null, null));
             }
         }
     }
@@ -666,7 +700,7 @@
             if (isValidEventLocked("logDatasetSelected()", sessionId)) {
                 mEventHistory.addEvent(
                         new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
-                                null, null, null, null, null, null, -1));
+                                null, null, null, null, null, null, null));
             }
         }
     }
@@ -681,14 +715,24 @@
             @Nullable ArrayList<String> changedDatasetIds,
             @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
             @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
-            @Nullable String detectedRemoteId, int detectedFieldScore) {
+            @Nullable ArrayList<AutofillId> detectedFieldIdsList,
+            @Nullable ArrayList<Match> detectedMatchesList) {
+
         synchronized (mLock) {
             if (isValidEventLocked("logDatasetNotSelected()", sessionId)) {
+                AutofillId[] detectedFieldsIds = null;
+                Match[] detectedMatches = null;
+                if (detectedFieldIdsList != null) {
+                    detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()];
+                    detectedFieldIdsList.toArray(detectedFieldsIds);
+                    detectedMatches = new Match[detectedMatchesList.size()];
+                    detectedMatchesList.toArray(detectedMatches);
+                }
                 mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null,
                         clientState, selectedDatasets, ignoredDatasets,
                         changedFieldIds, changedDatasetIds,
                         manuallyFilledFieldIds, manuallyFilledDatasetIds,
-                        detectedRemoteId, detectedFieldScore));
+                        detectedFieldsIds, detectedMatches));
             }
         }
     }
@@ -757,7 +801,8 @@
         pw.print(prefix); pw.print("Default component: ");
             pw.println(mContext.getString(R.string.config_defaultAutofillService));
         pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
-        pw.print(prefix); pw.print("Field detection: "); pw.println(isFieldDetectionEnabled());
+        pw.print(prefix); pw.print("Field classification enabled: ");
+            pw.println(isFieldClassificationEnabled());
         pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
         pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
 
@@ -1014,10 +1059,10 @@
         return false;
     }
 
-    // TODO(b/67867469): remove once feature is finished
-    boolean isFieldDetectionEnabled() {
+    boolean isFieldClassificationEnabled() {
         return Settings.Secure.getIntForUser(
-                mContext.getContentResolver(), Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION, 0,
+                mContext.getContentResolver(),
+                Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 0,
                 mUserId) == 1;
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3615bca..4e64afb 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -55,6 +55,7 @@
 import android.os.SystemClock;
 import android.service.autofill.AutofillService;
 import android.service.autofill.Dataset;
+import android.service.autofill.FieldClassification.Match;
 import android.service.autofill.FillContext;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
@@ -64,6 +65,7 @@
 import android.service.autofill.SaveRequest;
 import android.service.autofill.UserData;
 import android.service.autofill.ValueFinder;
+import android.service.autofill.EditDistanceScorer;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LocalLog;
@@ -236,10 +238,15 @@
                 structure.ensureData();
 
                 // Sanitize structure before it's sent to service.
-                if (!mComponentName.equals(structure.getActivityComponent())) {
+                final ComponentName componentNameFromApp = structure.getActivityComponent();
+                if (!mComponentName.equals(componentNameFromApp)) {
                     Slog.w(TAG, "Activity " + mComponentName + " forged different component on "
-                            + "AssistStructure: " + structure.getActivityComponent());
+                            + "AssistStructure: " + componentNameFromApp);
                     structure.setActivityComponent(mComponentName);
+                    mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_FORGED_COMPONENT_ATTEMPT)
+                            .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FORGED_COMPONENT_NAME,
+                                    componentNameFromApp == null ? "null"
+                                            : componentNameFromApp.flattenToShortString()));
                 }
                 structure.sanitizeForParceling(true);
 
@@ -499,7 +506,7 @@
         }
 
         // TODO(b/67867469): remove once feature is finished
-        if (response.getFieldClassificationIds() != null && !mService.isFieldDetectionEnabled()) {
+        if (response.getFieldClassificationIds() != null && !mService.isFieldClassificationEnabled()) {
             Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
             processNullResponseLocked(requestFlags);
             return;
@@ -942,19 +949,18 @@
         }
 
         final UserData userData = mService.getUserData();
-        final AutofillId detectableFieldId;
-        final String detectableRemoteId;
-        String detectedRemoteId = null;
-        if (userData == null) {
-            detectableFieldId = null;
-            detectableRemoteId = null;
-        } else {
-            // TODO(b/67867469): hardcoded to just first entry on initial refactoring.
-            detectableFieldId = fieldClassificationIds[0];
-            detectableRemoteId = userData.getRemoteIds()[0];
-        }
 
-        int detectedFieldScore = -1;
+        final ArrayList<AutofillId> detectedFieldIds;
+        final ArrayList<Match> detectedMatches;
+
+        if (userData != null) {
+            final int maxFieldsSize = UserData.getMaxFieldClassificationIdsSize();
+            detectedFieldIds = new ArrayList<>(maxFieldsSize);
+            detectedMatches = new ArrayList<>(maxFieldsSize);
+        } else {
+            detectedFieldIds = null;
+            detectedMatches = null;
+        }
 
         for (int i = 0; i < mViewStates.size(); i++) {
             final ViewState viewState = mViewStates.valueAt(i);
@@ -998,8 +1004,8 @@
                     final AutofillValue currentValue = viewState.getCurrentValue();
                     if (currentValue == null) {
                         if (sDebug) {
-                            Slog.d(TAG, "logContextCommitted(): skipping view witout current value "
-                                    + "( " + viewState + ")");
+                            Slog.d(TAG, "logContextCommitted(): skipping view without current "
+                                    + "value ( " + viewState + ")");
                         }
                         continue;
                     }
@@ -1060,19 +1066,10 @@
                         } // for j
                     }
 
-                    // Check if detectable field changed.
-                    if (detectableFieldId != null && detectableFieldId.equals(viewState.id)
-                            && currentValue.isText() && currentValue.getTextValue() != null) {
-                        final String actualValue = currentValue.getTextValue().toString();
-                        // TODO(b/67867469): hardcoded to just first entry on initial refactoring.
-                        final String expectedValue = userData.getValues()[0];
-                        if (actualValue.equalsIgnoreCase(expectedValue)) {
-                            detectedRemoteId = detectableRemoteId;
-                            detectedFieldScore = 0;
-                        } else if (sVerbose) {
-                            Slog.v(TAG, "Detection mismatch for field " + detectableFieldId);
-                        }
-                        // TODO(b/67867469): set score on partial hits
+                    // Sets field classification score for field
+                    if (userData!= null) {
+                        setScore(detectedFieldIds, detectedMatches, userData, viewState.id,
+                                currentValue);
                     }
                 } // else
             } // else
@@ -1085,8 +1082,8 @@
                     + ", changedAutofillIds=" + changedFieldIds
                     + ", changedDatasetIds=" + changedDatasetIds
                     + ", manuallyFilledIds=" + manuallyFilledIds
-                    + ", detectableFieldId=" + detectableFieldId
-                    + ", detectedFieldScore=" + detectedFieldScore
+                    + ", detectedFieldIds=" + detectedFieldIds
+                    + ", detectedMatches=" + detectedMatches
                     );
         }
 
@@ -1109,7 +1106,46 @@
         mService.logContextCommitted(id, mClientState, mSelectedDatasetIds, ignoredDatasets,
                 changedFieldIds, changedDatasetIds,
                 manuallyFilledFieldIds, manuallyFilledDatasetIds,
-                detectedRemoteId, detectedFieldScore);
+                detectedFieldIds, detectedMatches);
+    }
+
+    /**
+     * Adds the top score match to {@code detectedFieldsIds} and {@code detectedMatches} for
+     * {@code fieldId} based on its {@code currentValue} and {@code userData}.
+     */
+    private static void setScore(@NonNull ArrayList<AutofillId> detectedFieldIds,
+            @NonNull ArrayList<Match> detectedMatches, @NonNull UserData userData,
+            @NonNull AutofillId fieldId, @NonNull AutofillValue currentValue) {
+
+        final String[] userValues = userData.getValues();
+        final String[] remoteIds = userData.getRemoteIds();
+
+        // Sanity check
+        if (userValues == null || remoteIds == null || userValues.length != remoteIds.length) {
+            final int valuesLength = userValues == null ? -1 : userValues.length;
+            final int idsLength = remoteIds == null ? -1 : remoteIds.length;
+            Slog.w(TAG, "setScores(): user data mismatch: values.length = "
+                    + valuesLength + ", ids.length = " + idsLength);
+            return;
+        }
+        String remoteId = null;
+        float topScore = 0;
+        for (int i = 0; i < userValues.length; i++) {
+            final String value = userValues[i];
+            final float score = userData.getScorer().getScore(currentValue, value);
+            if (score > topScore) {
+                topScore = score;
+                remoteId = remoteIds[i];
+            }
+        }
+
+        if (remoteId != null && topScore > 0) {
+            if (sVerbose) Slog.v(TAG, "setScores(): top score for #" + fieldId + " is " + topScore);
+            detectedFieldIds.add(fieldId);
+            detectedMatches.add(new Match(remoteId, topScore));
+        } else if (sVerbose) {
+            Slog.v(TAG, "setScores(): no top score for #" + fieldId + ": " + topScore);
+        }
     }
 
     /**
@@ -1675,7 +1711,7 @@
                 break;
             case ACTION_VIEW_ENTERED:
                 if (sVerbose && virtualBounds != null) {
-                    Slog.w(TAG, "entered on virtual child " + id + ": " + virtualBounds);
+                    Slog.v(TAG, "entered on virtual child " + id + ": " + virtualBounds);
                 }
                 requestNewFillResponseIfNecessaryLocked(id, viewState, flags);
 
diff --git a/services/backup/java/com/android/server/backup/FileMetadata.java b/services/backup/java/com/android/server/backup/FileMetadata.java
index 5465609..3d260cb 100644
--- a/services/backup/java/com/android/server/backup/FileMetadata.java
+++ b/services/backup/java/com/android/server/backup/FileMetadata.java
@@ -36,7 +36,7 @@
     public long mode;                      // e.g. 0666 (actually int)
     public long mtime;                     // last mod time, UTC time_t (actually int)
     public long size;                      // bytes of content
-    public int version;                    // App version.
+    public long version;                   // App version.
     public boolean hasApk;                 // Whether backup file contains apk.
 
     @Override
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index f658f22..2d2993d 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -99,10 +99,10 @@
     // For compactness we store the SHA-256 hash of each app's Signatures
     // rather than the Signature blocks themselves.
     public class Metadata {
-        public int versionCode;
+        public long versionCode;
         public ArrayList<byte[]> sigHashes;
 
-        Metadata(int version, ArrayList<byte[]> hashes) {
+        Metadata(long version, ArrayList<byte[]> hashes) {
             versionCode = version;
             sigHashes = hashes;
         }
@@ -206,7 +206,7 @@
                 homeInfo = mPackageManager.getPackageInfo(home.getPackageName(),
                         PackageManager.GET_SIGNATURES);
                 homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName());
-                homeVersion = homeInfo.versionCode;
+                homeVersion = homeInfo.getLongVersionCode();
                 homeSigHashes = BackupUtils.hashSignatureArray(homeInfo.signatures);
             } catch (NameNotFoundException e) {
                 Slog.w(TAG, "Can't access preferred home info");
@@ -287,7 +287,7 @@
                         // metadata again.  In either case, take it out of mExisting so that
                         // we don't consider it deleted later.
                         mExisting.remove(packName);
-                        if (info.versionCode == mStateVersions.get(packName).versionCode) {
+                        if (info.getLongVersionCode() == mStateVersions.get(packName).versionCode) {
                             continue;
                         }
                     }
@@ -309,13 +309,18 @@
 
                     // marshal the version code in a canonical form
                     outputBuffer.reset();
-                    outputBufferStream.writeInt(info.versionCode);
+                    if (info.versionCodeMajor != 0) {
+                        outputBufferStream.writeInt(Integer.MIN_VALUE);
+                        outputBufferStream.writeLong(info.getLongVersionCode());
+                    } else {
+                        outputBufferStream.writeInt(info.versionCode);
+                    }
                     writeSignatureHashArray(outputBufferStream,
                             BackupUtils.hashSignatureArray(info.signatures));
 
                     if (DEBUG) {
                         Slog.v(TAG, "+ writing metadata for " + packName
-                                + " version=" + info.versionCode
+                                + " version=" + info.getLongVersionCode()
                                 + " entityLen=" + outputBuffer.size());
                     }
                     
@@ -409,7 +414,13 @@
                 }
             } else {
                 // it's a file metadata record
-                int versionCode = inputBufferStream.readInt();
+                int versionCodeInt = inputBufferStream.readInt();
+                long versionCode;
+                if (versionCodeInt == Integer.MIN_VALUE) {
+                    versionCode = inputBufferStream.readLong();
+                } else {
+                    versionCode = versionCodeInt;
+                }
                 ArrayList<byte[]> sigs = readSignatureHashArray(inputBufferStream);
                 if (DEBUG) {
                     Slog.i(TAG, "   read metadata for " + key
@@ -561,7 +572,13 @@
             // The global metadata was last; now read all the apps
             while (true) {
                 pkg = in.readUTF();
-                int versionCode = in.readInt();
+                int versionCodeInt = in.readInt();
+                long versionCode;
+                if (versionCodeInt == Integer.MIN_VALUE) {
+                    versionCode = in.readLong();
+                } else {
+                    versionCode = versionCodeInt;
+                }
 
                 if (!ignoreExisting) {
                     mExisting.add(pkg);
@@ -609,7 +626,12 @@
             // now write all the app names + versions
             for (PackageInfo pkg : pkgs) {
                 out.writeUTF(pkg.packageName);
-                out.writeInt(pkg.versionCode);
+                if (pkg.versionCodeMajor != 0) {
+                    out.writeInt(Integer.MIN_VALUE);
+                    out.writeLong(pkg.getLongVersionCode());
+                } else {
+                    out.writeInt(pkg.versionCode);
+                }
             }
 
             out.flush();
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index 3eaee9a..bd0d853 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -2389,19 +2389,24 @@
             if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
             mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
             synchronized (mQueueLock) {
-                final IBackupTransport transport =
-                        mTransportManager.getTransportBinder(transportName);
-                if (transport == null) {
-                    // transport is currently unavailable -- make sure to retry
+                TransportClient transportClient =
+                        mTransportManager
+                                .getTransportClient(transportName, "BMS.clearBackupData()");
+                if (transportClient == null) {
+                    // transport is currently unregistered -- make sure to retry
                     Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
                             new ClearRetryParams(transportName, packageName));
                     mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
                     return;
                 }
                 long oldId = Binder.clearCallingIdentity();
+                OnTaskFinishedListener listener =
+                        caller ->
+                                mTransportManager.disposeOfTransportClient(transportClient, caller);
                 mWakelock.acquire();
-                Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
-                        new ClearParams(transport, info));
+                Message msg = mBackupHandler.obtainMessage(
+                        MSG_RUN_CLEAR,
+                        new ClearParams(transportClient, info, listener));
                 mBackupHandler.sendMessage(msg);
                 Binder.restoreCallingIdentity(oldId);
             }
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 9011b95..477724d 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -268,8 +268,13 @@
 
             case MSG_RUN_CLEAR: {
                 ClearParams params = (ClearParams) msg.obj;
-                (new PerformClearTask(backupManagerService, params.transport,
-                        params.packageInfo)).run();
+                Runnable task =
+                        new PerformClearTask(
+                                backupManagerService,
+                                params.transportClient,
+                                params.packageInfo,
+                                params.listener);
+                task.run();
                 break;
             }
 
diff --git a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
index 7af01ea..84ca59b 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
@@ -23,46 +23,54 @@
 
 import com.android.internal.backup.IBackupTransport;
 import com.android.server.backup.RefactoredBackupManagerService;
+import com.android.server.backup.transport.TransportClient;
 
 import java.io.File;
 
 public class PerformClearTask implements Runnable {
-
-    private RefactoredBackupManagerService backupManagerService;
-    IBackupTransport mTransport;
-    PackageInfo mPackage;
+    private final RefactoredBackupManagerService mBackupManagerService;
+    private final TransportClient mTransportClient;
+    private final PackageInfo mPackage;
+    private final OnTaskFinishedListener mListener;
 
     PerformClearTask(RefactoredBackupManagerService backupManagerService,
-            IBackupTransport transport, PackageInfo packageInfo) {
-        this.backupManagerService = backupManagerService;
-        mTransport = transport;
+            TransportClient transportClient, PackageInfo packageInfo,
+            OnTaskFinishedListener listener) {
+        mBackupManagerService = backupManagerService;
+        mTransportClient = transportClient;
         mPackage = packageInfo;
+        mListener = listener;
     }
 
     public void run() {
+        String callerLogString = "PerformClearTask.run()";
+        IBackupTransport transport = null;
         try {
             // Clear the on-device backup state to ensure a full backup next time
-            File stateDir = new File(backupManagerService.getBaseStateDir(),
-                    mTransport.transportDirName());
+            File stateDir = new File(mBackupManagerService.getBaseStateDir(),
+                    mTransportClient.getTransportDirName());
             File stateFile = new File(stateDir, mPackage.packageName);
             stateFile.delete();
 
+            transport = mTransportClient.connectOrThrow(callerLogString);
             // Tell the transport to remove all the persistent storage for the app
             // TODO - need to handle failures
-            mTransport.clearBackupData(mPackage);
+            transport.clearBackupData(mPackage);
         } catch (Exception e) {
             Slog.e(TAG, "Transport threw clearing data for " + mPackage + ": " + e.getMessage());
         } finally {
-            try {
-                // TODO - need to handle failures
-                mTransport.finishBackup();
-            } catch (Exception e) {
-                // Nothing we can do here, alas
-                Slog.e(TAG, "Unable to mark clear operation finished: " + e.getMessage());
+            if (transport != null) {
+                try {
+                    // TODO - need to handle failures
+                    transport.finishBackup();
+                } catch (Exception e) {
+                    // Nothing we can do here, alas
+                    Slog.e(TAG, "Unable to mark clear operation finished: " + e.getMessage());
+                }
             }
-
+            mListener.onFinished(callerLogString);
             // Last but not least, release the cpu
-            backupManagerService.getWakelock().release();
+            mBackupManagerService.getWakelock().release();
         }
     }
 }
diff --git a/services/backup/java/com/android/server/backup/params/ClearParams.java b/services/backup/java/com/android/server/backup/params/ClearParams.java
index d744efc..dc3bba0 100644
--- a/services/backup/java/com/android/server/backup/params/ClearParams.java
+++ b/services/backup/java/com/android/server/backup/params/ClearParams.java
@@ -18,15 +18,20 @@
 
 import android.content.pm.PackageInfo;
 
-import com.android.internal.backup.IBackupTransport;
+import com.android.server.backup.internal.OnTaskFinishedListener;
+import com.android.server.backup.transport.TransportClient;
 
 public class ClearParams {
-
-    public IBackupTransport transport;
+    public TransportClient transportClient;
     public PackageInfo packageInfo;
+    public OnTaskFinishedListener listener;
 
-    public ClearParams(IBackupTransport _transport, PackageInfo _info) {
-        transport = _transport;
-        packageInfo = _info;
+    public ClearParams(
+            TransportClient transportClient,
+            PackageInfo packageInfo,
+            OnTaskFinishedListener listener) {
+        this.transportClient = transportClient;
+        this.packageInfo = packageInfo;
+        this.listener = listener;
     }
 }
diff --git a/services/backup/java/com/android/server/backup/params/ClearRetryParams.java b/services/backup/java/com/android/server/backup/params/ClearRetryParams.java
index fcf66e4..41b5641 100644
--- a/services/backup/java/com/android/server/backup/params/ClearRetryParams.java
+++ b/services/backup/java/com/android/server/backup/params/ClearRetryParams.java
@@ -17,12 +17,11 @@
 package com.android.server.backup.params;
 
 public class ClearRetryParams {
-
     public String transportName;
     public String packageName;
 
-    public ClearRetryParams(String transport, String pkg) {
-        transportName = transport;
-        packageName = pkg;
+    public ClearRetryParams(String transportName, String packageName) {
+        this.transportName = transportName;
+        this.packageName = packageName;
     }
 }
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index b538c6d..5884dc5 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -495,14 +495,14 @@
                 return;
             }
 
-            if (metaInfo.versionCode > mCurrentPackage.versionCode) {
+            if (metaInfo.versionCode > mCurrentPackage.getLongVersionCode()) {
                 // Data is from a "newer" version of the app than we have currently
                 // installed.  If the app has not declared that it is prepared to
                 // handle this case, we do not attempt the restore.
                 if ((mCurrentPackage.applicationInfo.flags
                         & ApplicationInfo.FLAG_RESTORE_ANY_VERSION) == 0) {
                     String message = "Source version " + metaInfo.versionCode
-                            + " > installed version " + mCurrentPackage.versionCode;
+                            + " > installed version " + mCurrentPackage.getLongVersionCode();
                     Slog.w(TAG, "Package " + pkgName + ": " + message);
                     Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null,
                             BackupManagerMonitor.EXTRA_LOG_RESTORE_VERSION,
@@ -522,7 +522,7 @@
                 } else {
                     if (DEBUG) {
                         Slog.v(TAG, "Source version " + metaInfo.versionCode
-                                + " > installed version " + mCurrentPackage.versionCode
+                                + " > installed version " + mCurrentPackage.getLongVersionCode()
                                 + " but restoreAnyVersion");
                     }
                     Bundle monitoringExtras = BackupManagerMonitorUtils.putMonitoringExtra(null,
@@ -543,7 +543,7 @@
                 Slog.v(TAG, "Package " + pkgName
                         + " restore version [" + metaInfo.versionCode
                         + "] is compatible with installed version ["
-                        + mCurrentPackage.versionCode + "]");
+                        + mCurrentPackage.getLongVersionCode() + "]");
             }
 
             // Reset per-package preconditions and fire the appropriate next state
@@ -635,7 +635,7 @@
     }
 
     // Guts of a key/value restore operation
-    void initiateOneRestore(PackageInfo app, int appVersionCode) {
+    void initiateOneRestore(PackageInfo app, long appVersionCode) {
         final String packageName = app.packageName;
 
         if (DEBUG) {
diff --git a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java
index 734fa1d..010684e 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupManagerMonitorUtils.java
@@ -56,6 +56,8 @@
                             pkg.packageName);
                     bundle.putInt(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION,
                             pkg.versionCode);
+                    bundle.putLong(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION,
+                            pkg.getLongVersionCode());
                 }
                 if (extras != null) {
                     bundle.putAll(extras);
diff --git a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
index b3e20dc..a731fc9 100644
--- a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
@@ -97,7 +97,7 @@
 
         printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
         printer.println(pkg.packageName);
-        printer.println(Integer.toString(pkg.versionCode));
+        printer.println(Long.toString(pkg.getLongVersionCode()));
         printer.println(Integer.toString(Build.VERSION.SDK_INT));
 
         String installerName = packageManager.getInstallerPackageName(pkg.packageName);
diff --git a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
index 2910ba2..ff9cb56 100644
--- a/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
+++ b/services/backup/java/com/android/server/backup/utils/TarBackupReader.java
@@ -422,7 +422,7 @@
                                     LOG_EVENT_CATEGORY_BACKUP_MANAGER_POLICY,
                                     null);
                             policy = RestorePolicy.ACCEPT;
-                        } else if (pkgInfo.versionCode >= info.version) {
+                        } else if (pkgInfo.getLongVersionCode() >= info.version) {
                             Slog.i(TAG, "Sig + version match; taking data");
                             policy = RestorePolicy.ACCEPT;
                             mMonitor = BackupManagerMonitorUtils.monitorEvent(
@@ -439,7 +439,7 @@
                                 Slog.i(TAG, "Data version " + info.version
                                         + " is newer than installed "
                                         + "version "
-                                        + pkgInfo.versionCode
+                                        + pkgInfo.getLongVersionCode()
                                         + " - requiring apk");
                                 policy = RestorePolicy.ACCEPT_IF_APK;
                             } else {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index f2f01cf..d44fe4d 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -21,6 +21,7 @@
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.Preconditions.checkState;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
 
 import android.Manifest;
 import android.annotation.CheckResult;
@@ -69,6 +70,7 @@
 import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
 
@@ -440,32 +442,35 @@
             return;
         }
 
-        Binder.withCleanCallingIdentity(() -> {
-            try {
-                if (containsEither(packageInfo.requestedPermissions,
-                        Manifest.permission.RUN_IN_BACKGROUND,
-                        Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) {
-                    mIdleController.addPowerSaveWhitelistApp(packageInfo.packageName);
-                } else {
-                    mIdleController.removePowerSaveWhitelistApp(packageInfo.packageName);
-                }
-            } catch (RemoteException e) {
-                /* ignore - local call */
-            }
+        Binder.withCleanCallingIdentity(obtainRunnable(CompanionDeviceManagerService::
+                updateSpecialAccessPermissionAsSystem, this, packageInfo).recycleOnUse());
+    }
 
-            NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(getContext());
+    private void updateSpecialAccessPermissionAsSystem(PackageInfo packageInfo) {
+        try {
             if (containsEither(packageInfo.requestedPermissions,
-                    Manifest.permission.USE_DATA_IN_BACKGROUND,
-                    Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)) {
-                networkPolicyManager.addUidPolicy(
-                        packageInfo.applicationInfo.uid,
-                        NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
+                    android.Manifest.permission.RUN_IN_BACKGROUND,
+                    android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) {
+                mIdleController.addPowerSaveWhitelistApp(packageInfo.packageName);
             } else {
-                networkPolicyManager.removeUidPolicy(
-                        packageInfo.applicationInfo.uid,
-                        NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
+                mIdleController.removePowerSaveWhitelistApp(packageInfo.packageName);
             }
-        });
+        } catch (RemoteException e) {
+            /* ignore - local call */
+        }
+
+        NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(getContext());
+        if (containsEither(packageInfo.requestedPermissions,
+                android.Manifest.permission.USE_DATA_IN_BACKGROUND,
+                android.Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)) {
+            networkPolicyManager.addUidPolicy(
+                    packageInfo.applicationInfo.uid,
+                    NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
+        } else {
+            networkPolicyManager.removeUidPolicy(
+                    packageInfo.applicationInfo.uid,
+                    NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
+        }
     }
 
     private static <T> boolean containsEither(T[] array, T a, T b) {
@@ -474,17 +479,17 @@
 
     @Nullable
     private PackageInfo getPackageInfo(String packageName, int userId) {
-        return Binder.withCleanCallingIdentity(() -> {
+        return Binder.withCleanCallingIdentity(PooledLambda.obtainSupplier((context, pkg, id) -> {
             try {
-                return getContext().getPackageManager().getPackageInfoAsUser(
-                        packageName,
+                return context.getPackageManager().getPackageInfoAsUser(
+                        pkg,
                         PackageManager.GET_PERMISSIONS | PackageManager.GET_CONFIGURATIONS,
-                        userId);
+                        id);
             } catch (PackageManager.NameNotFoundException e) {
-                Slog.e(LOG_TAG, "Failed to get PackageInfo for package " + packageName, e);
+                Slog.e(LOG_TAG, "Failed to get PackageInfo for package " + pkg, e);
                 return null;
             }
-        });
+        }, getContext(), packageName, userId).recycleOnUse());
     }
 
     private void recordAssociation(String priviledgedPackage, String deviceAddress) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 04279a3..763a4e4 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -67,6 +67,7 @@
 import java.io.PrintWriter;
 import java.util.HashMap;
 import java.util.LinkedList;
+import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -79,15 +80,14 @@
     private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
     private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
 
-    private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID="bluetooth_addr_valid";
-    private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address";
-    private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name";
+    private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID = "bluetooth_addr_valid";
+    private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS = "bluetooth_address";
+    private static final String SECURE_SETTINGS_BLUETOOTH_NAME = "bluetooth_name";
 
     private static final int ACTIVE_LOG_MAX_SIZE = 20;
     private static final int CRASH_LOG_MAX_SIZE = 100;
     private static final String REASON_AIRPLANE_MODE = "airplane mode";
     private static final String REASON_DISALLOWED = "disallowed by system";
-    private static final String REASON_SHARING_DISALLOWED = "sharing disallowed by system";
     private static final String REASON_RESTARTED = "automatic restart";
     private static final String REASON_START_CRASH = "turn-on crash";
     private static final String REASON_SYSTEM_BOOT = "system boot";
@@ -130,14 +130,14 @@
     private static final int MAX_ERROR_RESTART_RETRIES = 6;
 
     // Bluetooth persisted setting is off
-    private static final int BLUETOOTH_OFF=0;
+    private static final int BLUETOOTH_OFF = 0;
     // Bluetooth persisted setting is on
     // and Airplane mode won't affect Bluetooth state at start up
-    private static final int BLUETOOTH_ON_BLUETOOTH=1;
+    private static final int BLUETOOTH_ON_BLUETOOTH = 1;
     // Bluetooth persisted setting is on
     // but Airplane mode will affect Bluetooth state at start up
     // and Airplane mode will have higher priority.
-    private static final int BLUETOOTH_ON_AIRPLANE=2;
+    private static final int BLUETOOTH_ON_AIRPLANE = 2;
 
     private static final int SERVICE_IBLUETOOTH = 1;
     private static final int SERVICE_IBLUETOOTHGATT = 2;
@@ -154,8 +154,7 @@
     private IBinder mBluetoothBinder;
     private IBluetooth mBluetooth;
     private IBluetoothGatt mBluetoothGatt;
-    private final ReentrantReadWriteLock mBluetoothLock =
-        new ReentrantReadWriteLock();
+    private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock();
     private boolean mBinding;
     private boolean mUnbinding;
 
@@ -175,7 +174,7 @@
         private boolean mEnable;
         private long mTimestamp;
 
-        public ActiveLog(String packageName, boolean enable, long timestamp) {
+        ActiveLog(String packageName, boolean enable, long timestamp) {
             mPackageName = packageName;
             mEnable = enable;
             mTimestamp = timestamp;
@@ -186,8 +185,8 @@
         }
 
         public String toString() {
-            return  timeToLog(mTimestamp) + (mEnable ? "  Enabled " : " Disabled ") + " by "
-                + mPackageName;
+            return timeToLog(mTimestamp) + (mEnable ? "  Enabled " : " Disabled ") + " by "
+                    + mPackageName;
         }
 
     }
@@ -203,7 +202,8 @@
     private boolean mEnableExternal;
 
     // Map of apps registered to keep BLE scanning on.
-    private Map<IBinder, ClientDeathRecipient> mBleApps = new ConcurrentHashMap<IBinder, ClientDeathRecipient>();
+    private Map<IBinder, ClientDeathRecipient> mBleApps =
+            new ConcurrentHashMap<IBinder, ClientDeathRecipient>();
 
     private int mState;
     private final BluetoothHandler mHandler;
@@ -212,51 +212,52 @@
 
     // Save a ProfileServiceConnections object for each of the bound
     // bluetooth profile services
-    private final Map <Integer, ProfileServiceConnections> mProfileServices =
-            new HashMap <Integer, ProfileServiceConnections>();
+    private final Map<Integer, ProfileServiceConnections> mProfileServices =
+            new HashMap<Integer, ProfileServiceConnections>();
 
     private final boolean mPermissionReviewRequired;
 
     private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() {
         @Override
-        public void onBluetoothStateChange(int prevState, int newState) throws RemoteException  {
-            Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE,prevState,newState);
+        public void onBluetoothStateChange(int prevState, int newState) throws RemoteException {
+            Message msg =
+                    mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE, prevState, newState);
             mHandler.sendMessage(msg);
         }
     };
 
     private final UserRestrictionsListener mUserRestrictionsListener =
             new UserRestrictionsListener() {
-        @Override
-        public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
-                Bundle prevRestrictions) {
+                @Override
+                public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
+                        Bundle prevRestrictions) {
 
-            if (UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions,
-                    UserManager.DISALLOW_BLUETOOTH_SHARING)) {
-                updateOppLauncherComponentState(userId, newRestrictions.getBoolean(
-                        UserManager.DISALLOW_BLUETOOTH_SHARING));
-            }
+                    if (UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions,
+                            UserManager.DISALLOW_BLUETOOTH_SHARING)) {
+                        updateOppLauncherComponentState(userId,
+                                newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH_SHARING));
+                    }
 
-            // DISALLOW_BLUETOOTH can only be set by DO or PO on the system user.
-            if (userId == UserHandle.USER_SYSTEM &&
-                UserRestrictionsUtils.restrictionsChanged(
-                    prevRestrictions, newRestrictions, UserManager.DISALLOW_BLUETOOTH)) {
-                if (userId == UserHandle.USER_SYSTEM && newRestrictions.getBoolean(
-                        UserManager.DISALLOW_BLUETOOTH)) {
-                    updateOppLauncherComponentState(userId, true); // Sharing disallowed
-                    sendDisableMsg(REASON_DISALLOWED);
-                } else {
-                    updateOppLauncherComponentState(userId, newRestrictions.getBoolean(
-                            UserManager.DISALLOW_BLUETOOTH_SHARING));
+                    // DISALLOW_BLUETOOTH can only be set by DO or PO on the system user.
+                    if (userId == UserHandle.USER_SYSTEM
+                            && UserRestrictionsUtils.restrictionsChanged(prevRestrictions,
+                            newRestrictions, UserManager.DISALLOW_BLUETOOTH)) {
+                        if (userId == UserHandle.USER_SYSTEM && newRestrictions.getBoolean(
+                                UserManager.DISALLOW_BLUETOOTH)) {
+                            updateOppLauncherComponentState(userId, true); // Sharing disallowed
+                            sendDisableMsg(REASON_DISALLOWED);
+                        } else {
+                            updateOppLauncherComponentState(userId, newRestrictions.getBoolean(
+                                    UserManager.DISALLOW_BLUETOOTH_SHARING));
+                        }
+                    }
                 }
-            }
-        }
-    };
+            };
 
     private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) {
         @Override
         public void onChange(boolean unused) {
-            synchronized(this) {
+            synchronized (this) {
                 if (isBluetoothPersistedStateOn()) {
                     if (isAirplaneModeOn()) {
                         persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE);
@@ -278,8 +279,9 @@
                     mBluetoothLock.readLock().unlock();
                 }
 
-                Slog.d(TAG, "Airplane Mode change - current state:  " +
-                          BluetoothAdapter.nameForState(st));
+                Slog.d(TAG,
+                        "Airplane Mode change - current state:  " + BluetoothAdapter.nameForState(
+                                st));
 
                 if (isAirplaneModeOn()) {
                     // Clear registered LE apps to force shut-off
@@ -295,11 +297,11 @@
                                 mEnableExternal = false;
                             }
                         } catch (RemoteException e) {
-                            Slog.e(TAG,"Unable to call onBrEdrDown", e);
+                            Slog.e(TAG, "Unable to call onBrEdrDown", e);
                         } finally {
                             mBluetoothLock.readLock().unlock();
                         }
-                    } else if (st == BluetoothAdapter.STATE_ON){
+                    } else if (st == BluetoothAdapter.STATE_ON) {
                         sendDisableMsg(REASON_AIRPLANE_MODE);
                     }
                 } else if (mEnableExternal) {
@@ -315,35 +317,42 @@
             String action = intent.getAction();
             if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) {
                 String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME);
-                if (DBG) Slog.d(TAG, "Bluetooth Adapter name changed to " + newName);
+                if (DBG) {
+                    Slog.d(TAG, "Bluetooth Adapter name changed to " + newName);
+                }
                 if (newName != null) {
                     storeNameAndAddress(newName, null);
                 }
             } else if (BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED.equals(action)) {
                 String newAddress = intent.getStringExtra(BluetoothAdapter.EXTRA_BLUETOOTH_ADDRESS);
                 if (newAddress != null) {
-                    if (DBG) Slog.d(TAG, "Bluetooth Adapter address changed to " + newAddress);
+                    if (DBG) {
+                        Slog.d(TAG, "Bluetooth Adapter address changed to " + newAddress);
+                    }
                     storeNameAndAddress(null, newAddress);
                 } else {
-                    if (DBG) Slog.e(TAG, "No Bluetooth Adapter address parameter found");
+                    if (DBG) {
+                        Slog.e(TAG, "No Bluetooth Adapter address parameter found");
+                    }
                 }
             } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
                 final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
                 if (Settings.Global.BLUETOOTH_ON.equals(name)) {
                     // The Bluetooth On state may be changed during system restore.
-                    final String prevValue = intent.getStringExtra(
-                            Intent.EXTRA_SETTING_PREVIOUS_VALUE);
-                    final String newValue = intent.getStringExtra(
-                            Intent.EXTRA_SETTING_NEW_VALUE);
+                    final String prevValue =
+                            intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE);
+                    final String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
 
-                    if (DBG) Slog.d(TAG, "ACTION_SETTING_RESTORED with BLUETOOTH_ON, prevValue=" +
-                                    prevValue + ", newValue=" + newValue);
+                    if (DBG) {
+                        Slog.d(TAG,
+                                "ACTION_SETTING_RESTORED with BLUETOOTH_ON, prevValue=" + prevValue
+                                        + ", newValue=" + newValue);
+                    }
 
                     if ((newValue != null) && (prevValue != null) && !prevValue.equals(newValue)) {
                         Message msg = mHandler.obtainMessage(MESSAGE_RESTORE_USER_SETTING,
-                                                             newValue.equals("0") ?
-                                                             RESTORE_SETTING_TO_OFF :
-                                                             RESTORE_SETTING_TO_ON, 0);
+                                newValue.equals("0") ? RESTORE_SETTING_TO_OFF
+                                        : RESTORE_SETTING_TO_ON, 0);
                         mHandler.sendMessage(msg);
                     }
                 }
@@ -356,8 +365,8 @@
 
         mContext = context;
 
-        mPermissionReviewRequired = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_permissionReviewRequired);
+        mPermissionReviewRequired = context.getResources()
+                .getBoolean(com.android.internal.R.bool.config_permissionReviewRequired);
 
         mActiveLogs = new LinkedList<ActiveLog>();
         mCrashTimestamps = new LinkedList<Long>();
@@ -389,23 +398,26 @@
 
         loadStoredNameAndAddress();
         if (isBluetoothPersistedStateOn()) {
-            if (DBG) Slog.d(TAG, "Startup: Bluetooth persisted state is ON.");
+            if (DBG) {
+                Slog.d(TAG, "Startup: Bluetooth persisted state is ON.");
+            }
             mEnableExternal = true;
         }
 
-        String airplaneModeRadios = Settings.Global.getString(mContentResolver,
-            Settings.Global.AIRPLANE_MODE_RADIOS);
-        if (airplaneModeRadios == null ||
-            airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH)) {
+        String airplaneModeRadios =
+                Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS);
+        if (airplaneModeRadios == null || airplaneModeRadios.contains(
+                Settings.Global.RADIO_BLUETOOTH)) {
             mContentResolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
-                true, mAirplaneModeObserver);
+                    Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
+                    mAirplaneModeObserver);
         }
 
         int systemUiUid = -1;
         try {
-            systemUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui",
-                    PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
+            systemUiUid = mContext.getPackageManager()
+                    .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
+                            UserHandle.USER_SYSTEM);
         } catch (PackageManager.NameNotFoundException e) {
             // Some platforms, such as wearables do not have a system ui.
             Slog.w(TAG, "Unable to resolve SystemUI's UID.", e);
@@ -416,7 +428,7 @@
     /**
      *  Returns true if airplane mode is currently on
      */
-    private final boolean isAirplaneModeOn() {
+    private boolean isAirplaneModeOn() {
         return Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
     }
@@ -424,31 +436,32 @@
     /**
      *  Returns true if the Bluetooth saved state is "on"
      */
-    private final boolean isBluetoothPersistedStateOn() {
-        int state = Settings.Global.getInt(mContentResolver,
-                                           Settings.Global.BLUETOOTH_ON, -1);
-        if (DBG) Slog.d(TAG, "Bluetooth persisted state: " + state);
+    private boolean isBluetoothPersistedStateOn() {
+        int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1);
+        if (DBG) {
+            Slog.d(TAG, "Bluetooth persisted state: " + state);
+        }
         return state != BLUETOOTH_OFF;
     }
 
     /**
      *  Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH
      */
-    private final boolean isBluetoothPersistedStateOnBluetooth() {
-        return Settings.Global.getInt(mContentResolver,
-                Settings.Global.BLUETOOTH_ON, BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH;
+    private boolean isBluetoothPersistedStateOnBluetooth() {
+        return Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON,
+                BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH;
     }
 
     /**
      *  Save the Bluetooth on/off state
      */
     private void persistBluetoothSetting(int value) {
-        if (DBG) Slog.d(TAG, "Persisting Bluetooth Setting: " + value);
+        if (DBG) {
+            Slog.d(TAG, "Persisting Bluetooth Setting: " + value);
+        }
         // waive WRITE_SECURE_SETTINGS permission check
         long callingIdentity = Binder.clearCallingIdentity();
-        Settings.Global.putInt(mContext.getContentResolver(),
-                               Settings.Global.BLUETOOTH_ON,
-                               value);
+        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BLUETOOTH_ON, value);
         Binder.restoreCallingIdentity(callingIdentity);
     }
 
@@ -458,7 +471,7 @@
      * @return
      */
     private boolean isNameAndAddressSet() {
-        return mName !=null && mAddress!= null && mName.length()>0 && mAddress.length()>0;
+        return mName != null && mAddress != null && mName.length() > 0 && mAddress.length() > 0;
     }
 
     /**
@@ -466,17 +479,24 @@
      * in the local cache
      */
     private void loadStoredNameAndAddress() {
-        if (DBG) Slog.d(TAG, "Loading stored name and address");
-        if (mContext.getResources().getBoolean
-            (com.android.internal.R.bool.config_bluetooth_address_validation) &&
-             Settings.Secure.getInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0) == 0) {
+        if (DBG) {
+            Slog.d(TAG, "Loading stored name and address");
+        }
+        if (mContext.getResources()
+                .getBoolean(com.android.internal.R.bool.config_bluetooth_address_validation)
+                && Settings.Secure.getInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0)
+                == 0) {
             // if the valid flag is not set, don't load the address and name
-            if (DBG) Slog.d(TAG, "invalid bluetooth name and address stored");
+            if (DBG) {
+                Slog.d(TAG, "invalid bluetooth name and address stored");
+            }
             return;
         }
         mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME);
         mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS);
-        if (DBG) Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress);
+        if (DBG) {
+            Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress);
+        }
     }
 
     /**
@@ -489,15 +509,20 @@
         if (name != null) {
             Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name);
             mName = name;
-            if (DBG) Slog.d(TAG,"Stored Bluetooth name: " +
-                Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_NAME));
+            if (DBG) {
+                Slog.d(TAG, "Stored Bluetooth name: " + Settings.Secure.getString(mContentResolver,
+                        SECURE_SETTINGS_BLUETOOTH_NAME));
+            }
         }
 
         if (address != null) {
             Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address);
-            mAddress=address;
-            if (DBG)  Slog.d(TAG,"Stored Bluetoothaddress: " +
-                Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_ADDRESS));
+            mAddress = address;
+            if (DBG) {
+                Slog.d(TAG,
+                        "Stored Bluetoothaddress: " + Settings.Secure.getString(mContentResolver,
+                                SECURE_SETTINGS_BLUETOOTH_ADDRESS));
+            }
         }
 
         if ((name != null) && (address != null)) {
@@ -505,7 +530,7 @@
         }
     }
 
-    public IBluetooth registerAdapter(IBluetoothManagerCallback callback){
+    public IBluetooth registerAdapter(IBluetoothManagerCallback callback) {
         if (callback == null) {
             Slog.w(TAG, "Callback is null in registerAdapter");
             return null;
@@ -522,19 +547,17 @@
             Slog.w(TAG, "Callback is null in unregisterAdapter");
             return;
         }
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
-                                                "Need BLUETOOTH permission");
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER);
         msg.obj = callback;
         mHandler.sendMessage(msg);
     }
 
     public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
-                                                "Need BLUETOOTH permission");
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         if (callback == null) {
-          Slog.w(TAG, "registerStateChangeCallback: Callback is null!");
-          return;
+            Slog.w(TAG, "registerStateChangeCallback: Callback is null!");
+            return;
         }
         Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK);
         msg.obj = callback;
@@ -542,11 +565,10 @@
     }
 
     public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
-                                                "Need BLUETOOTH permission");
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         if (callback == null) {
-          Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!");
-          return;
+            Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!");
+            return;
         }
         Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK);
         msg.obj = callback;
@@ -554,15 +576,16 @@
     }
 
     public boolean isEnabled() {
-        if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
-            (!checkIfCallerIsForegroundUser())) {
-            Slog.w(TAG,"isEnabled(): not allowed for non-active and non system user");
+        if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
+            Slog.w(TAG, "isEnabled(): not allowed for non-active and non system user");
             return false;
         }
 
         try {
             mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) return mBluetooth.isEnabled();
+            if (mBluetooth != null) {
+                return mBluetooth.isEnabled();
+            }
         } catch (RemoteException e) {
             Slog.e(TAG, "isEnabled()", e);
         } finally {
@@ -572,15 +595,16 @@
     }
 
     public int getState() {
-        if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
-                (!checkIfCallerIsForegroundUser())) {
+        if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
             Slog.w(TAG, "getState(): report OFF for non-active and non system user");
             return BluetoothAdapter.STATE_OFF;
         }
 
         try {
             mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) return mBluetooth.getState();
+            if (mBluetooth != null) {
+                return mBluetooth.getState();
+            }
         } catch (RemoteException e) {
             Slog.e(TAG, "getState()", e);
         } finally {
@@ -592,26 +616,29 @@
     class ClientDeathRecipient implements IBinder.DeathRecipient {
         private String mPackageName;
 
-        public ClientDeathRecipient(String packageName) {
+        ClientDeathRecipient(String packageName) {
             mPackageName = packageName;
         }
 
         public void binderDied() {
-            if (DBG) Slog.d(TAG, "Binder is dead - unregister " + mPackageName);
-            if (isBleAppPresent()) {
-              // Nothing to do, another app is here.
-              return;
+            if (DBG) {
+                Slog.d(TAG, "Binder is dead - unregister " + mPackageName);
             }
-            if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash");
+            if (isBleAppPresent()) {
+                // Nothing to do, another app is here.
+                return;
+            }
+            if (DBG) {
+                Slog.d(TAG, "Disabling LE only mode after application crash");
+            }
             try {
                 mBluetoothLock.readLock().lock();
-                if (mBluetooth != null &&
-                    mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
+                if (mBluetooth != null && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
                     mEnable = false;
                     mBluetooth.onBrEdrDown();
                 }
             } catch (RemoteException e) {
-                 Slog.e(TAG,"Unable to call onBrEdrDown", e);
+                Slog.e(TAG, "Unable to call onBrEdrDown", e);
             } finally {
                 mBluetoothLock.readLock().unlock();
             }
@@ -641,15 +668,17 @@
             @Override
             public void onChange(boolean selfChange) {
                 if (isBleScanAlwaysAvailable()) {
-                  // Nothing to do
-                  return;
+                    // Nothing to do
+                    return;
                 }
                 // BLE scan is not available.
                 disableBleScanMode();
                 clearBleApps();
                 try {
                     mBluetoothLock.readLock().lock();
-                    if (mBluetooth != null) mBluetooth.onBrEdrDown();
+                    if (mBluetooth != null) {
+                        mBluetooth.onBrEdrDown();
+                    }
                 } catch (RemoteException e) {
                     Slog.e(TAG, "error when disabling bluetooth", e);
                 } finally {
@@ -659,8 +688,8 @@
         };
 
         mContentResolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE),
-                false, contentObserver);
+                Settings.Global.getUriFor(Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE), false,
+                contentObserver);
     }
 
     // Disable ble scan only mode.
@@ -668,7 +697,9 @@
         try {
             mBluetoothLock.writeLock().lock();
             if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) {
-                if (DBG) Slog.d(TAG, "Reseting the mEnable flag for clean disable");
+                if (DBG) {
+                    Slog.d(TAG, "Reseting the mEnable flag for clean disable");
+                }
                 mEnable = false;
             }
         } catch (RemoteException e) {
@@ -688,15 +719,21 @@
                 throw new IllegalArgumentException("BLE app (" + packageName + ") already dead!");
             }
             mBleApps.put(token, deathRec);
-            if (DBG) Slog.d(TAG, "Registered for death of " + packageName);
+            if (DBG) {
+                Slog.d(TAG, "Registered for death of " + packageName);
+            }
         } else if (!enable && r != null) {
             // Unregister death recipient as the app goes away.
             token.unlinkToDeath(r, 0);
             mBleApps.remove(token);
-            if (DBG) Slog.d(TAG, "Unregistered for death of " + packageName);
+            if (DBG) {
+                Slog.d(TAG, "Unregistered for death of " + packageName);
+            }
         }
         int appCount = mBleApps.size();
-        if (DBG) Slog.d(TAG, appCount + " registered Ble Apps");
+        if (DBG) {
+            Slog.d(TAG, appCount + " registered Ble Apps");
+        }
         if (appCount == 0 && mEnable) {
             disableBleScanMode();
         }
@@ -713,7 +750,9 @@
 
     /** @hide */
     public boolean isBleAppPresent() {
-        if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size());
+        if (DBG) {
+            Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size());
+        }
         return mBleApps.size() > 0;
     }
 
@@ -721,17 +760,23 @@
      * Action taken when GattService is turned on
      */
     private void onBluetoothGattServiceUp() {
-        if (DBG) Slog.d(TAG,"BluetoothGatt Service is Up");
+        if (DBG) {
+            Slog.d(TAG, "BluetoothGatt Service is Up");
+        }
         try {
             mBluetoothLock.readLock().lock();
             if (mBluetooth == null) {
-                if (DBG) Slog.w(TAG, "onBluetoothServiceUp: mBluetooth is null!");
+                if (DBG) {
+                    Slog.w(TAG, "onBluetoothServiceUp: mBluetooth is null!");
+                }
                 return;
             }
             int st = mBluetooth.getState();
             if (st != BluetoothAdapter.STATE_BLE_ON) {
-                if (DBG) Slog.v(TAG, "onBluetoothServiceUp: state isn't BLE_ON: " +
-                        BluetoothAdapter.nameForState(st));
+                if (DBG) {
+                    Slog.v(TAG, "onBluetoothServiceUp: state isn't BLE_ON: "
+                            + BluetoothAdapter.nameForState(st));
+                }
                 return;
             }
             if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) {
@@ -740,7 +785,7 @@
                 persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
             }
         } catch (RemoteException e) {
-            Slog.e(TAG,"Unable to call onServiceUp", e);
+            Slog.e(TAG, "Unable to call onServiceUp", e);
         } finally {
             mBluetoothLock.readLock().unlock();
         }
@@ -751,7 +796,9 @@
      * and turn off all service and stack if no LE app needs it
      */
     private void sendBrEdrDownCallback() {
-        if (DBG) Slog.d(TAG,"Calling sendBrEdrDownCallback callbacks");
+        if (DBG) {
+            Slog.d(TAG, "Calling sendBrEdrDownCallback callbacks");
+        }
 
         if (mBluetooth == null) {
             Slog.w(TAG, "Bluetooth handle is null");
@@ -768,7 +815,9 @@
         } else {
             try {
                 mBluetoothLock.readLock().lock();
-                if (mBluetooth != null) mBluetooth.onBrEdrDown();
+                if (mBluetooth != null) {
+                    mBluetooth.onBrEdrDown();
+                }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Call to onBrEdrDown() failed.", e);
             } finally {
@@ -778,8 +827,7 @@
 
     }
 
-    public boolean enableNoAutoConnect(String packageName)
-    {
+    public boolean enableNoAutoConnect(String packageName) {
         if (isBluetoothDisallowed()) {
             if (DBG) {
                 Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed");
@@ -788,11 +836,11 @@
         }
 
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH ADMIN permission");
+                "Need BLUETOOTH ADMIN permission");
 
         if (DBG) {
-            Slog.d(TAG,"enableNoAutoConnect():  mBluetooth =" + mBluetooth +
-                    " mBinding = " + mBinding);
+            Slog.d(TAG, "enableNoAutoConnect():  mBluetooth =" + mBluetooth + " mBinding = "
+                    + mBinding);
         }
         int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
 
@@ -800,7 +848,7 @@
             throw new SecurityException("no permission to enable Bluetooth quietly");
         }
 
-        synchronized(mReceiver) {
+        synchronized (mReceiver) {
             mQuietEnableExternal = true;
             mEnableExternal = true;
             sendEnableMsg(true, packageName);
@@ -814,7 +862,7 @@
 
         if (isBluetoothDisallowed()) {
             if (DBG) {
-                Slog.d(TAG,"enable(): not enabling - bluetooth disallowed");
+                Slog.d(TAG, "enable(): not enabling - bluetooth disallowed");
             }
             return false;
         }
@@ -828,26 +876,26 @@
             mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                     "Need BLUETOOTH ADMIN permission");
 
-            if (!isEnabled() && mPermissionReviewRequired
-                    && startConsentUiIfNeeded(packageName, callingUid,
-                            BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
+            if (!isEnabled() && mPermissionReviewRequired && startConsentUiIfNeeded(packageName,
+                    callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
                 return false;
             }
         }
 
         if (DBG) {
-            Slog.d(TAG,"enable(" + packageName + "):  mBluetooth =" + mBluetooth +
-                    " mBinding = " + mBinding + " mState = " +
-                    BluetoothAdapter.nameForState(mState));
+            Slog.d(TAG, "enable(" + packageName + "):  mBluetooth =" + mBluetooth + " mBinding = "
+                    + mBinding + " mState = " + BluetoothAdapter.nameForState(mState));
         }
 
-        synchronized(mReceiver) {
+        synchronized (mReceiver) {
             mQuietEnableExternal = false;
             mEnableExternal = true;
             // waive WRITE_SECURE_SETTINGS permission check
             sendEnableMsg(false, packageName);
         }
-        if (DBG) Slog.d(TAG, "enable returning");
+        if (DBG) {
+            Slog.d(TAG, "enable returning");
+        }
         return true;
     }
 
@@ -864,19 +912,17 @@
             mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                     "Need BLUETOOTH ADMIN permission");
 
-            if (isEnabled() && mPermissionReviewRequired
-                    && startConsentUiIfNeeded(packageName, callingUid,
-                            BluetoothAdapter.ACTION_REQUEST_DISABLE)) {
+            if (isEnabled() && mPermissionReviewRequired && startConsentUiIfNeeded(packageName,
+                    callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE)) {
                 return false;
             }
         }
 
         if (DBG) {
-            Slog.d(TAG,"disable(): mBluetooth = " + mBluetooth +
-                " mBinding = " + mBinding);
+            Slog.d(TAG, "disable(): mBluetooth = " + mBluetooth + " mBinding = " + mBinding);
         }
 
-        synchronized(mReceiver) {
+        synchronized (mReceiver) {
             if (persist) {
                 persistBluetoothSetting(BLUETOOTH_OFF);
             }
@@ -886,8 +932,8 @@
         return true;
     }
 
-    private boolean startConsentUiIfNeeded(String packageName,
-            int callingUid, String intentAction) throws RemoteException {
+    private boolean startConsentUiIfNeeded(String packageName, int callingUid, String intentAction)
+            throws RemoteException {
         try {
             // Validate the package only if we are going to use it
             ApplicationInfo applicationInfo = mContext.getPackageManager()
@@ -895,14 +941,13 @@
                             PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                             UserHandle.getUserId(callingUid));
             if (applicationInfo.uid != callingUid) {
-                throw new SecurityException("Package " + callingUid
-                        + " not in uid " + callingUid);
+                throw new SecurityException("Package " + callingUid + " not in uid " + callingUid);
             }
 
             Intent intent = new Intent(intentAction);
             intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            intent.setFlags(
+                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
             try {
                 mContext.startActivity(intent);
             } catch (ActivityNotFoundException e) {
@@ -918,13 +963,15 @@
 
     public void unbindAndFinish() {
         if (DBG) {
-            Slog.d(TAG,"unbindAndFinish(): " + mBluetooth +
-                " mBinding = " + mBinding + " mUnbinding = " + mUnbinding);
+            Slog.d(TAG, "unbindAndFinish(): " + mBluetooth + " mBinding = " + mBinding
+                    + " mUnbinding = " + mUnbinding);
         }
 
         try {
             mBluetoothLock.writeLock().lock();
-            if (mUnbinding) return;
+            if (mUnbinding) {
+                return;
+            }
             mUnbinding = true;
             mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
             mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE);
@@ -933,7 +980,7 @@
                 try {
                     mBluetooth.unregisterCallback(mBluetoothCallback);
                 } catch (RemoteException re) {
-                    Slog.e(TAG, "Unable to unregister BluetoothCallback",re);
+                    Slog.e(TAG, "Unable to unregister BluetoothCallback", re);
                 }
                 mBluetoothBinder = null;
                 mBluetooth = null;
@@ -959,8 +1006,8 @@
             IBluetoothProfileServiceConnection proxy) {
         if (!mEnable) {
             if (DBG) {
-                Slog.d(TAG, "Trying to bind to profile: " + bluetoothProfile +
-                        ", while Bluetooth was disabled");
+                Slog.d(TAG, "Trying to bind to profile: " + bluetoothProfile
+                        + ", while Bluetooth was disabled");
             }
             return false;
         }
@@ -968,15 +1015,19 @@
             ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile));
             if (psc == null) {
                 if (DBG) {
-                    Slog.d(TAG, "Creating new ProfileServiceConnections object for"
-                            + " profile: " + bluetoothProfile);
+                    Slog.d(TAG, "Creating new ProfileServiceConnections object for" + " profile: "
+                            + bluetoothProfile);
                 }
 
-                if (bluetoothProfile != BluetoothProfile.HEADSET) return false;
+                if (bluetoothProfile != BluetoothProfile.HEADSET) {
+                    return false;
+                }
 
                 Intent intent = new Intent(IBluetoothHeadset.class.getName());
                 psc = new ProfileServiceConnections(intent);
-                if (!psc.bindService()) return false;
+                if (!psc.bindService()) {
+                    return false;
+                }
 
                 mProfileServices.put(new Integer(bluetoothProfile), psc);
             }
@@ -1022,7 +1073,9 @@
      * PHASE_SYSTEM_SERVICES_READY.
      */
     public void handleOnBootPhase() {
-        if (DBG) Slog.d(TAG, "Bluetooth boot completed");
+        if (DBG) {
+            Slog.d(TAG, "Bluetooth boot completed");
+        }
         UserManagerInternal userManagerInternal =
                 LocalServices.getService(UserManagerInternal.class);
         userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
@@ -1031,10 +1084,14 @@
             return;
         }
         if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) {
-            if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth.");
+            if (DBG) {
+                Slog.d(TAG, "Auto-enabling Bluetooth.");
+            }
             sendEnableMsg(mQuietEnableExternal, REASON_SYSTEM_BOOT);
         } else if (!isNameAndAddressSet()) {
-            if (DBG) Slog.d(TAG, "Getting adapter name and address");
+            if (DBG) {
+                Slog.d(TAG, "Getting adapter name and address");
+            }
             Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
             mHandler.sendMessage(getMsg);
         }
@@ -1044,7 +1101,9 @@
      * Called when switching to a different foreground user.
      */
     public void handleOnSwitchUser(int userHandle) {
-        if (DBG) Slog.d(TAG, "User " + userHandle + " switched");
+        if (DBG) {
+            Slog.d(TAG, "User " + userHandle + " switched");
+        }
         mHandler.obtainMessage(MESSAGE_USER_SWITCHED, userHandle, 0).sendToTarget();
     }
 
@@ -1052,7 +1111,9 @@
      * Called when user is unlocked.
      */
     public void handleOnUnlockUser(int userHandle) {
-        if (DBG) Slog.d(TAG, "User " + userHandle + " unlocked");
+        if (DBG) {
+            Slog.d(TAG, "User " + userHandle + " unlocked");
+        }
         mHandler.obtainMessage(MESSAGE_USER_UNLOCKED, userHandle, 0).sendToTarget();
     }
 
@@ -1060,10 +1121,10 @@
      * This class manages the clients connected to a given ProfileService
      * and maintains the connection with that service.
      */
-    final private class ProfileServiceConnections implements ServiceConnection,
-            IBinder.DeathRecipient {
+    private final class ProfileServiceConnections
+            implements ServiceConnection, IBinder.DeathRecipient {
         final RemoteCallbackList<IBluetoothProfileServiceConnection> mProxies =
-                new RemoteCallbackList <IBluetoothProfileServiceConnection>();
+                new RemoteCallbackList<IBluetoothProfileServiceConnection>();
         IBinder mService;
         ComponentName mClassName;
         Intent mIntent;
@@ -1076,8 +1137,8 @@
         }
 
         private boolean bindService() {
-            if (mIntent != null && mService == null &&
-                    doBind(mIntent, this, 0, UserHandle.CURRENT_OR_SELF)) {
+            if (mIntent != null && mService == null && doBind(mIntent, this, 0,
+                    UserHandle.CURRENT_OR_SELF)) {
                 Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
                 msg.obj = this;
                 mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS);
@@ -1090,7 +1151,7 @@
         private void addProxy(IBluetoothProfileServiceConnection proxy) {
             mProxies.register(proxy);
             if (mService != null) {
-                try{
+                try {
                     proxy.onServiceConnected(mClassName, mService);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Unable to connect to proxy", e);
@@ -1158,7 +1219,9 @@
 
         @Override
         public void onServiceDisconnected(ComponentName className) {
-            if (mService == null) return;
+            if (mService == null) {
+                return;
+            }
             mService.unlinkToDeath(this, 0);
             mService = null;
             mClassName = null;
@@ -1187,8 +1250,7 @@
         @Override
         public void binderDied() {
             if (DBG) {
-                Slog.w(TAG, "Profile service for profile: " + mClassName
-                        + " died.");
+                Slog.w(TAG, "Profile service for profile: " + mClassName + " died.");
             }
             onServiceDisconnected(mClassName);
             // Trigger rebind
@@ -1201,12 +1263,15 @@
     private void sendBluetoothStateCallback(boolean isUp) {
         try {
             int n = mStateChangeCallbacks.beginBroadcast();
-            if (DBG) Slog.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers.");
-            for (int i=0; i <n;i++) {
+            if (DBG) {
+                Slog.d(TAG, "Broadcasting onBluetoothStateChange(" + isUp + ") to " + n
+                        + " receivers.");
+            }
+            for (int i = 0; i < n; i++) {
                 try {
                     mStateChangeCallbacks.getBroadcastItem(i).onBluetoothStateChange(isUp);
                 } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to call onBluetoothStateChange() on callback #" + i , e);
+                    Slog.e(TAG, "Unable to call onBluetoothStateChange() on callback #" + i, e);
                 }
             }
         } finally {
@@ -1220,11 +1285,11 @@
     private void sendBluetoothServiceUpCallback() {
         try {
             int n = mCallbacks.beginBroadcast();
-            Slog.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
-            for (int i=0; i <n;i++) {
+            Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
+            for (int i = 0; i < n; i++) {
                 try {
                     mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
-                }  catch (RemoteException e) {
+                } catch (RemoteException e) {
                     Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
                 }
             }
@@ -1232,17 +1297,18 @@
             mCallbacks.finishBroadcast();
         }
     }
+
     /**
      * Inform BluetoothAdapter instances that Adapter service is down
      */
     private void sendBluetoothServiceDownCallback() {
         try {
             int n = mCallbacks.beginBroadcast();
-            Slog.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
-            for (int i=0; i <n;i++) {
+            Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
+            for (int i = 0; i < n; i++) {
                 try {
                     mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
-                }  catch (RemoteException e) {
+                } catch (RemoteException e) {
                     Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
                 }
             }
@@ -1252,12 +1318,10 @@
     }
 
     public String getAddress() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
-                "Need BLUETOOTH permission");
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
 
-        if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
-                (!checkIfCallerIsForegroundUser())) {
-            Slog.w(TAG,"getAddress(): not allowed for non-active and non system user");
+        if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
+            Slog.w(TAG, "getAddress(): not allowed for non-active and non system user");
             return null;
         }
 
@@ -1268,9 +1332,13 @@
 
         try {
             mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) return mBluetooth.getAddress();
+            if (mBluetooth != null) {
+                return mBluetooth.getAddress();
+            }
         } catch (RemoteException e) {
-            Slog.e(TAG, "getAddress(): Unable to retrieve address remotely. Returning cached address", e);
+            Slog.e(TAG,
+                    "getAddress(): Unable to retrieve address remotely. Returning cached address",
+                    e);
         } finally {
             mBluetoothLock.readLock().unlock();
         }
@@ -1282,18 +1350,18 @@
     }
 
     public String getName() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
-                                                "Need BLUETOOTH permission");
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
 
-        if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
-            (!checkIfCallerIsForegroundUser())) {
-            Slog.w(TAG,"getName(): not allowed for non-active and non system user");
+        if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
+            Slog.w(TAG, "getName(): not allowed for non-active and non system user");
             return null;
         }
 
         try {
             mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) return mBluetooth.getName();
+            if (mBluetooth != null) {
+                return mBluetooth.getName();
+            }
         } catch (RemoteException e) {
             Slog.e(TAG, "getName(): Unable to retrieve name remotely. Returning cached name", e);
         } finally {
@@ -1309,7 +1377,9 @@
     private class BluetoothServiceConnection implements ServiceConnection {
         public void onServiceConnected(ComponentName componentName, IBinder service) {
             String name = componentName.getClassName();
-            if (DBG) Slog.d(TAG, "BluetoothServiceConnection: " + name);
+            if (DBG) {
+                Slog.d(TAG, "BluetoothServiceConnection: " + name);
+            }
             Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
             if (name.equals("com.android.bluetooth.btservice.AdapterService")) {
                 msg.arg1 = SERVICE_IBLUETOOTH;
@@ -1326,7 +1396,9 @@
         public void onServiceDisconnected(ComponentName componentName) {
             // Called if we unexpectedly disconnect.
             String name = componentName.getClassName();
-            if (DBG) Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + name);
+            if (DBG) {
+                Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + name);
+            }
             Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
             if (name.equals("com.android.bluetooth.btservice.AdapterService")) {
                 msg.arg1 = SERVICE_IBLUETOOTH;
@@ -1345,7 +1417,7 @@
     private class BluetoothHandler extends Handler {
         boolean mGetNameAddressOnly = false;
 
-        public BluetoothHandler(Looper looper) {
+        BluetoothHandler(Looper looper) {
             super(looper);
         }
 
@@ -1353,26 +1425,29 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MESSAGE_GET_NAME_AND_ADDRESS:
-                    if (DBG) Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS");
+                    if (DBG) {
+                        Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS");
+                    }
                     try {
                         mBluetoothLock.writeLock().lock();
                         if ((mBluetooth == null) && (!mBinding)) {
-                            if (DBG) Slog.d(TAG, "Binding to service to get name and address");
+                            if (DBG) {
+                                Slog.d(TAG, "Binding to service to get name and address");
+                            }
                             mGetNameAddressOnly = true;
                             Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
                             mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS);
                             Intent i = new Intent(IBluetooth.class.getName());
                             if (!doBind(i, mConnection,
-                                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
-                                UserHandle.CURRENT)) {
+                                    Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
+                                    UserHandle.CURRENT)) {
                                 mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
                             } else {
                                 mBinding = true;
                             }
                         } else if (mBluetooth != null) {
                             try {
-                                storeNameAndAddress(mBluetooth.getName(),
-                                                    mBluetooth.getAddress());
+                                storeNameAndAddress(mBluetooth.getName(), mBluetooth.getAddress());
                             } catch (RemoteException re) {
                                 Slog.e(TAG, "Unable to grab names", re);
                             }
@@ -1431,15 +1506,16 @@
                         // on the order of (2 * SERVICE_RESTART_TIME_MS).
                         //
                         waitForOnOff(false, true);
-                        Message restartMsg = mHandler.obtainMessage(
-                                MESSAGE_RESTART_BLUETOOTH_SERVICE);
-                        mHandler.sendMessageDelayed(restartMsg,
-                                2 * SERVICE_RESTART_TIME_MS);
+                        Message restartMsg =
+                                mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
+                        mHandler.sendMessageDelayed(restartMsg, 2 * SERVICE_RESTART_TIME_MS);
                     }
                     break;
 
                 case MESSAGE_DISABLE:
-                    if (DBG) Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth);
+                    if (DBG) {
+                        Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth);
+                    }
                     mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                     if (mEnable && mBluetooth != null) {
                         waitForOnOff(true, false);
@@ -1455,45 +1531,45 @@
                 case MESSAGE_RESTORE_USER_SETTING:
                     try {
                         if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) {
-                            if (DBG) Slog.d(TAG, "Restore Bluetooth state to disabled");
+                            if (DBG) {
+                                Slog.d(TAG, "Restore Bluetooth state to disabled");
+                            }
                             disable(REASON_RESTORE_USER_SETTING, true);
                         } else if ((msg.arg1 == RESTORE_SETTING_TO_ON) && !mEnable) {
-                            if (DBG) Slog.d(TAG, "Restore Bluetooth state to enabled");
+                            if (DBG) {
+                                Slog.d(TAG, "Restore Bluetooth state to enabled");
+                            }
                             enable(REASON_RESTORE_USER_SETTING);
                         }
                     } catch (RemoteException e) {
-                        Slog.e(TAG,"Unable to change Bluetooth On setting", e);
+                        Slog.e(TAG, "Unable to change Bluetooth On setting", e);
                     }
                     break;
 
-                case MESSAGE_REGISTER_ADAPTER:
-                {
+                case MESSAGE_REGISTER_ADAPTER: {
                     IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
                     mCallbacks.register(callback);
                     break;
                 }
-                case MESSAGE_UNREGISTER_ADAPTER:
-                {
+                case MESSAGE_UNREGISTER_ADAPTER: {
                     IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
                     mCallbacks.unregister(callback);
                     break;
                 }
-                case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK:
-                {
-                    IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
+                case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: {
+                    IBluetoothStateChangeCallback callback =
+                            (IBluetoothStateChangeCallback) msg.obj;
                     mStateChangeCallbacks.register(callback);
                     break;
                 }
-                case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK:
-                {
-                    IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
+                case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK: {
+                    IBluetoothStateChangeCallback callback =
+                            (IBluetoothStateChangeCallback) msg.obj;
                     mStateChangeCallbacks.unregister(callback);
                     break;
                 }
-                case MESSAGE_ADD_PROXY_DELAYED:
-                {
-                    ProfileServiceConnections psc = mProfileServices.get(
-                            new Integer(msg.arg1));
+                case MESSAGE_ADD_PROXY_DELAYED: {
+                    ProfileServiceConnections psc = mProfileServices.get(new Integer(msg.arg1));
                     if (psc == null) {
                         break;
                     }
@@ -1502,8 +1578,7 @@
                     psc.addProxy(proxy);
                     break;
                 }
-                case MESSAGE_BIND_PROFILE_SERVICE:
-                {
+                case MESSAGE_BIND_PROFILE_SERVICE: {
                     ProfileServiceConnections psc = (ProfileServiceConnections) msg.obj;
                     removeMessages(MESSAGE_BIND_PROFILE_SERVICE, msg.obj);
                     if (psc == null) {
@@ -1512,16 +1587,17 @@
                     psc.bindService();
                     break;
                 }
-                case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
-                {
-                    if (DBG) Slog.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
+                case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: {
+                    if (DBG) {
+                        Slog.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
+                    }
 
                     IBinder service = (IBinder) msg.obj;
                     try {
                         mBluetoothLock.writeLock().lock();
                         if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
-                            mBluetoothGatt = IBluetoothGatt.Stub
-                                    .asInterface(Binder.allowBlocking(service));
+                            mBluetoothGatt =
+                                    IBluetoothGatt.Stub.asInterface(Binder.allowBlocking(service));
                             onBluetoothGattServiceUp();
                             break;
                         } // else must be SERVICE_IBLUETOOTH
@@ -1536,31 +1612,33 @@
                         if (!isNameAndAddressSet()) {
                             Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
                             mHandler.sendMessage(getMsg);
-                            if (mGetNameAddressOnly) return;
+                            if (mGetNameAddressOnly) {
+                                return;
+                            }
                         }
 
                         //Register callback object
                         try {
                             mBluetooth.registerCallback(mBluetoothCallback);
                         } catch (RemoteException re) {
-                            Slog.e(TAG, "Unable to register BluetoothCallback",re);
+                            Slog.e(TAG, "Unable to register BluetoothCallback", re);
                         }
                         //Inform BluetoothAdapter instances that service is up
                         sendBluetoothServiceUpCallback();
 
                         //Do enable request
                         try {
-                            if (mQuietEnable == false) {
+                            if (mQuietEnable) {
                                 if (!mBluetooth.enable()) {
-                                    Slog.e(TAG,"IBluetooth.enable() returned false");
+                                    Slog.e(TAG, "IBluetooth.enable() returned false");
                                 }
                             } else {
                                 if (!mBluetooth.enableNoAutoConnect()) {
-                                    Slog.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
+                                    Slog.e(TAG, "IBluetooth.enableNoAutoConnect() returned false");
                                 }
                             }
                         } catch (RemoteException e) {
-                            Slog.e(TAG,"Unable to call enable()",e);
+                            Slog.e(TAG, "Unable to call enable()", e);
                         }
                     } finally {
                         mBluetoothLock.writeLock().unlock();
@@ -1573,43 +1651,42 @@
                     }
                     break;
                 }
-                case MESSAGE_BLUETOOTH_STATE_CHANGE:
-                {
+                case MESSAGE_BLUETOOTH_STATE_CHANGE: {
                     int prevState = msg.arg1;
                     int newState = msg.arg2;
                     if (DBG) {
-                      Slog.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: " + BluetoothAdapter.nameForState(prevState) + " > " +
-                        BluetoothAdapter.nameForState(newState));
+                        Slog.d(TAG,
+                                "MESSAGE_BLUETOOTH_STATE_CHANGE: " + BluetoothAdapter.nameForState(
+                                        prevState) + " > " + BluetoothAdapter.nameForState(
+                                        newState));
                     }
                     mState = newState;
                     bluetoothStateChangeHandler(prevState, newState);
                     // handle error state transition case from TURNING_ON to OFF
                     // unbind and rebind bluetooth service and enable bluetooth
-                    if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) &&
-                            (newState == BluetoothAdapter.STATE_OFF) &&
-                            (mBluetooth != null) && mEnable) {
+                    if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) && (newState
+                            == BluetoothAdapter.STATE_OFF) && (mBluetooth != null) && mEnable) {
                         recoverBluetoothServiceFromError(false);
                     }
-                    if ((prevState == BluetoothAdapter.STATE_TURNING_ON) &&
-                            (newState == BluetoothAdapter.STATE_BLE_ON) &&
-                            (mBluetooth != null) && mEnable) {
+                    if ((prevState == BluetoothAdapter.STATE_TURNING_ON) && (newState
+                            == BluetoothAdapter.STATE_BLE_ON) && (mBluetooth != null) && mEnable) {
                         recoverBluetoothServiceFromError(true);
                     }
                     // If we tried to enable BT while BT was in the process of shutting down,
                     // wait for the BT process to fully tear down and then force a restart
                     // here.  This is a bit of a hack (b/29363429).
-                    if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_OFF) &&
-                            (newState == BluetoothAdapter.STATE_OFF)) {
+                    if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_OFF) && (newState
+                            == BluetoothAdapter.STATE_OFF)) {
                         if (mEnable) {
                             Slog.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting.");
                             waitForOnOff(false, true);
-                            Message restartMsg = mHandler.obtainMessage(
-                                    MESSAGE_RESTART_BLUETOOTH_SERVICE);
+                            Message restartMsg =
+                                    mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                             mHandler.sendMessageDelayed(restartMsg, 2 * SERVICE_RESTART_TIME_MS);
                         }
                     }
-                    if (newState == BluetoothAdapter.STATE_ON ||
-                            newState == BluetoothAdapter.STATE_BLE_ON) {
+                    if (newState == BluetoothAdapter.STATE_ON
+                            || newState == BluetoothAdapter.STATE_BLE_ON) {
                         // bluetooth is working, reset the counter
                         if (mErrorRecoveryRetryCounter != 0) {
                             Slog.w(TAG, "bluetooth is recovered from error");
@@ -1618,14 +1695,15 @@
                     }
                     break;
                 }
-                case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
-                {
+                case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: {
                     Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED(" + msg.arg1 + ")");
                     try {
                         mBluetoothLock.writeLock().lock();
                         if (msg.arg1 == SERVICE_IBLUETOOTH) {
                             // if service is unbinded already, do nothing and return
-                            if (mBluetooth == null) break;
+                            if (mBluetooth == null) {
+                                break;
+                            }
                             mBluetooth = null;
                         } else if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
                             mBluetoothGatt = null;
@@ -1644,33 +1722,31 @@
                     if (mEnable) {
                         mEnable = false;
                         // Send a Bluetooth Restart message
-                        Message restartMsg = mHandler.obtainMessage(
-                            MESSAGE_RESTART_BLUETOOTH_SERVICE);
-                        mHandler.sendMessageDelayed(restartMsg,
-                            SERVICE_RESTART_TIME_MS);
+                        Message restartMsg =
+                                mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
+                        mHandler.sendMessageDelayed(restartMsg, SERVICE_RESTART_TIME_MS);
                     }
 
                     sendBluetoothServiceDownCallback();
 
                     // Send BT state broadcast to update
                     // the BT icon correctly
-                    if ((mState == BluetoothAdapter.STATE_TURNING_ON) ||
-                            (mState == BluetoothAdapter.STATE_ON)) {
+                    if ((mState == BluetoothAdapter.STATE_TURNING_ON) || (mState
+                            == BluetoothAdapter.STATE_ON)) {
                         bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
-                                                    BluetoothAdapter.STATE_TURNING_OFF);
+                                BluetoothAdapter.STATE_TURNING_OFF);
                         mState = BluetoothAdapter.STATE_TURNING_OFF;
                     }
                     if (mState == BluetoothAdapter.STATE_TURNING_OFF) {
                         bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
-                                                    BluetoothAdapter.STATE_OFF);
+                                BluetoothAdapter.STATE_OFF);
                     }
 
                     mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
                     mState = BluetoothAdapter.STATE_OFF;
                     break;
                 }
-                case MESSAGE_RESTART_BLUETOOTH_SERVICE:
-                {
+                case MESSAGE_RESTART_BLUETOOTH_SERVICE: {
                     Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE");
                     /* Enable without persisting the setting as
                      it doesnt change when IBluetooth
@@ -1687,8 +1763,7 @@
                     mBluetoothLock.writeLock().unlock();
                     break;
                 }
-                case MESSAGE_TIMEOUT_UNBIND:
-                {
+                case MESSAGE_TIMEOUT_UNBIND: {
                     Slog.e(TAG, "MESSAGE_TIMEOUT_UNBIND");
                     mBluetoothLock.writeLock().lock();
                     mUnbinding = false;
@@ -1697,7 +1772,9 @@
                 }
 
                 case MESSAGE_USER_SWITCHED: {
-                    if (DBG) Slog.d(TAG, "MESSAGE_USER_SWITCHED");
+                    if (DBG) {
+                        Slog.d(TAG, "MESSAGE_USER_SWITCHED");
+                    }
                     mHandler.removeMessages(MESSAGE_USER_SWITCHED);
 
                     /* disable and enable BT when detect a user switch */
@@ -1735,12 +1812,12 @@
                         handleDisable();
                         // Pbap service need receive STATE_TURNING_OFF intent to close
                         bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
-                                                    BluetoothAdapter.STATE_TURNING_OFF);
+                                BluetoothAdapter.STATE_TURNING_OFF);
 
                         boolean didDisableTimeout = !waitForOnOff(false, true);
 
                         bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
-                                                    BluetoothAdapter.STATE_OFF);
+                                BluetoothAdapter.STATE_OFF);
                         sendBluetoothServiceDownCallback();
 
                         try {
@@ -1785,14 +1862,18 @@
                     break;
                 }
                 case MESSAGE_USER_UNLOCKED: {
-                    if (DBG) Slog.d(TAG, "MESSAGE_USER_UNLOCKED");
+                    if (DBG) {
+                        Slog.d(TAG, "MESSAGE_USER_UNLOCKED");
+                    }
                     mHandler.removeMessages(MESSAGE_USER_SWITCHED);
 
                     if (mEnable && !mBinding && (mBluetooth == null)) {
                         // We should be connected, but we gave up for some
                         // reason; maybe the Bluetooth service wasn't encryption
                         // aware, so try binding again.
-                        if (DBG) Slog.d(TAG, "Enabled but not bound; retrying after unlock");
+                        if (DBG) {
+                            Slog.d(TAG, "Enabled but not bound; retrying after unlock");
+                        }
                         handleEnable(mQuietEnable);
                     }
                 }
@@ -1807,10 +1888,10 @@
             mBluetoothLock.writeLock().lock();
             if ((mBluetooth == null) && (!mBinding)) {
                 //Start bind timeout and bind
-                Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
-                mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
+                Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
+                mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS);
                 Intent i = new Intent(IBluetooth.class.getName());
-                if (!doBind(i, mConnection,Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
+                if (!doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
                         UserHandle.CURRENT)) {
                     mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
                 } else {
@@ -1820,17 +1901,16 @@
                 //Enable bluetooth
                 try {
                     if (!mQuietEnable) {
-                        if(!mBluetooth.enable()) {
-                            Slog.e(TAG,"IBluetooth.enable() returned false");
+                        if (!mBluetooth.enable()) {
+                            Slog.e(TAG, "IBluetooth.enable() returned false");
                         }
-                    }
-                    else {
-                        if(!mBluetooth.enableNoAutoConnect()) {
-                            Slog.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
+                    } else {
+                        if (!mBluetooth.enableNoAutoConnect()) {
+                            Slog.e(TAG, "IBluetooth.enableNoAutoConnect() returned false");
                         }
                     }
                 } catch (RemoteException e) {
-                    Slog.e(TAG,"Unable to call enable()",e);
+                    Slog.e(TAG, "Unable to call enable()", e);
                 }
             }
         } finally {
@@ -1852,13 +1932,15 @@
         try {
             mBluetoothLock.readLock().lock();
             if (mBluetooth != null) {
-                if (DBG) Slog.d(TAG,"Sending off request.");
+                if (DBG) {
+                    Slog.d(TAG, "Sending off request.");
+                }
                 if (!mBluetooth.disable()) {
-                    Slog.e(TAG,"IBluetooth.disable() returned false");
+                    Slog.e(TAG, "IBluetooth.disable() returned false");
                 }
             }
         } catch (RemoteException e) {
-            Slog.e(TAG,"Unable to call disable()",e);
+            Slog.e(TAG, "Unable to call disable()", e);
         } finally {
             mBluetoothLock.readLock().unlock();
         }
@@ -1876,15 +1958,12 @@
         boolean valid = false;
         try {
             foregroundUser = ActivityManager.getCurrentUser();
-            valid = (callingUser == foregroundUser) ||
-                    parentUser == foregroundUser    ||
-                    callingAppId == Process.NFC_UID ||
-                    callingAppId == mSystemUiUid;
+            valid = (callingUser == foregroundUser) || parentUser == foregroundUser
+                    || callingAppId == Process.NFC_UID || callingAppId == mSystemUiUid;
             if (DBG && !valid) {
-                Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid
-                    + " callingUser=" + callingUser
-                    + " parentUser=" + parentUser
-                    + " foregroundUser=" + foregroundUser);
+                Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid + " callingUser="
+                        + callingUser + " parentUser=" + parentUser + " foregroundUser="
+                        + foregroundUser);
             }
         } finally {
             Binder.restoreCallingIdentity(callingIdentity);
@@ -1893,8 +1972,11 @@
     }
 
     private void sendBleStateChanged(int prevState, int newState) {
-        if (DBG) Slog.d(TAG,"Sending BLE State Change: " + BluetoothAdapter.nameForState(prevState) +
-            " > " + BluetoothAdapter.nameForState(newState));
+        if (DBG) {
+            Slog.d(TAG,
+                    "Sending BLE State Change: " + BluetoothAdapter.nameForState(prevState) + " > "
+                            + BluetoothAdapter.nameForState(newState));
+        }
         // Send broadcast message to everyone else
         Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
         intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
@@ -1909,14 +1991,15 @@
             return;
         }
         // Notify all proxy objects first of adapter state change
-        if (newState == BluetoothAdapter.STATE_BLE_ON ||
-                newState == BluetoothAdapter.STATE_OFF) {
+        if (newState == BluetoothAdapter.STATE_BLE_ON || newState == BluetoothAdapter.STATE_OFF) {
             boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF
-               && newState == BluetoothAdapter.STATE_BLE_ON);
+                    && newState == BluetoothAdapter.STATE_BLE_ON);
 
             if (newState == BluetoothAdapter.STATE_OFF) {
                 // If Bluetooth is off, send service down event to proxy objects, and unbind
-                if (DBG) Slog.d(TAG, "Bluetooth is complete send Service Down");
+                if (DBG) {
+                    Slog.d(TAG, "Bluetooth is complete send Service Down");
+                }
                 sendBluetoothServiceDownCallback();
                 unbindAndFinish();
                 sendBleStateChanged(prevState, newState);
@@ -1925,16 +2008,23 @@
 
             } else if (!intermediate_off) {
                 // connect to GattService
-                if (DBG) Slog.d(TAG, "Bluetooth is in LE only mode");
+                if (DBG) {
+                    Slog.d(TAG, "Bluetooth is in LE only mode");
+                }
                 if (mBluetoothGatt != null) {
-                    if (DBG) Slog.d(TAG, "Calling BluetoothGattServiceUp");
+                    if (DBG) {
+                        Slog.d(TAG, "Calling BluetoothGattServiceUp");
+                    }
                     onBluetoothGattServiceUp();
                 } else {
-                    if (DBG) Slog.d(TAG, "Binding Bluetooth GATT service");
-                    if (mContext.getPackageManager().hasSystemFeature(
-                                                    PackageManager.FEATURE_BLUETOOTH_LE)) {
+                    if (DBG) {
+                        Slog.d(TAG, "Binding Bluetooth GATT service");
+                    }
+                    if (mContext.getPackageManager()
+                            .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
                         Intent i = new Intent(IBluetoothGatt.class.getName());
-                        doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT);
+                        doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
+                                UserHandle.CURRENT);
                     }
                 }
                 sendBleStateChanged(prevState, newState);
@@ -1942,7 +2032,9 @@
                 isStandardBroadcast = false;
 
             } else if (intermediate_off) {
-                if (DBG) Slog.d(TAG, "Intermediate off, back to LE only mode");
+                if (DBG) {
+                    Slog.d(TAG, "Intermediate off, back to LE only mode");
+                }
                 // For LE only mode, broadcast as is
                 sendBleStateChanged(prevState, newState);
                 sendBluetoothStateCallback(false); // BT is OFF for general users
@@ -1955,13 +2047,13 @@
             sendBluetoothStateCallback(isUp);
             sendBleStateChanged(prevState, newState);
 
-        } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON ||
-                newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) {
+        } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON
+                || newState == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
             sendBleStateChanged(prevState, newState);
             isStandardBroadcast = false;
 
-        } else if (newState == BluetoothAdapter.STATE_TURNING_ON ||
-                newState == BluetoothAdapter.STATE_TURNING_OFF) {
+        } else if (newState == BluetoothAdapter.STATE_TURNING_ON
+                || newState == BluetoothAdapter.STATE_TURNING_OFF) {
             sendBleStateChanged(prevState, newState);
         }
 
@@ -1988,13 +2080,21 @@
         while (i < 10) {
             try {
                 mBluetoothLock.readLock().lock();
-                if (mBluetooth == null) break;
+                if (mBluetooth == null) {
+                    break;
+                }
                 if (on) {
-                    if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true;
+                    if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) {
+                        return true;
+                    }
                 } else if (off) {
-                    if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true;
+                    if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) {
+                        return true;
+                    }
                 } else {
-                    if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true;
+                    if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) {
+                        return true;
+                    }
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "getState()", e);
@@ -2009,7 +2109,7 @@
             }
             i++;
         }
-        Slog.e(TAG,"waitForOnOff time out");
+        Slog.e(TAG, "waitForOnOff time out");
         return false;
     }
 
@@ -2019,8 +2119,7 @@
     }
 
     private void sendEnableMsg(boolean quietMode, String packageName) {
-        mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE,
-                             quietMode ? 1 : 0, 0));
+        mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0));
         addActiveLog(packageName, true);
         mLastEnabledTime = SystemClock.elapsedRealtime();
     }
@@ -2035,15 +2134,17 @@
     }
 
     private void addCrashLog() {
-      synchronized (mCrashTimestamps) {
-        if (mCrashTimestamps.size() == CRASH_LOG_MAX_SIZE) mCrashTimestamps.removeFirst();
-        mCrashTimestamps.add(System.currentTimeMillis());
-        mCrashes++;
-      }
+        synchronized (mCrashTimestamps) {
+            if (mCrashTimestamps.size() == CRASH_LOG_MAX_SIZE) {
+                mCrashTimestamps.removeFirst();
+            }
+            mCrashTimestamps.add(System.currentTimeMillis());
+            mCrashes++;
+        }
     }
 
     private void recoverBluetoothServiceFromError(boolean clearBle) {
-        Slog.e(TAG,"recoverBluetoothServiceFromError");
+        Slog.e(TAG, "recoverBluetoothServiceFromError");
         try {
             mBluetoothLock.readLock().lock();
             if (mBluetooth != null) {
@@ -2082,15 +2183,14 @@
         mState = BluetoothAdapter.STATE_OFF;
 
         if (clearBle) {
-          clearBleApps();
+            clearBleApps();
         }
 
         mEnable = false;
 
         if (mErrorRecoveryRetryCounter++ < MAX_ERROR_RESTART_RETRIES) {
             // Send a Bluetooth Restart message to reenable bluetooth
-            Message restartMsg = mHandler.obtainMessage(
-                             MESSAGE_RESTART_BLUETOOTH_SERVICE);
+            Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
             mHandler.sendMessageDelayed(restartMsg, ERROR_RESTART_TIME_MS);
         } else {
             // todo: notify user to power down and power up phone to make bluetooth work.
@@ -2118,9 +2218,9 @@
     private void updateOppLauncherComponentState(int userId, boolean bluetoothSharingDisallowed) {
         final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth",
                 "com.android.bluetooth.opp.BluetoothOppLauncherActivity");
-        final int newState = bluetoothSharingDisallowed
-                ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
-                : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+        final int newState =
+                bluetoothSharingDisallowed ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+                        : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
         try {
             final IPackageManager imp = AppGlobals.getPackageManager();
             imp.setComponentEnabledSetting(oppLauncherComponent, newState,
@@ -2132,7 +2232,9 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) {
+            return;
+        }
         String errorMsg = null;
 
         boolean protoOut = (args.length > 0) && args[0].startsWith("--proto");
@@ -2145,11 +2247,10 @@
             writer.println("  name: " + mName);
             if (mEnable) {
                 long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime;
-                String onDurationString = String.format("%02d:%02d:%02d.%03d",
-                                          (int)(onDuration / (1000 * 60 * 60)),
-                                          (int)((onDuration / (1000 * 60)) % 60),
-                                          (int)((onDuration / 1000) % 60),
-                                          (int)(onDuration % 1000));
+                String onDurationString = String.format(Locale.US, "%02d:%02d:%02d.%03d",
+                        (int) (onDuration / (1000 * 60 * 60)),
+                        (int) ((onDuration / (1000 * 60)) % 60), (int) ((onDuration / 1000) % 60),
+                        (int) (onDuration % 1000));
                 writer.println("  time since enabled: " + onDurationString);
             }
 
@@ -2162,14 +2263,17 @@
                 }
             }
 
-            writer.println("\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s"));
-            if (mCrashes == CRASH_LOG_MAX_SIZE) writer.println("(last " + CRASH_LOG_MAX_SIZE + ")");
+            writer.println(
+                    "\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s"));
+            if (mCrashes == CRASH_LOG_MAX_SIZE) {
+                writer.println("(last " + CRASH_LOG_MAX_SIZE + ")");
+            }
             for (Long time : mCrashTimestamps) {
-              writer.println("  " + timeToLog(time.longValue()));
+                writer.println("  " + timeToLog(time));
             }
 
-            writer.println("\n" + mBleApps.size() + " BLE app" +
-                            (mBleApps.size() == 1 ? "" : "s") + "registered");
+            writer.println("\n" + mBleApps.size() + " BLE app" + (mBleApps.size() == 1 ? "" : "s")
+                    + "registered");
             for (ClientDeathRecipient app : mBleApps.values()) {
                 writer.println("  " + app.getPackageName());
             }
@@ -2194,7 +2298,9 @@
         }
         if (errorMsg != null) {
             // Silently return if we are extracting metrics in Protobuf format
-            if (protoOut) return;
+            if (protoOut) {
+                return;
+            }
             writer.println(errorMsg);
         }
     }
diff --git a/services/core/java/com/android/server/DisplayThread.java b/services/core/java/com/android/server/DisplayThread.java
index cad2a61..85c799c 100644
--- a/services/core/java/com/android/server/DisplayThread.java
+++ b/services/core/java/com/android/server/DisplayThread.java
@@ -40,7 +40,7 @@
         if (sInstance == null) {
             sInstance = new DisplayThread();
             sInstance.start();
-            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
             sHandler = new Handler(sInstance.getLooper());
         }
     }
diff --git a/services/core/java/com/android/server/FgThread.java b/services/core/java/com/android/server/FgThread.java
index 18fb477..021bfaa 100644
--- a/services/core/java/com/android/server/FgThread.java
+++ b/services/core/java/com/android/server/FgThread.java
@@ -39,7 +39,7 @@
         if (sInstance == null) {
             sInstance = new FgThread();
             sInstance.start();
-            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
             sHandler = new Handler(sInstance.getLooper());
         }
     }
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index fc57a0d..dc35051 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -108,6 +108,8 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
 import android.text.TextUtils;
 import android.text.style.SuggestionSpan;
 import android.util.ArrayMap;
@@ -189,6 +191,7 @@
     static final int MSG_CREATE_SESSION = 1050;
 
     static final int MSG_START_INPUT = 2000;
+    static final int MSG_START_VR_INPUT = 2010;
 
     static final int MSG_UNBIND_CLIENT = 3000;
     static final int MSG_BIND_CLIENT = 3010;
@@ -317,6 +320,28 @@
         }
     }
 
+    /**
+     * VR state callback.
+     * Listens for when VR mode finishes.
+     */
+    private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
+        @Override
+        public void onVrStateChanged(boolean enabled) {
+            if (!enabled) {
+                restoreNonVrImeFromSettingsNoCheck();
+            }
+        }
+    };
+
+    private void restoreNonVrImeFromSettingsNoCheck() {
+        // switch back to non-VR InputMethod from settings.
+        synchronized (mMethodMap) {
+            final String lastInputId = mSettings.getSelectedInputMethod();
+            setInputMethodLocked(lastInputId,
+                    mSettings.getSelectedInputMethodSubtypeId(lastInputId));
+        }
+    }
+
     static final class ClientState {
         final IInputMethodClient client;
         final IInputContext inputContext;
@@ -863,6 +888,30 @@
     }
 
     /**
+     * Start a VR InputMethod that matches IME with package name of {@param component}.
+     * Note: This method is called from {@link VrManager}.
+     */
+    private void startVrInputMethodNoCheck(@Nullable ComponentName component) {
+        if (component == null) {
+            // clear the current VR-only IME (if any) and restore normal IME.
+            restoreNonVrImeFromSettingsNoCheck();
+            return;
+        }
+
+        synchronized (mMethodMap) {
+            String packageName = component.getPackageName();
+            for (InputMethodInfo info : mMethodList) {
+                if (TextUtils.equals(info.getPackageName(), packageName) && info.isVrOnly()) {
+                    // set this is as current inputMethod without updating settings.
+                    setInputMethodEnabled(info.getId(), true);
+                    setInputMethodLocked(info.getId(), NOT_A_SUBTYPE_ID);
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
      * Handles {@link Intent#ACTION_LOCALE_CHANGED}.
      *
      * <p>Note: For historical reasons, {@link Intent#ACTION_LOCALE_CHANGED} has been sent to all
@@ -1338,6 +1387,15 @@
         mFileManager = new InputMethodFileManager(mMethodMap, userId);
         mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(
                 mSettings, context);
+        // Register VR-state listener.
+        IVrManager vrManager = (IVrManager) ServiceManager.getService(Context.VR_SERVICE);
+        if (vrManager != null) {
+            try {
+                vrManager.registerListener(mVrStateCallbacks);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to register VR mode state listener.");
+            }
+        }
     }
 
     private void resetDefaultImeLocked(Context context) {
@@ -1562,12 +1620,27 @@
 
     @Override
     public List<InputMethodInfo> getInputMethodList() {
+        return getInputMethodList(false /* isVrOnly */);
+    }
+
+    public List<InputMethodInfo> getVrInputMethodList() {
+        return getInputMethodList(true /* isVrOnly */);
+    }
+
+    private List<InputMethodInfo> getInputMethodList(final boolean isVrOnly) {
         // TODO: Make this work even for non-current users?
         if (!calledFromValidUser()) {
             return Collections.emptyList();
         }
         synchronized (mMethodMap) {
-            return new ArrayList<>(mMethodList);
+            ArrayList<InputMethodInfo> methodList = new ArrayList<>();
+            for (InputMethodInfo info : mMethodList) {
+
+                if (info.isVrOnly() == isVrOnly) {
+                    methodList.add(info);
+                }
+            }
+            return methodList;
         }
     }
 
@@ -3356,6 +3429,9 @@
             case MSG_SET_INTERACTIVE:
                 handleSetInteractive(msg.arg1 != 0);
                 return true;
+            case MSG_START_VR_INPUT:
+                startVrInputMethodNoCheck((ComponentName) msg.obj);
+                return true;
             case MSG_SWITCH_IME:
                 handleSwitchInputMethod(msg.arg1 != 0);
                 return true;
@@ -3876,8 +3952,12 @@
 
     private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
             boolean setSubtypeOnly) {
-        // Update the history of InputMethod and Subtype
-        mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
+        // Updates to InputMethod are transient in VR mode. Its not included in history.
+        final boolean isVrInput = imi != null && imi.isVrOnly();
+        if (!isVrInput) {
+            // Update the history of InputMethod and Subtype
+            mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
+        }
 
         mCurUserActionNotificationSequenceNumber =
                 Math.max(mCurUserActionNotificationSequenceNumber + 1, 1);
@@ -3892,6 +3972,11 @@
                     mCurUserActionNotificationSequenceNumber, mCurClient));
         }
 
+        if (isVrInput) {
+            // Updates to InputMethod are transient in VR mode. Any changes to Settings are skipped.
+            return;
+        }
+
         // Set Subtype here
         if (imi == null || subtypeId < 0) {
             mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
@@ -4351,6 +4436,11 @@
             mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
             mHandler.sendEmptyMessage(MSG_HIDE_CURRENT_INPUT_METHOD);
         }
+
+        @Override
+        public void startVrInputMethodNoCheck(@Nullable ComponentName componentName) {
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_START_VR_INPUT, componentName));
+        }
     }
 
     private static String imeWindowStatusToString(final int imeWindowVis) {
diff --git a/services/core/java/com/android/server/IoThread.java b/services/core/java/com/android/server/IoThread.java
index ad4c194..bfe825a 100644
--- a/services/core/java/com/android/server/IoThread.java
+++ b/services/core/java/com/android/server/IoThread.java
@@ -36,7 +36,7 @@
         if (sInstance == null) {
             sInstance = new IoThread();
             sInstance.start();
-            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
             sHandler = new Handler(sInstance.getLooper());
         }
     }
diff --git a/services/core/java/com/android/server/UiThread.java b/services/core/java/com/android/server/UiThread.java
index fd88d26..f813074 100644
--- a/services/core/java/com/android/server/UiThread.java
+++ b/services/core/java/com/android/server/UiThread.java
@@ -47,7 +47,7 @@
             sInstance = new UiThread();
             sInstance.start();
             final Looper looper = sInstance.getLooper();
-            looper.setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+            looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
             looper.setSlowDispatchThresholdMs(SLOW_DISPATCH_THRESHOLD_MS);
             sHandler = new Handler(sInstance.getLooper());
         }
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index b11b16e1..9bfdd0c 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -65,6 +65,12 @@
     static final int POSITION_TOP = Integer.MAX_VALUE;
     static final int POSITION_BOTTOM = Integer.MIN_VALUE;
 
+
+    /**
+     * Counter for next free stack ID to use for dynamic activity stacks. Unique across displays.
+     */
+    private static int sNextFreeStackId = 0;
+
     private ActivityStackSupervisor mSupervisor;
     /** Actual Display this object tracks. */
     int mDisplayId;
@@ -231,6 +237,10 @@
         return getOrCreateStack(windowingMode, activityType, onTop);
     }
 
+    private int getNextStackId() {
+        return sNextFreeStackId++;
+    }
+
     /**
      * Creates a stack matching the input windowing mode and activity type on this display.
      * @param windowingMode The windowing mode the stack should be created in. If
@@ -278,7 +288,7 @@
             }
         }
 
-        final int stackId = mSupervisor.getNextStackId();
+        final int stackId = getNextStackId();
         return createStackUnchecked(windowingMode, activityType, stackId, onTop);
     }
 
@@ -423,7 +433,9 @@
                         || !otherStack.affectedBySplitScreenResize()) {
                     continue;
                 }
-                otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+                        false /* animate */, false /* showRecents */,
+                        false /* sendNonResizeableNotification */);
             }
         } finally {
             mSupervisor.mWindowManager.continueSurfaceLayout();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5e67396..982380c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -250,6 +250,7 @@
 import android.app.backup.IBackupManager;
 import android.app.servertransaction.ConfigurationChangeItem;
 import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManagerInternal;
 import android.appwidget.AppWidgetManager;
 import android.content.ActivityNotFoundException;
@@ -4522,9 +4523,18 @@
         userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, ALLOW_FULL_ONLY, "startActivity", null);
         // TODO: Switch to user app stacks here.
-        return mActivityStartController.startActivityMayWait(caller, -1, callingPackage,
-                intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
-                profilerInfo, null, null, bOptions, false, userId, null, "startActivityAsUser");
+        return mActivityStartController.obtainStarter(intent, "startActivityAsUser")
+                .setCaller(caller)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setResultTo(resultTo)
+                .setResultWho(resultWho)
+                .setRequestCode(requestCode)
+                .setStartFlags(startFlags)
+                .setProfilerInfo(profilerInfo)
+                .setMayWait(bOptions, userId)
+                .execute();
+
     }
 
     @Override
@@ -4585,11 +4595,17 @@
 
         // TODO: Switch to user app stacks here.
         try {
-            int ret = mActivityStartController.startActivityMayWait(null, targetUid,
-                    targetPackage, intent, resolvedType, null, null, resultTo, resultWho,
-                    requestCode, startFlags, null, null, null, bOptions, ignoreTargetSecurity,
-                    userId, null, "startActivityAsCaller");
-            return ret;
+            return mActivityStartController.obtainStarter(intent, "startActivityAsCaller")
+                    .setCallingUid(targetUid)
+                    .setCallingPackage(targetPackage)
+                    .setResolvedType(resolvedType)
+                    .setResultTo(resultTo)
+                    .setResultWho(resultWho)
+                    .setRequestCode(requestCode)
+                    .setStartFlags(startFlags)
+                    .setMayWait(bOptions, userId)
+                    .setIgnoreTargetSecurity(ignoreTargetSecurity)
+                    .execute();
         } catch (SecurityException e) {
             // XXX need to figure out how to propagate to original app.
             // A SecurityException here is generally actually a fault of the original
@@ -4615,9 +4631,18 @@
                 userId, false, ALLOW_FULL_ONLY, "startActivityAndWait", null);
         WaitResult res = new WaitResult();
         // TODO: Switch to user app stacks here.
-        mActivityStartController.startActivityMayWait(caller, -1, callingPackage, intent,
-                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
-                profilerInfo, res, null, bOptions, false, userId, null, "startActivityAndWait");
+        mActivityStartController.obtainStarter(intent, "startActivityAndWait")
+                .setCaller(caller)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setResultTo(resultTo)
+                .setResultWho(resultWho)
+                .setRequestCode(requestCode)
+                .setStartFlags(startFlags)
+                .setMayWait(bOptions, userId)
+                .setProfilerInfo(profilerInfo)
+                .setWaitResult(res)
+                .execute();
         return res;
     }
 
@@ -4629,10 +4654,17 @@
         userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, ALLOW_FULL_ONLY, "startActivityWithConfig", null);
         // TODO: Switch to user app stacks here.
-        int ret = mActivityStartController.startActivityMayWait(caller, -1, callingPackage, intent,
-                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, null, null,
-                config, bOptions, false, userId, null, "startActivityWithConfig");
-        return ret;
+        return mActivityStartController.obtainStarter(intent, "startActivityWithConfig")
+                .setCaller(caller)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setResultTo(resultTo)
+                .setResultWho(resultWho)
+                .setRequestCode(requestCode)
+                .setStartFlags(startFlags)
+                .setGlobalConfiguration(config)
+                .setMayWait(bOptions, userId)
+                .execute();
     }
 
     @Override
@@ -4678,9 +4710,16 @@
         userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
                 ALLOW_FULL_ONLY, "startVoiceActivity", null);
         // TODO: Switch to user app stacks here.
-        return mActivityStartController.startActivityMayWait(null, callingUid, callingPackage,
-                intent, resolvedType, session, interactor, null, null, 0, startFlags, profilerInfo,
-                null, null, bOptions, false, userId, null, "startVoiceActivity");
+        return mActivityStartController.obtainStarter(intent, "startVoiceActivity")
+                .setCallingUid(callingUid)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setVoiceSession(session)
+                .setVoiceInteractor(interactor)
+                .setStartFlags(startFlags)
+                .setProfilerInfo(profilerInfo)
+                .setMayWait(bOptions, userId)
+                .execute();
     }
 
     @Override
@@ -4689,9 +4728,13 @@
         enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
         userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
                 ALLOW_FULL_ONLY, "startAssistantActivity", null);
-        return mActivityStartController.startActivityMayWait(null, callingUid, callingPackage,
-                intent, resolvedType, null, null, null, null, 0, 0, null, null, null, bOptions,
-                false, userId, null, "startAssistantActivity");
+
+        return mActivityStartController.obtainStarter(intent, "startAssistantActivity")
+                .setCallingUid(callingUid)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setMayWait(bOptions, userId)
+                .execute();
     }
 
     @Override
@@ -4731,9 +4774,12 @@
                 intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
                 intent.setComponent(recentsComponent);
                 intent.putExtras(options);
-                return mActivityStartController.startActivityMayWait(null, recentsUid,
-                        recentsPackage, intent, null, null, null, null, null, 0, 0, null, null,
-                        null, activityOptions, false, userId, null, "startRecentsActivity");
+
+                return mActivityStartController.obtainStarter(intent, "startRecentsActivity")
+                        .setCallingUid(recentsUid)
+                        .setCallingPackage(recentsPackage)
+                        .setMayWait(activityOptions, userId)
+                        .execute();
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -4906,11 +4952,22 @@
             }
 
             final long origId = Binder.clearCallingIdentity();
-            int res = mActivityStartController.startActivity(r.app.thread, intent,
-                    null /*ephemeralIntent*/, r.resolvedType, aInfo, null /*rInfo*/, null,
-                    null, resultTo != null ? resultTo.appToken : null, resultWho, requestCode, -1,
-                    r.launchedFromUid, r.launchedFromPackage, -1, r.launchedFromUid, 0, options,
-                    false, false, null, null, "startNextMatchingActivity");
+            // TODO(b/64750076): Check if calling pid should really be -1.
+            final int res = mActivityStartController
+                    .obtainStarter(intent, "startNextMatchingActivity")
+                    .setCaller(r.app.thread)
+                    .setResolvedType(r.resolvedType)
+                    .setActivityInfo(aInfo)
+                    .setResultTo(resultTo != null ? resultTo.appToken : null)
+                    .setResultWho(resultWho)
+                    .setRequestCode(requestCode)
+                    .setCallingPid(-1)
+                    .setCallingUid(r.launchedFromUid)
+                    .setCallingPackage(r.launchedFromPackage)
+                    .setRealCallingPid(-1)
+                    .setRealCallingUid(r.launchedFromUid)
+                    .setActivityOptions(options)
+                    .execute();
             Binder.restoreCallingIdentity(origId);
 
             r.finishing = wasFinishing;
@@ -8820,7 +8877,10 @@
         final int callingAppId = UserHandle.getAppId(callingUid);
         if ((callingAppId == SYSTEM_UID) || (callingAppId == ROOT_UID)) {
             if ("com.android.settings.files".equals(grantUri.uri.getAuthority())) {
-                // Exempted authority for cropping user photos in Settings app
+                // Exempted authority for
+                // 1. cropping user photos and sharing a generated license html
+                //    file in Settings app
+                // 2. sharing a generated license html file in TvSettings app
             } else {
                 Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission"
                         + " grant to " + grantUri + "; use startActivityAsCaller() instead");
@@ -9964,7 +10024,7 @@
                     }
                 }
 
-                TaskRecord task = new TaskRecord(this,
+                TaskRecord task = TaskRecord.create(this,
                         mStackSupervisor.getNextTaskIdForUserLocked(r.userId),
                         ainfo, intent, description);
                 if (!mRecentTasks.addToBottom(task)) {
@@ -10510,7 +10570,8 @@
                 if (toTop) {
                     stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
                 }
-                stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents);
+                stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents,
+                        true /* sendNonResizeableNotification */);
                 return windowingMode != task.getWindowingMode();
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -14542,7 +14603,7 @@
                 try {
                     PackageInfo pi = pm.getPackageInfo(pkg, 0, UserHandle.getCallingUserId());
                     if (pi != null) {
-                        sb.append(" v").append(pi.versionCode);
+                        sb.append(" v").append(pi.getLongVersionCode());
                         if (pi.versionName != null) {
                             sb.append(" (").append(pi.versionName).append(")");
                         }
@@ -20168,6 +20229,11 @@
             // Instrumentation can kill and relaunch even persistent processes
             forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId,
                     "start instr");
+            // Inform usage stats to make the target package active
+            if (mUsageStatsService != null) {
+                mUsageStatsService.reportEvent(ii.targetPackage, userId,
+                        UsageEvents.Event.SYSTEM_INTERACTION);
+            }
             ProcessRecord app = addAppLocked(ai, defProcess, false, abiOverride);
             app.instr = activeInstr;
             activeInstr.mFinished = false;
@@ -23824,7 +23890,13 @@
      */
     @Override
     public boolean startUserInBackground(final int userId) {
-        return mUserController.startUser(userId, /* foreground */ false);
+        return startUserInBackgroundWithListener(userId, null);
+    }
+
+    @Override
+    public boolean startUserInBackgroundWithListener(final int userId,
+                @Nullable IProgressListener unlockListener) {
+        return mUserController.startUser(userId, /* foreground */ false, unlockListener);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index af4d3f8..fcf9946 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -483,10 +483,12 @@
 
     @Override
     public void setWindowingMode(int windowingMode) {
-        setWindowingMode(windowingMode, false /* animate */, true /* showRecents */);
+        setWindowingMode(windowingMode, false /* animate */, true /* showRecents */,
+                true /* sendNonResizeableNotification */);
     }
 
-    void setWindowingMode(int preferredWindowingMode, boolean animate, boolean showRecents) {
+    void setWindowingMode(int preferredWindowingMode, boolean animate, boolean showRecents,
+            boolean sendNonResizeableNotification) {
         final int currentMode = getWindowingMode();
         final ActivityDisplay display = getDisplay();
         final TaskRecord topTask = topTask();
@@ -505,7 +507,8 @@
         final boolean alreadyInSplitScreenMode = display.hasSplitScreenPrimaryStack();
 
         // Take any required action due to us not supporting the preferred windowing mode.
-        if (windowingMode != preferredWindowingMode && isActivityTypeStandardOrUndefined()) {
+        if (sendNonResizeableNotification
+                && windowingMode != preferredWindowingMode && isActivityTypeStandardOrUndefined()) {
             if (alreadyInSplitScreenMode
                     && (preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
                     || preferredWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)) {
@@ -524,8 +527,9 @@
         final WindowManagerService wm = mService.mWindowManager;
         final ActivityRecord topActivity = getTopActivity();
 
-        if (windowingMode != WINDOWING_MODE_FULLSCREEN && topActivity != null
-                && topActivity.isNonResizableOrForcedResizable() && !topActivity.noDisplay) {
+        if (sendNonResizeableNotification && windowingMode != WINDOWING_MODE_FULLSCREEN
+                && topActivity != null && topActivity.isNonResizableOrForcedResizable()
+                && !topActivity.noDisplay) {
             // Inform the user that they are starting an app that may not work correctly in
             // multi-window mode.
             final String packageName = topActivity.appInfo.packageName;
@@ -3899,12 +3903,19 @@
                 try {
                     ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
                             destIntent.getComponent(), 0, srec.userId);
-                    int res = mService.getActivityStartController().startActivity(
-                            srec.app.thread, destIntent, null /*ephemeralIntent*/, null, aInfo,
-                            null /*rInfo*/, null, null, parent.appToken, null, 0, -1,
-                            parent.launchedFromUid, parent.launchedFromPackage, -1,
-                            parent.launchedFromUid, 0, null, false, true, null, null,
-                            "navigateUpTo");
+                    // TODO(b/64750076): Check if calling pid should really be -1.
+                    final int res = mService.getActivityStartController()
+                            .obtainStarter(destIntent, "navigateUpTo")
+                            .setCaller(srec.app.thread)
+                            .setActivityInfo(aInfo)
+                            .setResultTo(parent.appToken)
+                            .setCallingPid(-1)
+                            .setCallingUid(parent.launchedFromUid)
+                            .setCallingPackage(parent.launchedFromPackage)
+                            .setRealCallingPid(-1)
+                            .setRealCallingUid(parent.launchedFromUid)
+                            .setComponentSpecified(true)
+                            .execute();
                     foundParentInTask = res == ActivityManager.START_SUCCESS;
                 } catch (RemoteException e) {
                     foundParentInTask = false;
@@ -5015,8 +5026,8 @@
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             boolean toTop, ActivityRecord activity, ActivityRecord source,
             ActivityOptions options) {
-        final TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession,
-                voiceInteractor);
+        final TaskRecord task = TaskRecord.create(
+                mService, taskId, info, intent, voiceSession, voiceInteractor);
         // add the task to stack first, mTaskPositioner might need the stack association
         addTask(task, toTop, "createTaskRecord");
         final boolean isLockscreenShown = mService.mStackSupervisor.getKeyguardController()
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 48c08a5..7561d0f 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -114,7 +114,11 @@
 import android.app.WaitResult;
 import android.app.WindowConfiguration.ActivityType;
 import android.app.WindowConfiguration.WindowingMode;
+import android.app.servertransaction.ActivityLifecycleItem;
+import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.LaunchActivityItem;
+import android.app.servertransaction.PauseActivityItem;
+import android.app.servertransaction.ResumeActivityItem;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -298,9 +302,6 @@
 
     private LaunchingBoundsController mLaunchingBoundsController;
 
-    /** Counter for next free stack ID to use for dynamic activity stacks. */
-    private int mNextFreeStackId = 0;
-
     /**
      * Maps the task identifier that activities are currently being started in to the userId of the
      * task. Each time a new task is created, the entry for the userId of the task is incremented
@@ -1396,16 +1397,33 @@
                 r.setLastReportedConfiguration(mergedConfiguration);
 
                 logIfTransactionTooLarge(r.intent, r.icicle);
-                mService.mLifecycleManager.scheduleTransaction(app.thread, r.appToken,
-                        new LaunchActivityItem(new Intent(r.intent),
+
+
+                // Create activity launch transaction.
+                final ClientTransaction clientTransaction = new ClientTransaction(app.thread,
+                        r.appToken);
+                clientTransaction.addCallback(new LaunchActivityItem(new Intent(r.intent),
                         System.identityHashCode(r), r.info,
                         // TODO: Have this take the merged configuration instead of separate global
                         // and override configs.
                         mergedConfiguration.getGlobalConfiguration(),
                         mergedConfiguration.getOverrideConfiguration(), r.compat,
                         r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
-                        r.persistentState, results, newIntents, !andResume,
-                        mService.isNextTransitionForward(), profilerInfo));
+                        r.persistentState, results, newIntents, mService.isNextTransitionForward(),
+                        profilerInfo));
+
+                // Set desired final state.
+                final ActivityLifecycleItem lifecycleItem;
+                if (andResume) {
+                    lifecycleItem = new ResumeActivityItem(mService.isNextTransitionForward());
+                } else {
+                    lifecycleItem = new PauseActivityItem();
+                }
+                clientTransaction.setLifecycleStateRequest(lifecycleItem);
+
+                // Schedule transaction.
+                mService.mLifecycleManager.scheduleTransaction(clientTransaction);
+
 
                 if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
                     // This may be a heavy-weight process!  Note that the package
@@ -2904,16 +2922,6 @@
         }
     }
 
-    int getNextStackId() {
-        while (true) {
-            if (getStack(mNextFreeStackId) == null) {
-                break;
-            }
-            mNextFreeStackId++;
-        }
-        return mNextFreeStackId;
-    }
-
     /**
      * Called to restore the state of the task into the stack that it's supposed to go into.
      *
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index 317a68f..a97b93c 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -25,8 +25,6 @@
 
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
-import android.app.ProfilerInfo;
-import android.app.WaitResult;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Intent;
@@ -34,7 +32,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.res.Configuration;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.FactoryTest;
@@ -43,12 +40,10 @@
 import android.os.Looper;
 import android.os.Message;
 import android.provider.Settings;
-import android.service.voice.IVoiceInteractionSession;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
-import com.android.internal.app.IVoiceInteractor;
 import com.android.server.am.ActivityStarter.DefaultFactory;
 import com.android.server.am.ActivityStarter.Factory;
 
@@ -72,7 +67,6 @@
 
     private final ActivityManagerService mService;
     private final ActivityStackSupervisor mSupervisor;
-    private final ActivityStartInterceptor mInterceptor;
 
     /** Last home activity record we attempted to start. */
     private ActivityRecord mLastHomeActivityStartRecord;
@@ -115,7 +109,9 @@
     private ActivityStarter mLastStarter;
 
     ActivityStartController(ActivityManagerService service) {
-        this(service, service.mStackSupervisor, new DefaultFactory());
+        this(service, service.mStackSupervisor,
+                new DefaultFactory(service, service.mStackSupervisor,
+                    new ActivityStartInterceptor(service, service.mStackSupervisor)));
     }
 
     @VisibleForTesting
@@ -124,38 +120,20 @@
         mService = service;
         mSupervisor = supervisor;
         mHandler = new StartHandler(mService.mHandlerThread.getLooper());
-        mInterceptor = new ActivityStartInterceptor(mService, mSupervisor);
         mFactory = factory;
+        mFactory.setController(this);
     }
 
     /**
-     * Retrieves a starter to be used for a new start request. The starter will be added to the
-     * active starters list.
-     *
-     * TODO(b/64750076): This should be removed when {@link #obtainStarter} is implemented. At that
-     * time, {@link ActivityStarter#execute} will be able to handle cleaning up the starter's
-     * internal references.
+     * @return A starter to configure and execute starting an activity. It is valid until after
+     *         {@link ActivityStarter#execute} is invoked. At that point, the starter should be
+     *         considered invalid and no longer modified or used.
      */
-    private ActivityStarter createStarter() {
-        mLastStarter = mFactory.getStarter(this, mService, mService.mStackSupervisor, mInterceptor);
-        return mLastStarter;
-    }
+    ActivityStarter obtainStarter(Intent intent, String reason) {
+        final ActivityStarter starter = mFactory.obtainStarter();
+        mLastStarter = starter;
 
-    /**
-     * TODO(b/64750076): Remove once we directly expose starter interface to callers through
-     * {@link #obtainStarter}.
-     */
-    int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
-            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
-            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
-            ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
-            ActivityRecord[] outActivity, TaskRecord inTask, String reason) {
-        return createStarter().startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
-                aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
-                callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
-                options, ignoreTargetSecurity, componentSpecified, outActivity, inTask, reason);
+        return starter.setIntent(intent).setReason(reason);
     }
 
     /**
@@ -170,18 +148,12 @@
     void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
         mSupervisor.moveHomeStackTaskToTop(reason);
 
-        final ActivityStarter starter = createStarter();
-
-        mLastHomeActivityStartResult = starter.startActivityLocked(null /*caller*/, intent,
-                null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
-                null /*voiceSession*/, null /*voiceInteractor*/, null /*resultTo*/,
-                null /*resultWho*/, 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/,
-                null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
-                0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/,
-                false /*componentSpecified*/, tmpOutRecord, null /*inTask*/,
-                "startHomeActivity: " + reason);
+        mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
+                .setOutActivity(tmpOutRecord)
+                .setCallingUid(0)
+                .setActivityInfo(aInfo)
+                .execute();
         mLastHomeActivityStartRecord = tmpOutRecord[0];
-
         if (mSupervisor.inResumeTopActivity) {
             // If we are in resume section already, home activity will be initialized, but not
             // resumed (to avoid recursive resume) and will stay that way until something pokes it
@@ -228,9 +200,10 @@
                     intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
                     intent.setComponent(new ComponentName(
                             ri.activityInfo.packageName, ri.activityInfo.name));
-                    startActivity(null, intent, null /*ephemeralIntent*/, null, ri.activityInfo,
-                            null /*rInfo*/, null, null, null, null, 0, 0, 0, null, 0, 0, 0, null,
-                            false, false, null, null, "startSetupActivity");
+                    obtainStarter(intent, "startSetupActivity")
+                            .setCallingUid(0)
+                            .setActivityInfo(ri.activityInfo)
+                            .execute();
                 }
             }
         }
@@ -246,9 +219,17 @@
                 null);
 
         // TODO: Switch to user app stacks here.
-        return startActivityMayWait(null, uid, callingPackage,
-                intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
-                null, null, null, bOptions, false, userId, inTask, reason);
+        return obtainStarter(intent, reason)
+                .setCallingUid(uid)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setResultTo(resultTo)
+                .setResultWho(resultWho)
+                .setRequestCode(requestCode)
+                .setStartFlags(startFlags)
+                .setMayWait(bOptions, userId)
+                .setInTask(inTask)
+                .execute();
     }
 
     final int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
@@ -262,24 +243,6 @@
         return ret;
     }
 
-    /**
-     * TODO(b/64750076): Remove once we directly expose starter interface to callers through
-     * {@link #obtainStarter}.
-     */
-    int startActivityMayWait(IApplicationThread caller, int callingUid,
-            String callingPackage, Intent intent, String resolvedType,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            IBinder resultTo, String resultWho, int requestCode, int startFlags,
-            ProfilerInfo profilerInfo, WaitResult outResult,
-            Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId,
-            TaskRecord inTask, String reason) {
-        return createStarter().startActivityMayWait(caller, callingUid, callingPackage, intent,
-                resolvedType, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
-                startFlags, profilerInfo, outResult, globalConfig, bOptions,
-                ignoreTargetSecurity,
-                userId, inTask, reason);
-    }
-
     int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions, int userId,
             String reason) {
@@ -340,11 +303,23 @@
 
                     ActivityOptions options = ActivityOptions.fromBundle(
                             i == intents.length - 1 ? bOptions : null);
-                    int res = startActivity(caller, intent, null /*ephemeralIntent*/,
-                            resolvedTypes[i], aInfo, null /*rInfo*/, null, null, resultTo, null, -1,
-                            callingPid, callingUid, callingPackage,
-                            realCallingPid, realCallingUid, 0,
-                            options, false, componentSpecified, outActivity, null, reason);
+
+                    final int res = obtainStarter(intent, reason)
+                            .setCaller(caller)
+                            .setResolvedType(resolvedTypes[i])
+                            .setActivityInfo(aInfo)
+                            .setResultTo(resultTo)
+                            .setRequestCode(-1)
+                            .setCallingPid(callingPid)
+                            .setCallingUid(callingUid)
+                            .setCallingPackage(callingPackage)
+                            .setRealCallingPid(realCallingPid)
+                            .setRealCallingUid(realCallingUid)
+                            .setActivityOptions(options)
+                            .setComponentSpecified(componentSpecified)
+                            .setOutActivity(outActivity)
+                            .execute();
+
                     if (res < 0) {
                         return res;
                     }
@@ -369,10 +344,11 @@
         while (!mPendingActivityLaunches.isEmpty()) {
             final PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
             final boolean resume = doResume && mPendingActivityLaunches.isEmpty();
-            final ActivityStarter starter = createStarter();
+            final ActivityStarter starter = obtainStarter(null /* intent */,
+                    "pendingActivityLaunch");
             try {
-                starter.startActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags, resume,
-                        null, null, null /*outRecords*/);
+                starter.startResolvedActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags,
+                        resume, null, null, null /* outRecords */);
             } catch (Exception e) {
                 Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
                 pal.sendErrorResult(e.getMessage());
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 3bee4228..dda8e9c 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -74,7 +74,6 @@
 import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
@@ -87,7 +86,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.AuxiliaryResolveInfo;
-import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
@@ -186,6 +184,13 @@
     // The reason we were trying to start the last activity
     private String mLastStartReason;
 
+    /*
+     * Request details provided through setter methods. Should be reset after {@link #execute()}
+     * to avoid unnecessarily retaining parameters. Note that the request is ignored when
+     * {@link #startResolvedActivity} is invoked directly.
+     */
+    private Request mRequest = new Request();
+
     /**
      * An interface that to provide {@link ActivityStarter} instances to the controller. This is
      * used by tests to inject their own starter implementations for verification purposes.
@@ -193,27 +198,96 @@
     @VisibleForTesting
     interface Factory {
         /**
+         * Sets the {@link ActivityStartController} to be passed to {@link ActivityStarter}.
+         */
+        void setController(ActivityStartController controller);
+
+        /**
          * Generates an {@link ActivityStarter} that is ready to handle a new start request.
          * @param controller The {@link ActivityStartController} which the starter who will own
          *                   this instance.
          * @return an {@link ActivityStarter}
          */
-        ActivityStarter getStarter(ActivityStartController controller,
-                ActivityManagerService service, ActivityStackSupervisor supervisor,
-                ActivityStartInterceptor interceptor);
+        ActivityStarter obtainStarter();
     }
 
     /**
      * Default implementation of {@link StarterFactory}.
      */
     static class DefaultFactory implements Factory {
-        @Override
-        public ActivityStarter getStarter(ActivityStartController controller,
-                ActivityManagerService service, ActivityStackSupervisor supervisor,
-                ActivityStartInterceptor interceptor) {
-            // TODO(b/64750076): Investigate recycling instances to reduce object creation overhead.
-            return new ActivityStarter(controller, service, supervisor, interceptor);
+        private ActivityStartController mController;
+        private ActivityManagerService mService;
+        private ActivityStackSupervisor mSupervisor;
+        private ActivityStartInterceptor mInterceptor;
+
+        DefaultFactory(ActivityManagerService service,
+                ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
+            mService = service;
+            mSupervisor = supervisor;
+            mInterceptor = interceptor;
         }
+
+        @Override
+        public void setController(ActivityStartController controller) {
+            mController = controller;
+        }
+
+        @Override
+        public ActivityStarter obtainStarter() {
+            // TODO(b/64750076): Investigate recycling instances to reduce object creation overhead.
+            return new ActivityStarter(mController, mService, mSupervisor, mInterceptor);
+        }
+    }
+
+    /**
+     * Container for capturing initial start request details. This information is NOT reset until
+     * the {@link ActivityStarter} is recycled, allowing for multiple invocations with the same
+     * parameters.
+     *
+     * TODO(b/64750076): Investigate consolidating member variables of {@link ActivityStarter} with
+     * the request object. Note that some member variables are referenced in
+     * {@link #dump(PrintWriter, String)} and therefore cannot be cleared immediately after
+     * execution.
+     */
+    private static class Request {
+        private static final int DEFAULT_CALLING_UID = -1;
+        private static final int DEFAULT_CALLING_PID = 0;
+
+        IApplicationThread caller;
+        Intent intent;
+        Intent ephemeralIntent;
+        String resolvedType;
+        ActivityInfo activityInfo;
+        ResolveInfo resolveInfo;
+        IVoiceInteractionSession voiceSession;
+        IVoiceInteractor voiceInteractor;
+        IBinder resultTo;
+        String resultWho;
+        int requestCode;
+        int callingPid = DEFAULT_CALLING_UID;
+        int callingUid = DEFAULT_CALLING_PID;
+        String callingPackage;
+        int realCallingPid;
+        int realCallingUid;
+        int startFlags;
+        ActivityOptions activityOptions;
+        boolean ignoreTargetSecurity;
+        boolean componentSpecified;
+        ActivityRecord[] outActivity;
+        TaskRecord inTask;
+        String reason;
+        ProfilerInfo profilerInfo;
+        Configuration globalConfig;
+        Bundle waitOptions;
+        int userId;
+        WaitResult waitResult;
+
+        /**
+         * Indicates that we should wait for the result of the start request. This flag is set when
+         * {@link ActivityStarter#setMayWait(Bundle, int)} is called.
+         * {@see ActivityStarter#startActivityMayWait}.
+         */
+        boolean mayWait;
     }
 
     ActivityStarter(ActivityStartController controller, ActivityManagerService service,
@@ -224,14 +298,44 @@
         mInterceptor = interceptor;
     }
 
-    boolean relatedToPackage(String packageName) {
-        return (mLastStartActivityRecord[0] != null
-                        && packageName.equals(mLastStartActivityRecord[0].packageName))
-                || (mStartActivity != null
-                        && packageName.equals(mStartActivity.packageName));
+    ActivityRecord getStartActivity() {
+        return mStartActivity;
     }
 
-    int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
+    boolean relatedToPackage(String packageName) {
+        return (mLastStartActivityRecord[0] != null
+                && packageName.equals(mLastStartActivityRecord[0].packageName))
+                || (mStartActivity != null && packageName.equals(mStartActivity.packageName));
+    }
+
+    /**
+     * Starts an activity based on the request parameters provided earlier.
+     * @return The starter result.
+     */
+    int execute() {
+        // TODO(b/64750076): Look into passing request directly to these methods to allow
+        // for transactional diffs and preprocessing.
+        if (mRequest.mayWait) {
+            return startActivityMayWait(mRequest.caller, mRequest.callingUid,
+                    mRequest.callingPackage, mRequest.intent, mRequest.resolvedType,
+                    mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
+                    mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,
+                    mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
+                    mRequest.waitOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
+                    mRequest.inTask, mRequest.reason);
+        } else {
+            return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,
+                    mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,
+                    mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
+                    mRequest.resultWho, mRequest.requestCode, mRequest.callingPid,
+                    mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid,
+                    mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions,
+                    mRequest.ignoreTargetSecurity, mRequest.componentSpecified,
+                    mRequest.outActivity, mRequest.inTask, mRequest.reason);
+        }
+    }
+
+    private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
             String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
@@ -265,7 +369,6 @@
         return result != START_ABORTED ? result : START_SUCCESS;
     }
 
-    /** DO NOT call this method directly. Use {@link #startActivityLocked} instead. */
     private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
             String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
@@ -548,8 +651,8 @@
 
         mController.doPendingActivityLaunches(false);
 
-        return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true,
-                options, inTask, outActivity);
+        return startResolvedActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
+                true /* doResume */, options, inTask, outActivity);
     }
 
     /**
@@ -569,7 +672,8 @@
                 auxiliaryResponse.failureIntent, callingPackage, verificationBundle,
                 resolvedType, userId, auxiliaryResponse.packageName, auxiliaryResponse.splitName,
                 auxiliaryResponse.installFailureActivity, auxiliaryResponse.versionCode,
-                auxiliaryResponse.token, auxiliaryResponse.needsPhaseTwo);
+                auxiliaryResponse.token, auxiliaryResponse.resolveInfo.getExtras(),
+                auxiliaryResponse.needsPhaseTwo);
     }
 
     void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack targetStack) {
@@ -610,7 +714,7 @@
         }
     }
 
-    final int startActivityMayWait(IApplicationThread caller, int callingUid,
+    private int startActivityMayWait(IApplicationThread caller, int callingUid,
             String callingPackage, Intent intent, String resolvedType,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             IBinder resultTo, String resultWho, int requestCode, int startFlags,
@@ -754,7 +858,7 @@
             }
 
             final ActivityRecord[] outRecord = new ActivityRecord[1];
-            int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
+            int res = startActivity(caller, intent, ephemeralIntent, resolvedType,
                     aInfo, rInfo, voiceSession, voiceInteractor,
                     resultTo, resultWho, requestCode, callingPid,
                     callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
@@ -820,10 +924,16 @@
         }
     }
 
-    int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
-            ActivityRecord[] outActivity) {
+    /**
+     * Starts an activity based on the provided {@link ActivityRecord} and environment parameters.
+     * Note that this method is called internally as well as part of {@link #startActivity}.
+     *
+     * @return The start result.
+     */
+    int startResolvedActivity(final ActivityRecord r, ActivityRecord sourceRecord,
+        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+        int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
+        ActivityRecord[] outActivity) {
         int result = START_CANCELED;
         try {
             mService.mWindowManager.deferSurfaceLayout();
@@ -2008,6 +2118,155 @@
                 (flags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0;
     }
 
+    ActivityStarter setIntent(Intent intent) {
+        mRequest.intent = intent;
+        return this;
+    }
+
+    ActivityStarter setReason(String reason) {
+        mRequest.reason = reason;
+        return this;
+    }
+
+    ActivityStarter setCaller(IApplicationThread caller) {
+        mRequest.caller = caller;
+        return this;
+    }
+
+    ActivityStarter setEphemeralIntent(Intent intent) {
+        mRequest.ephemeralIntent = intent;
+        return this;
+    }
+
+
+    ActivityStarter setResolvedType(String type) {
+        mRequest.resolvedType = type;
+        return this;
+    }
+
+    ActivityStarter setActivityInfo(ActivityInfo info) {
+        mRequest.activityInfo = info;
+        return this;
+    }
+
+    ActivityStarter setResolveInfo(ResolveInfo info) {
+        mRequest.resolveInfo = info;
+        return this;
+    }
+
+    ActivityStarter setVoiceSession(IVoiceInteractionSession voiceSession) {
+        mRequest.voiceSession = voiceSession;
+        return this;
+    }
+
+    ActivityStarter setVoiceInteractor(IVoiceInteractor voiceInteractor) {
+        mRequest.voiceInteractor = voiceInteractor;
+        return this;
+    }
+
+    ActivityStarter setResultTo(IBinder resultTo) {
+        mRequest.resultTo = resultTo;
+        return this;
+    }
+
+    ActivityStarter setResultWho(String resultWho) {
+        mRequest.resultWho = resultWho;
+        return this;
+    }
+
+    ActivityStarter setRequestCode(int requestCode) {
+        mRequest.requestCode = requestCode;
+        return this;
+    }
+
+    ActivityStarter setCallingPid(int pid) {
+        mRequest.callingPid = pid;
+        return this;
+    }
+
+    ActivityStarter setCallingUid(int uid) {
+        mRequest.callingUid = uid;
+        return this;
+    }
+
+    ActivityStarter setCallingPackage(String callingPackage) {
+        mRequest.callingPackage = callingPackage;
+        return this;
+    }
+
+    ActivityStarter setRealCallingPid(int pid) {
+        mRequest.realCallingPid = pid;
+        return this;
+    }
+
+    ActivityStarter setRealCallingUid(int uid) {
+        mRequest.realCallingUid = uid;
+        return this;
+    }
+
+    ActivityStarter setStartFlags(int startFlags) {
+        mRequest.startFlags = startFlags;
+        return this;
+    }
+
+    ActivityStarter setActivityOptions(ActivityOptions options) {
+        mRequest.activityOptions = options;
+        return this;
+    }
+
+    ActivityStarter setIgnoreTargetSecurity(boolean ignoreTargetSecurity) {
+        mRequest.ignoreTargetSecurity = ignoreTargetSecurity;
+        return this;
+    }
+
+    ActivityStarter setComponentSpecified(boolean componentSpecified) {
+        mRequest.componentSpecified = componentSpecified;
+        return this;
+    }
+
+    ActivityStarter setOutActivity(ActivityRecord[] outActivity) {
+        mRequest.outActivity = outActivity;
+        return this;
+    }
+
+    ActivityStarter setInTask(TaskRecord inTask) {
+        mRequest.inTask = inTask;
+        return this;
+    }
+
+    ActivityStarter setWaitResult(WaitResult result) {
+        mRequest.waitResult = result;
+        return this;
+    }
+
+    ActivityStarter setProfilerInfo(ProfilerInfo info) {
+        mRequest.profilerInfo = info;
+        return this;
+    }
+
+    ActivityStarter setGlobalConfiguration(Configuration config) {
+        mRequest.globalConfig = config;
+        return this;
+    }
+
+    ActivityStarter setWaitOptions(Bundle options) {
+        mRequest.waitOptions = options;
+        return this;
+    }
+
+    ActivityStarter setUserId(int userId) {
+        mRequest.userId = userId;
+        return this;
+    }
+
+    ActivityStarter setMayWait(Bundle options, int userId) {
+        mRequest.mayWait = true;
+        mRequest.waitOptions = options;
+        mRequest.userId = userId;
+
+        return this;
+    }
+
     void dump(PrintWriter pw, String prefix) {
         prefix = prefix + "  ";
         pw.print(prefix);
diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java
index e5872c03..f821f6b 100644
--- a/services/core/java/com/android/server/am/AppTaskImpl.java
+++ b/services/core/java/com/android/server/am/AppTaskImpl.java
@@ -122,9 +122,14 @@
                 throw new IllegalArgumentException("Bad app thread " + appThread);
             }
         }
-        return mService.getActivityStartController().startActivityMayWait(appThread, -1,
-                callingPackage, intent, resolvedType, null, null, null, null, 0, 0, null, null,
-                null, bOptions, false, callingUser, tr, "AppTaskImpl");
+
+        return mService.getActivityStartController().obtainStarter(intent, "AppTaskImpl")
+                .setCaller(appThread)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setMayWait(bOptions, callingUser)
+                .setInTask(tr)
+                .execute();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 9e9318a..87690d1 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -935,7 +935,7 @@
         }
     }
 
-    public void notePackageInstalled(String pkgName, int versionCode) {
+    public void notePackageInstalled(String pkgName, long versionCode) {
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.notePackageInstalledLocked(pkgName, versionCode);
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index d77e1a2..ba3e25a 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -22,8 +22,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Context.DEVICE_POLICY_SERVICE;
 import static android.content.Context.STATUS_BAR_SERVICE;
+import static android.content.Intent.ACTION_CALL_EMERGENCY;
 import static android.os.UserHandle.USER_ALL;
 import static android.os.UserHandle.USER_CURRENT;
+import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
@@ -45,6 +47,7 @@
 import android.app.admin.IDevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Binder;
 import android.os.Debug;
 import android.os.Handler;
@@ -52,9 +55,11 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
+import android.telecom.TelecomManager;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
@@ -133,6 +138,8 @@
     WindowManagerService mWindowManager;
     @VisibleForTesting
     LockPatternUtils mLockPatternUtils;
+    @VisibleForTesting
+    TelecomManager mTelecomManager;
 
     /**
      * Helper that is responsible for showing the right toast when a disallowed activity operation
@@ -165,7 +172,7 @@
     /**
      * Features that are allowed by DPC to show during LockTask mode.
      */
-    private final SparseArray<Integer> mLockTaskFeatures = new SparseArray<>();
+    private final SparseIntArray mLockTaskFeatures = new SparseIntArray();
 
     /**
      * Store the current lock task mode. Possible values:
@@ -298,6 +305,11 @@
             return false;
         }
 
+        // Allow emergency calling when the device is protected by a locked keyguard
+        if (isKeyguardAllowed(task.userId) && isEmergencyCallTask(task)) {
+            return false;
+        }
+
         return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty());
     }
 
@@ -306,6 +318,37 @@
                 & DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS) != 0;
     }
 
+    private boolean isKeyguardAllowed(int userId) {
+        return (getLockTaskFeaturesForUser(userId)
+                & DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD) != 0;
+    }
+
+    private boolean isEmergencyCallTask(TaskRecord task) {
+        final Intent intent = task.intent;
+        if (intent == null) {
+            return false;
+        }
+
+        // 1. The emergency keypad activity launched on top of the keyguard
+        if (EMERGENCY_DIALER_COMPONENT.equals(intent.getComponent())) {
+            return true;
+        }
+
+        // 2. The intent sent by the keypad, which is handled by Telephony
+        if (ACTION_CALL_EMERGENCY.equals(intent.getAction())) {
+            return true;
+        }
+
+        // 3. Telephony then starts the default package for making the call
+        final TelecomManager tm = getTelecomManager();
+        final String dialerPackage = tm != null ? tm.getSystemDialerPackage() : null;
+        if (dialerPackage != null && dialerPackage.equals(intent.getComponent().getPackageName())) {
+            return true;
+        }
+
+        return false;
+    }
+
     /**
      * Stop the current lock task mode.
      *
@@ -686,11 +729,10 @@
             mWindowManager.reenableKeyguard(mToken);
 
         } else if (lockTaskModeState == LOCK_TASK_MODE_LOCKED) {
-            int lockTaskFeatures = getLockTaskFeaturesForUser(userId);
-            if ((DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD & lockTaskFeatures) == 0) {
-                mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
-            } else {
+            if (isKeyguardAllowed(userId)) {
                 mWindowManager.reenableKeyguard(mToken);
+            } else {
+                mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
             }
 
         } else { // lockTaskModeState == LOCK_TASK_MODE_PINNED
@@ -784,6 +826,15 @@
         return mLockPatternUtils;
     }
 
+    @Nullable
+    private TelecomManager getTelecomManager() {
+        if (mTelecomManager == null) {
+            // We don't preserve the TelecomManager object to save memory
+            return mContext.getSystemService(TelecomManager.class);
+        }
+        return mTelecomManager;
+    }
+
     // Should only be called on the handler thread
     @NonNull
     private LockTaskNotify getLockTaskNotify() {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 9d3c2ae..71d6604 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -730,7 +730,7 @@
     /*
      *  Return true if package has been added false if not
      */
-    public boolean addPackage(String pkg, int versionCode, ProcessStatsService tracker) {
+    public boolean addPackage(String pkg, long versionCode, ProcessStatsService tracker) {
         if (!pkgList.containsKey(pkg)) {
             ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
                     versionCode);
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index effb86c..5f9d616 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -27,6 +27,7 @@
 import android.service.procstats.ProcessStatsServiceDumpProto;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
+import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -120,12 +121,12 @@
     }
 
     public ProcessState getProcessStateLocked(String packageName,
-            int uid, int versionCode, String processName) {
+            int uid, long versionCode, String processName) {
         return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName);
     }
 
     public ServiceState getServiceStateLocked(String packageName, int uid,
-            int versionCode, String processName, String className) {
+            long versionCode, String processName, String className) {
         return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName,
                 className);
     }
@@ -150,12 +151,13 @@
             }
             mProcessStats.mMemFactor = memFactor;
             mProcessStats.mStartTime = now;
-            final ArrayMap<String, SparseArray<SparseArray<ProcessStats.PackageState>>> pmap
+            final ArrayMap<String, SparseArray<LongSparseArray<ProcessStats.PackageState>>> pmap
                     = mProcessStats.mPackages.getMap();
             for (int ipkg=pmap.size()-1; ipkg>=0; ipkg--) {
-                final SparseArray<SparseArray<ProcessStats.PackageState>> uids = pmap.valueAt(ipkg);
+                final SparseArray<LongSparseArray<ProcessStats.PackageState>> uids =
+                        pmap.valueAt(ipkg);
                 for (int iuid=uids.size()-1; iuid>=0; iuid--) {
-                    final SparseArray<ProcessStats.PackageState> vers = uids.valueAt(iuid);
+                    final LongSparseArray<ProcessStats.PackageState> vers = uids.valueAt(iuid);
                     for (int iver=vers.size()-1; iver>=0; iver--) {
                         final ProcessStats.PackageState pkg = vers.valueAt(iver);
                         final ArrayMap<String, ServiceState> services = pkg.mServices;
@@ -308,17 +310,17 @@
                             Slog.w(TAG, "  Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu));
                         }
                     }
-                    ArrayMap<String, SparseArray<SparseArray<ProcessStats.PackageState>>> pkgMap
+                    ArrayMap<String, SparseArray<LongSparseArray<ProcessStats.PackageState>>> pkgMap
                             = stats.mPackages.getMap();
                     final int NPKG = pkgMap.size();
                     for (int ip=0; ip<NPKG; ip++) {
                         Slog.w(TAG, "Package: " + pkgMap.keyAt(ip));
-                        SparseArray<SparseArray<ProcessStats.PackageState>> uids
+                        SparseArray<LongSparseArray<ProcessStats.PackageState>> uids
                                 = pkgMap.valueAt(ip);
                         final int NUID = uids.size();
                         for (int iu=0; iu<NUID; iu++) {
                             Slog.w(TAG, "  Uid: " + uids.keyAt(iu));
-                            SparseArray<ProcessStats.PackageState> vers = uids.valueAt(iu);
+                            LongSparseArray<ProcessStats.PackageState> vers = uids.valueAt(iu);
                             final int NVERS = vers.size();
                             for (int iv=0; iv<NVERS; iv++) {
                                 Slog.w(TAG, "    Vers: " + vers.keyAt(iv));
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 83965ee..48737a5 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -193,6 +193,11 @@
     // Do not move the stack as a part of reparenting
     static final int REPARENT_LEAVE_STACK_IN_PLACE = 2;
 
+    /**
+     * The factory used to create {@link TaskRecord}. This allows OEM subclass {@link TaskRecord}.
+     */
+    private static TaskRecordFactory sTaskRecordFactory;
+
     final int taskId;       // Unique identifier for this task.
     String affinity;        // The affinity name for this task, or null; may change identity.
     String rootAffinity;    // Initial base affinity, or null; does not change from initial root.
@@ -312,6 +317,10 @@
 
     private TaskWindowContainerController mWindowContainerController;
 
+    /**
+     * Don't use constructor directly. Use {@link #create(ActivityManagerService, int, ActivityInfo,
+     * Intent, TaskDescription)} instead.
+     */
     TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
             IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
         mService = service;
@@ -331,6 +340,10 @@
         mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
     }
 
+    /**
+     * Don't use constructor directly. Use {@link #create(ActivityManagerService, int, ActivityInfo,
+     * Intent, IVoiceInteractionSession, IVoiceInteractor)} instead.
+     */
     TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
             TaskDescription _taskDescription) {
         mService = service;
@@ -357,7 +370,10 @@
         mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
     }
 
-    private TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
+    /**
+     * Don't use constructor directly. This is only used by XML parser.
+     */
+    TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
             Intent _affinityIntent, String _affinity, String _rootAffinity,
             ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
             boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId,
@@ -1632,278 +1648,6 @@
         updateTaskDescription();
     }
 
-    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
-        if (DEBUG_RECENTS) Slog.i(TAG_RECENTS, "Saving task=" + this);
-
-        out.attribute(null, ATTR_TASKID, String.valueOf(taskId));
-        if (realActivity != null) {
-            out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
-        }
-        out.attribute(null, ATTR_REALACTIVITY_SUSPENDED, String.valueOf(realActivitySuspended));
-        if (origActivity != null) {
-            out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
-        }
-        // Write affinity, and root affinity if it is different from affinity.
-        // We use the special string "@" for a null root affinity, so we can identify
-        // later whether we were given a root affinity or should just make it the
-        // same as the affinity.
-        if (affinity != null) {
-            out.attribute(null, ATTR_AFFINITY, affinity);
-            if (!affinity.equals(rootAffinity)) {
-                out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
-            }
-        } else if (rootAffinity != null) {
-            out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
-        }
-        out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
-        out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
-        out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
-        out.attribute(null, ATTR_USERID, String.valueOf(userId));
-        out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete));
-        out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
-        out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
-        out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
-        if (lastDescription != null) {
-            out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
-        }
-        if (lastTaskDescription != null) {
-            lastTaskDescription.saveToXml(out);
-        }
-        out.attribute(null, ATTR_TASK_AFFILIATION_COLOR, String.valueOf(mAffiliatedTaskColor));
-        out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
-        out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
-        out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
-        out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
-        out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
-        out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode));
-        out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE,
-                String.valueOf(mSupportsPictureInPicture));
-        if (mLastNonFullscreenBounds != null) {
-            out.attribute(
-                    null, ATTR_NON_FULLSCREEN_BOUNDS, mLastNonFullscreenBounds.flattenToString());
-        }
-        out.attribute(null, ATTR_MIN_WIDTH, String.valueOf(mMinWidth));
-        out.attribute(null, ATTR_MIN_HEIGHT, String.valueOf(mMinHeight));
-        out.attribute(null, ATTR_PERSIST_TASK_VERSION, String.valueOf(PERSIST_TASK_VERSION));
-
-        if (affinityIntent != null) {
-            out.startTag(null, TAG_AFFINITYINTENT);
-            affinityIntent.saveToXml(out);
-            out.endTag(null, TAG_AFFINITYINTENT);
-        }
-
-        out.startTag(null, TAG_INTENT);
-        intent.saveToXml(out);
-        out.endTag(null, TAG_INTENT);
-
-        final ArrayList<ActivityRecord> activities = mActivities;
-        final int numActivities = activities.size();
-        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
-            final ActivityRecord r = activities.get(activityNdx);
-            if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
-                    ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
-                            | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT) &&
-                            activityNdx > 0) {
-                // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
-                break;
-            }
-            out.startTag(null, TAG_ACTIVITY);
-            r.saveToXml(out);
-            out.endTag(null, TAG_ACTIVITY);
-        }
-    }
-
-    static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
-            throws IOException, XmlPullParserException {
-        Intent intent = null;
-        Intent affinityIntent = null;
-        ArrayList<ActivityRecord> activities = new ArrayList<>();
-        ComponentName realActivity = null;
-        boolean realActivitySuspended = false;
-        ComponentName origActivity = null;
-        String affinity = null;
-        String rootAffinity = null;
-        boolean hasRootAffinity = false;
-        boolean rootHasReset = false;
-        boolean autoRemoveRecents = false;
-        boolean askedCompatMode = false;
-        int taskType = 0;
-        int userId = 0;
-        boolean userSetupComplete = true;
-        int effectiveUid = -1;
-        String lastDescription = null;
-        long lastTimeOnTop = 0;
-        boolean neverRelinquishIdentity = true;
-        int taskId = INVALID_TASK_ID;
-        final int outerDepth = in.getDepth();
-        TaskDescription taskDescription = new TaskDescription();
-        int taskAffiliation = INVALID_TASK_ID;
-        int taskAffiliationColor = 0;
-        int prevTaskId = INVALID_TASK_ID;
-        int nextTaskId = INVALID_TASK_ID;
-        int callingUid = -1;
-        String callingPackage = "";
-        int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
-        boolean supportsPictureInPicture = false;
-        Rect bounds = null;
-        int minWidth = INVALID_MIN_SIZE;
-        int minHeight = INVALID_MIN_SIZE;
-        int persistTaskVersion = 0;
-
-        for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
-            final String attrName = in.getAttributeName(attrNdx);
-            final String attrValue = in.getAttributeValue(attrNdx);
-            if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
-                    attrName + " value=" + attrValue);
-            if (ATTR_TASKID.equals(attrName)) {
-                if (taskId == INVALID_TASK_ID) taskId = Integer.parseInt(attrValue);
-            } else if (ATTR_REALACTIVITY.equals(attrName)) {
-                realActivity = ComponentName.unflattenFromString(attrValue);
-            } else if (ATTR_REALACTIVITY_SUSPENDED.equals(attrName)) {
-                realActivitySuspended = Boolean.valueOf(attrValue);
-            } else if (ATTR_ORIGACTIVITY.equals(attrName)) {
-                origActivity = ComponentName.unflattenFromString(attrValue);
-            } else if (ATTR_AFFINITY.equals(attrName)) {
-                affinity = attrValue;
-            } else if (ATTR_ROOT_AFFINITY.equals(attrName)) {
-                rootAffinity = attrValue;
-                hasRootAffinity = true;
-            } else if (ATTR_ROOTHASRESET.equals(attrName)) {
-                rootHasReset = Boolean.parseBoolean(attrValue);
-            } else if (ATTR_AUTOREMOVERECENTS.equals(attrName)) {
-                autoRemoveRecents = Boolean.parseBoolean(attrValue);
-            } else if (ATTR_ASKEDCOMPATMODE.equals(attrName)) {
-                askedCompatMode = Boolean.parseBoolean(attrValue);
-            } else if (ATTR_USERID.equals(attrName)) {
-                userId = Integer.parseInt(attrValue);
-            } else if (ATTR_USER_SETUP_COMPLETE.equals(attrName)) {
-                userSetupComplete = Boolean.parseBoolean(attrValue);
-            } else if (ATTR_EFFECTIVE_UID.equals(attrName)) {
-                effectiveUid = Integer.parseInt(attrValue);
-            } else if (ATTR_TASKTYPE.equals(attrName)) {
-                taskType = Integer.parseInt(attrValue);
-            } else if (ATTR_LASTDESCRIPTION.equals(attrName)) {
-                lastDescription = attrValue;
-            } else if (ATTR_LASTTIMEMOVED.equals(attrName)) {
-                lastTimeOnTop = Long.parseLong(attrValue);
-            } else if (ATTR_NEVERRELINQUISH.equals(attrName)) {
-                neverRelinquishIdentity = Boolean.parseBoolean(attrValue);
-            } else if (attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
-                taskDescription.restoreFromXml(attrName, attrValue);
-            } else if (ATTR_TASK_AFFILIATION.equals(attrName)) {
-                taskAffiliation = Integer.parseInt(attrValue);
-            } else if (ATTR_PREV_AFFILIATION.equals(attrName)) {
-                prevTaskId = Integer.parseInt(attrValue);
-            } else if (ATTR_NEXT_AFFILIATION.equals(attrName)) {
-                nextTaskId = Integer.parseInt(attrValue);
-            } else if (ATTR_TASK_AFFILIATION_COLOR.equals(attrName)) {
-                taskAffiliationColor = Integer.parseInt(attrValue);
-            } else if (ATTR_CALLING_UID.equals(attrName)) {
-                callingUid = Integer.parseInt(attrValue);
-            } else if (ATTR_CALLING_PACKAGE.equals(attrName)) {
-                callingPackage = attrValue;
-            } else if (ATTR_RESIZE_MODE.equals(attrName)) {
-                resizeMode = Integer.parseInt(attrValue);
-            } else if (ATTR_SUPPORTS_PICTURE_IN_PICTURE.equals(attrName)) {
-                supportsPictureInPicture = Boolean.parseBoolean(attrValue);
-            } else if (ATTR_NON_FULLSCREEN_BOUNDS.equals(attrName)) {
-                bounds = Rect.unflattenFromString(attrValue);
-            } else if (ATTR_MIN_WIDTH.equals(attrName)) {
-                minWidth = Integer.parseInt(attrValue);
-            } else if (ATTR_MIN_HEIGHT.equals(attrName)) {
-                minHeight = Integer.parseInt(attrValue);
-            } else if (ATTR_PERSIST_TASK_VERSION.equals(attrName)) {
-                persistTaskVersion = Integer.parseInt(attrValue);
-            } else {
-                Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
-            }
-        }
-
-        int event;
-        while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
-                (event != XmlPullParser.END_TAG || in.getDepth() >= outerDepth)) {
-            if (event == XmlPullParser.START_TAG) {
-                final String name = in.getName();
-                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" +
-                        name);
-                if (TAG_AFFINITYINTENT.equals(name)) {
-                    affinityIntent = Intent.restoreFromXml(in);
-                } else if (TAG_INTENT.equals(name)) {
-                    intent = Intent.restoreFromXml(in);
-                } else if (TAG_ACTIVITY.equals(name)) {
-                    ActivityRecord activity = ActivityRecord.restoreFromXml(in, stackSupervisor);
-                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" +
-                            activity);
-                    if (activity != null) {
-                        activities.add(activity);
-                    }
-                } else {
-                    Slog.e(TAG, "restoreTask: Unexpected name=" + name);
-                    XmlUtils.skipCurrentTag(in);
-                }
-            }
-        }
-        if (!hasRootAffinity) {
-            rootAffinity = affinity;
-        } else if ("@".equals(rootAffinity)) {
-            rootAffinity = null;
-        }
-        if (effectiveUid <= 0) {
-            Intent checkIntent = intent != null ? intent : affinityIntent;
-            effectiveUid = 0;
-            if (checkIntent != null) {
-                IPackageManager pm = AppGlobals.getPackageManager();
-                try {
-                    ApplicationInfo ai = pm.getApplicationInfo(
-                            checkIntent.getComponent().getPackageName(),
-                            PackageManager.MATCH_UNINSTALLED_PACKAGES
-                                    | PackageManager.MATCH_DISABLED_COMPONENTS, userId);
-                    if (ai != null) {
-                        effectiveUid = ai.uid;
-                    }
-                } catch (RemoteException e) {
-                }
-            }
-            Slog.w(TAG, "Updating task #" + taskId + " for " + checkIntent
-                    + ": effectiveUid=" + effectiveUid);
-        }
-
-        if (persistTaskVersion < 1) {
-            // We need to convert the resize mode of home activities saved before version one if
-            // they are marked as RESIZE_MODE_RESIZEABLE to RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION
-            // since we didn't have that differentiation before version 1 and the system didn't
-            // resize home activities before then.
-            if (taskType == 1 /* old home type */ && resizeMode == RESIZE_MODE_RESIZEABLE) {
-                resizeMode = RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
-            }
-        } else {
-            // This activity has previously marked itself explicitly as both resizeable and
-            // supporting picture-in-picture.  Since there is no longer a requirement for
-            // picture-in-picture activities to be resizeable, we can mark this simply as
-            // resizeable and supporting picture-in-picture separately.
-            if (resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED) {
-                resizeMode = RESIZE_MODE_RESIZEABLE;
-                supportsPictureInPicture = true;
-            }
-        }
-
-        final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
-                affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
-                autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
-                activities, lastTimeOnTop, neverRelinquishIdentity, taskDescription,
-                taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid,
-                callingPackage, resizeMode, supportsPictureInPicture, realActivitySuspended,
-                userSetupComplete, minWidth, minHeight);
-        task.updateOverrideConfiguration(bounds);
-
-        for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
-            activities.get(activityNdx).setTask(task);
-        }
-
-        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task);
-        return task;
-    }
-
     private void adjustForMinimalTaskDimensions(Rect bounds) {
         if (bounds == null) {
             return;
@@ -2320,4 +2064,382 @@
             top = base = null;
         }
     }
+
+    /**
+     * Saves this {@link TaskRecord} to XML using given serializer.
+     */
+    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+        if (DEBUG_RECENTS) Slog.i(TAG_RECENTS, "Saving task=" + this);
+
+        out.attribute(null, ATTR_TASKID, String.valueOf(taskId));
+        if (realActivity != null) {
+            out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
+        }
+        out.attribute(null, ATTR_REALACTIVITY_SUSPENDED, String.valueOf(realActivitySuspended));
+        if (origActivity != null) {
+            out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
+        }
+        // Write affinity, and root affinity if it is different from affinity.
+        // We use the special string "@" for a null root affinity, so we can identify
+        // later whether we were given a root affinity or should just make it the
+        // same as the affinity.
+        if (affinity != null) {
+            out.attribute(null, ATTR_AFFINITY, affinity);
+            if (!affinity.equals(rootAffinity)) {
+                out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
+            }
+        } else if (rootAffinity != null) {
+            out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
+        }
+        out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
+        out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
+        out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
+        out.attribute(null, ATTR_USERID, String.valueOf(userId));
+        out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete));
+        out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
+        out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
+        out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
+        if (lastDescription != null) {
+            out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
+        }
+        if (lastTaskDescription != null) {
+            lastTaskDescription.saveToXml(out);
+        }
+        out.attribute(null, ATTR_TASK_AFFILIATION_COLOR, String.valueOf(mAffiliatedTaskColor));
+        out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
+        out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
+        out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
+        out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
+        out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
+        out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode));
+        out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE,
+                String.valueOf(mSupportsPictureInPicture));
+        if (mLastNonFullscreenBounds != null) {
+            out.attribute(
+                    null, ATTR_NON_FULLSCREEN_BOUNDS, mLastNonFullscreenBounds.flattenToString());
+        }
+        out.attribute(null, ATTR_MIN_WIDTH, String.valueOf(mMinWidth));
+        out.attribute(null, ATTR_MIN_HEIGHT, String.valueOf(mMinHeight));
+        out.attribute(null, ATTR_PERSIST_TASK_VERSION, String.valueOf(PERSIST_TASK_VERSION));
+
+        if (affinityIntent != null) {
+            out.startTag(null, TAG_AFFINITYINTENT);
+            affinityIntent.saveToXml(out);
+            out.endTag(null, TAG_AFFINITYINTENT);
+        }
+
+        out.startTag(null, TAG_INTENT);
+        intent.saveToXml(out);
+        out.endTag(null, TAG_INTENT);
+
+        final ArrayList<ActivityRecord> activities = mActivities;
+        final int numActivities = activities.size();
+        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
+            final ActivityRecord r = activities.get(activityNdx);
+            if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
+                    ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
+                            | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT) &&
+                            activityNdx > 0) {
+                // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
+                break;
+            }
+            out.startTag(null, TAG_ACTIVITY);
+            r.saveToXml(out);
+            out.endTag(null, TAG_ACTIVITY);
+        }
+    }
+
+    @VisibleForTesting
+    static TaskRecordFactory getTaskRecordFactory() {
+        if (sTaskRecordFactory == null) {
+            setTaskRecordFactory(new TaskRecordFactory());
+        }
+        return sTaskRecordFactory;
+    }
+
+    static void setTaskRecordFactory(TaskRecordFactory factory) {
+        sTaskRecordFactory = factory;
+    }
+
+    static TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+            Intent intent, IVoiceInteractionSession voiceSession,
+            IVoiceInteractor voiceInteractor) {
+        return getTaskRecordFactory().create(
+                service, taskId, info, intent, voiceSession, voiceInteractor);
+    }
+
+    static TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+            Intent intent, TaskDescription taskDescription) {
+        return getTaskRecordFactory().create(service, taskId, info, intent, taskDescription);
+    }
+
+    static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
+            throws IOException, XmlPullParserException {
+        return getTaskRecordFactory().restoreFromXml(in, stackSupervisor);
+    }
+
+    /**
+     * A factory class used to create {@link TaskRecord} or its subclass if any. This can be
+     * specified when system boots by setting it with
+     * {@link #setTaskRecordFactory(TaskRecordFactory)}.
+     */
+    static class TaskRecordFactory {
+
+        TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+                Intent intent, IVoiceInteractionSession voiceSession,
+                IVoiceInteractor voiceInteractor) {
+            return new TaskRecord(
+                    service, taskId, info, intent, voiceSession, voiceInteractor);
+        }
+
+        TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+                Intent intent, TaskDescription taskDescription) {
+            return new TaskRecord(service, taskId, info, intent, taskDescription);
+        }
+
+        /**
+         * Should only be used when we're restoring {@link TaskRecord} from storage.
+         */
+        TaskRecord create(ActivityManagerService service, int taskId, Intent intent,
+                Intent affinityIntent, String affinity, String rootAffinity,
+                ComponentName realActivity, ComponentName origActivity, boolean rootWasReset,
+                boolean autoRemoveRecents, boolean askedCompatMode, int userId,
+                int effectiveUid, String lastDescription, ArrayList<ActivityRecord> activities,
+                long lastTimeMoved, boolean neverRelinquishIdentity,
+                TaskDescription lastTaskDescription, int taskAffiliation, int prevTaskId,
+                int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
+                int resizeMode, boolean supportsPictureInPicture, boolean realActivitySuspended,
+                boolean userSetupComplete, int minWidth, int minHeight) {
+            return new TaskRecord(service, taskId, intent, affinityIntent, affinity,
+                    rootAffinity, realActivity, origActivity, rootWasReset, autoRemoveRecents,
+                    askedCompatMode, userId, effectiveUid, lastDescription, activities,
+                    lastTimeMoved, neverRelinquishIdentity, lastTaskDescription, taskAffiliation,
+                    prevTaskId, nextTaskId, taskAffiliationColor, callingUid, callingPackage,
+                    resizeMode, supportsPictureInPicture, realActivitySuspended, userSetupComplete,
+                    minWidth, minHeight);
+        }
+
+        TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
+                throws IOException, XmlPullParserException {
+            Intent intent = null;
+            Intent affinityIntent = null;
+            ArrayList<ActivityRecord> activities = new ArrayList<>();
+            ComponentName realActivity = null;
+            boolean realActivitySuspended = false;
+            ComponentName origActivity = null;
+            String affinity = null;
+            String rootAffinity = null;
+            boolean hasRootAffinity = false;
+            boolean rootHasReset = false;
+            boolean autoRemoveRecents = false;
+            boolean askedCompatMode = false;
+            int taskType = 0;
+            int userId = 0;
+            boolean userSetupComplete = true;
+            int effectiveUid = -1;
+            String lastDescription = null;
+            long lastTimeOnTop = 0;
+            boolean neverRelinquishIdentity = true;
+            int taskId = INVALID_TASK_ID;
+            final int outerDepth = in.getDepth();
+            TaskDescription taskDescription = new TaskDescription();
+            int taskAffiliation = INVALID_TASK_ID;
+            int taskAffiliationColor = 0;
+            int prevTaskId = INVALID_TASK_ID;
+            int nextTaskId = INVALID_TASK_ID;
+            int callingUid = -1;
+            String callingPackage = "";
+            int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
+            boolean supportsPictureInPicture = false;
+            Rect bounds = null;
+            int minWidth = INVALID_MIN_SIZE;
+            int minHeight = INVALID_MIN_SIZE;
+            int persistTaskVersion = 0;
+
+            for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
+                final String attrName = in.getAttributeName(attrNdx);
+                final String attrValue = in.getAttributeValue(attrNdx);
+                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
+                        attrName + " value=" + attrValue);
+                switch (attrName) {
+                    case ATTR_TASKID:
+                        if (taskId == INVALID_TASK_ID) taskId = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_REALACTIVITY:
+                        realActivity = ComponentName.unflattenFromString(attrValue);
+                        break;
+                    case ATTR_REALACTIVITY_SUSPENDED:
+                        realActivitySuspended = Boolean.valueOf(attrValue);
+                        break;
+                    case ATTR_ORIGACTIVITY:
+                        origActivity = ComponentName.unflattenFromString(attrValue);
+                        break;
+                    case ATTR_AFFINITY:
+                        affinity = attrValue;
+                        break;
+                    case ATTR_ROOT_AFFINITY:
+                        rootAffinity = attrValue;
+                        hasRootAffinity = true;
+                        break;
+                    case ATTR_ROOTHASRESET:
+                        rootHasReset = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_AUTOREMOVERECENTS:
+                        autoRemoveRecents = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_ASKEDCOMPATMODE:
+                        askedCompatMode = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_USERID:
+                        userId = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_USER_SETUP_COMPLETE:
+                        userSetupComplete = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_EFFECTIVE_UID:
+                        effectiveUid = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_TASKTYPE:
+                        taskType = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_LASTDESCRIPTION:
+                        lastDescription = attrValue;
+                        break;
+                    case ATTR_LASTTIMEMOVED:
+                        lastTimeOnTop = Long.parseLong(attrValue);
+                        break;
+                    case ATTR_NEVERRELINQUISH:
+                        neverRelinquishIdentity = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_TASK_AFFILIATION:
+                        taskAffiliation = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_PREV_AFFILIATION:
+                        prevTaskId = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_NEXT_AFFILIATION:
+                        nextTaskId = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_TASK_AFFILIATION_COLOR:
+                        taskAffiliationColor = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_CALLING_UID:
+                        callingUid = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_CALLING_PACKAGE:
+                        callingPackage = attrValue;
+                        break;
+                    case ATTR_RESIZE_MODE:
+                        resizeMode = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_SUPPORTS_PICTURE_IN_PICTURE:
+                        supportsPictureInPicture = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_NON_FULLSCREEN_BOUNDS:
+                        bounds = Rect.unflattenFromString(attrValue);
+                        break;
+                    case ATTR_MIN_WIDTH:
+                        minWidth = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_MIN_HEIGHT:
+                        minHeight = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_PERSIST_TASK_VERSION:
+                        persistTaskVersion = Integer.parseInt(attrValue);
+                        break;
+                    default:
+                        if (attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
+                            taskDescription.restoreFromXml(attrName, attrValue);
+                        } else {
+                            Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
+                        }
+                }
+            }
+
+            int event;
+            while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+                    (event != XmlPullParser.END_TAG || in.getDepth() >= outerDepth)) {
+                if (event == XmlPullParser.START_TAG) {
+                    final String name = in.getName();
+                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
+                            "TaskRecord: START_TAG name=" + name);
+                    if (TAG_AFFINITYINTENT.equals(name)) {
+                        affinityIntent = Intent.restoreFromXml(in);
+                    } else if (TAG_INTENT.equals(name)) {
+                        intent = Intent.restoreFromXml(in);
+                    } else if (TAG_ACTIVITY.equals(name)) {
+                        ActivityRecord activity =
+                                ActivityRecord.restoreFromXml(in, stackSupervisor);
+                        if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" +
+                                activity);
+                        if (activity != null) {
+                            activities.add(activity);
+                        }
+                    } else {
+                        Slog.e(TAG, "restoreTask: Unexpected name=" + name);
+                        XmlUtils.skipCurrentTag(in);
+                    }
+                }
+            }
+            if (!hasRootAffinity) {
+                rootAffinity = affinity;
+            } else if ("@".equals(rootAffinity)) {
+                rootAffinity = null;
+            }
+            if (effectiveUid <= 0) {
+                Intent checkIntent = intent != null ? intent : affinityIntent;
+                effectiveUid = 0;
+                if (checkIntent != null) {
+                    IPackageManager pm = AppGlobals.getPackageManager();
+                    try {
+                        ApplicationInfo ai = pm.getApplicationInfo(
+                                checkIntent.getComponent().getPackageName(),
+                                PackageManager.MATCH_UNINSTALLED_PACKAGES
+                                        | PackageManager.MATCH_DISABLED_COMPONENTS, userId);
+                        if (ai != null) {
+                            effectiveUid = ai.uid;
+                        }
+                    } catch (RemoteException e) {
+                    }
+                }
+                Slog.w(TAG, "Updating task #" + taskId + " for " + checkIntent
+                        + ": effectiveUid=" + effectiveUid);
+            }
+
+            if (persistTaskVersion < 1) {
+                // We need to convert the resize mode of home activities saved before version one if
+                // they are marked as RESIZE_MODE_RESIZEABLE to
+                // RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION since we didn't have that differentiation
+                // before version 1 and the system didn't resize home activities before then.
+                if (taskType == 1 /* old home type */ && resizeMode == RESIZE_MODE_RESIZEABLE) {
+                    resizeMode = RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+                }
+            } else {
+                // This activity has previously marked itself explicitly as both resizeable and
+                // supporting picture-in-picture.  Since there is no longer a requirement for
+                // picture-in-picture activities to be resizeable, we can mark this simply as
+                // resizeable and supporting picture-in-picture separately.
+                if (resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED) {
+                    resizeMode = RESIZE_MODE_RESIZEABLE;
+                    supportsPictureInPicture = true;
+                }
+            }
+
+            final TaskRecord task = create(stackSupervisor.mService, taskId, intent, affinityIntent,
+                    affinity, rootAffinity, realActivity, origActivity, rootHasReset,
+                    autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
+                    activities, lastTimeOnTop, neverRelinquishIdentity, taskDescription,
+                    taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid,
+                    callingPackage, resizeMode, supportsPictureInPicture, realActivitySuspended,
+                    userSetupComplete, minWidth, minHeight);
+            task.updateOverrideConfiguration(bounds);
+
+            for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
+                activities.get(activityNdx).setTask(task);
+            }
+
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task);
+            return task;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 4e3d8d2..6bd599b 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -24,6 +24,7 @@
 import static android.app.ActivityManager.USER_OP_SUCCESS;
 import static android.os.Process.SHELL_UID;
 import static android.os.Process.SYSTEM_UID;
+
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -37,6 +38,7 @@
 import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKING;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
@@ -830,6 +832,9 @@
     private IStorageManager getStorageManager() {
         return IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
     }
+    boolean startUser(final int userId, final boolean foreground) {
+        return startUser(userId, foreground, null);
+    }
 
     /**
      * Start user, if its not already running.
@@ -860,7 +865,10 @@
      * @param foreground true if user should be brought to the foreground
      * @return true if the user has been successfully started
      */
-    boolean startUser(final int userId, final boolean foreground) {
+    boolean startUser(
+            final int userId,
+            final boolean foreground,
+            @Nullable IProgressListener unlockListener) {
         if (mInjector.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
                 != PackageManager.PERMISSION_GRANTED) {
             String msg = "Permission Denial: switchUser() from pid="
@@ -918,6 +926,10 @@
                 final Integer userIdInt = userId;
                 mUserLru.remove(userIdInt);
                 mUserLru.add(userIdInt);
+
+                if (unlockListener != null) {
+                    uss.mUnlockProgress.addListener(unlockListener);
+                }
             }
             if (updateUmState) {
                 mInjector.getUserManagerInternal().setUserState(userId, uss.state);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 9cd52d7..bdfd4bd 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -323,7 +323,7 @@
     private final BroadcastReceiver mAccountsUpdatedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            EndPoint target = new EndPoint(null, null, context.getUserId());
+            EndPoint target = new EndPoint(null, null, getSendingUserId());
             updateRunningAccounts(target /* sync targets for user */);
         }
     };
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 2c6fe94..61b6fa0 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -196,9 +196,10 @@
 
     /**
      * @param userId userId to fetch data for.
+     * @param includePackage if false we will null out BrightnessChangeEvent.packageName
      * @return List of recent {@link BrightnessChangeEvent}s
      */
-    public ParceledListSlice<BrightnessChangeEvent> getEvents(int userId) {
+    public ParceledListSlice<BrightnessChangeEvent> getEvents(int userId, boolean includePackage) {
         // TODO include apps from any managed profiles in the brightness information.
         BrightnessChangeEvent[] events;
         synchronized (mEventsLock) {
@@ -207,7 +208,13 @@
         ArrayList<BrightnessChangeEvent> out = new ArrayList<>(events.length);
         for (int i = 0; i < events.length; ++i) {
             if (events[i].userId == userId) {
-                out.add(events[i]);
+                if (includePackage) {
+                    out.add(events[i]);
+                } else {
+                    BrightnessChangeEvent event = new BrightnessChangeEvent((events[i]));
+                    event.packageName = null;
+                    out.add(event);
+                }
             }
         }
         return new ParceledListSlice<>(out);
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index af8ecad..75f3056 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -33,11 +33,8 @@
 import android.opengl.Matrix;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings.Secure;
-import android.service.vr.IVrManager;
-import android.service.vr.IVrStateCallbacks;
 import android.util.MathUtils;
 import android.util.Slog;
 import android.view.animation.AnimationUtils;
@@ -51,7 +48,6 @@
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.time.ZoneId;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 import com.android.internal.R;
 
@@ -84,32 +80,6 @@
     private static final ColorMatrixEvaluator COLOR_MATRIX_EVALUATOR = new ColorMatrixEvaluator();
 
     private final Handler mHandler;
-    private final AtomicBoolean mIgnoreAllColorMatrixChanges = new AtomicBoolean();
-    private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
-        @Override
-        public void onVrStateChanged(final boolean enabled) {
-            // Turn off all night mode display stuff while device is in VR mode.
-            mIgnoreAllColorMatrixChanges.set(enabled);
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    // Cancel in-progress animations
-                    if (mColorMatrixAnimator != null) {
-                        mColorMatrixAnimator.cancel();
-                    }
-
-                    final DisplayTransformManager dtm =
-                            getLocalService(DisplayTransformManager.class);
-                    if (enabled) {
-                        dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, MATRIX_IDENTITY);
-                    } else if (mController != null && mController.isActivated()) {
-                        setMatrix(mController.getColorTemperature(), mMatrixNight);
-                        dtm.setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, mMatrixNight);
-                    }
-                }
-            });
-        }
-    };
 
     private float[] mMatrixNight = new float[16];
 
@@ -136,18 +106,6 @@
 
     @Override
     public void onBootPhase(int phase) {
-        if (phase >= PHASE_SYSTEM_SERVICES_READY) {
-            final IVrManager vrManager = IVrManager.Stub.asInterface(
-                    getBinderService(Context.VR_SERVICE));
-            if (vrManager != null) {
-                try {
-                    vrManager.registerListener(mVrStateCallbacks);
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Failed to register VR mode state listener: " + e);
-                }
-            }
-        }
-
         if (phase >= PHASE_BOOT_COMPLETED) {
             mBootCompleted = true;
 
@@ -359,11 +317,6 @@
             mColorMatrixAnimator.cancel();
         }
 
-        // Don't do any color matrix change animations if we are ignoring them anyway.
-        if (mIgnoreAllColorMatrixChanges.get()) {
-            return;
-        }
-
         final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
         final float[] from = dtm.getColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY);
         final float[] to = mIsActivated ? mMatrixNight : MATRIX_IDENTITY;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 379aaad..19a74d7 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -29,6 +29,7 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
@@ -1752,14 +1753,30 @@
         }
 
         @Override // Binder call
-        public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents() {
+        public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(String callingPackage) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.BRIGHTNESS_SLIDER_USAGE,
                     "Permission to read brightness events.");
-            int userId = UserHandle.getUserId(Binder.getCallingUid());
+
+            final int callingUid = Binder.getCallingUid();
+            AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
+            final int mode = appOpsManager.checkOp(AppOpsManager.OP_GET_USAGE_STATS,
+                    callingUid, callingPackage);
+            final boolean hasUsageStats;
+            if (mode == AppOpsManager.MODE_DEFAULT) {
+                // The default behavior here is to check if PackageManager has given the app
+                // permission.
+                hasUsageStats = mContext.checkCallingPermission(
+                        Manifest.permission.PACKAGE_USAGE_STATS)
+                        == PackageManager.PERMISSION_GRANTED;
+            } else {
+                hasUsageStats = mode == AppOpsManager.MODE_ALLOWED;
+            }
+
+            final int userId = UserHandle.getUserId(callingUid);
             final long token = Binder.clearCallingIdentity();
             try {
-                return mBrightnessTracker.getEvents(userId);
+                return mBrightnessTracker.getEvents(userId, hasUsageStats);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index e08c659..4525a49 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -479,7 +479,11 @@
             retArray[i] = foundInstances.get(i).intValue();
         }
 
-        Log.w(TAG, "Found " + retArray.length + " apps on hub handle " + hubHandle);
+        if (retArray.length == 0) {
+            Log.d(TAG, "No nanoapps found on hub ID " + hubHandle + " using NanoAppFilter: "
+                    + filter);
+        }
+
         return retArray;
     }
 
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 019c7c2..7d64aed4 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -39,8 +39,10 @@
 import android.content.pm.UserInfo;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.IInterface;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -86,6 +88,7 @@
     protected final String TAG = getClass().getSimpleName();
     protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
+    private static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
     protected static final String ENABLED_SERVICES_SEPARATOR = ":";
 
     /**
@@ -105,12 +108,15 @@
     private final IPackageManager mPm;
     private final UserManager mUm;
     private final Config mConfig;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
 
     // contains connections to all connected services, including app services
     // and system services
     private final ArrayList<ManagedServiceInfo> mServices = new ArrayList<>();
     // things that will be put into mServices as soon as they're ready
     private final ArrayList<String> mServicesBinding = new ArrayList<>();
+    private final ArraySet<String> mServicesRebinding = new ArraySet<>();
+
     // lists the component names of all enabled (and therefore potentially connected)
     // app services for current profiles.
     private ArraySet<ComponentName> mEnabledServicesForCurrentProfiles
@@ -890,6 +896,7 @@
 
         final String servicesBindingTag = name.toString() + "/" + userid;
         if (mServicesBinding.contains(servicesBindingTag)) {
+            Slog.v(TAG, "Not registering " + name + " as bind is already in progress");
             // stop registering this thing already! we're working on it
             return;
         }
@@ -938,6 +945,7 @@
                     boolean added = false;
                     ManagedServiceInfo info = null;
                     synchronized (mMutex) {
+                        mServicesRebinding.remove(servicesBindingTag);
                         mServicesBinding.remove(servicesBindingTag);
                         try {
                             mService = asInterface(binder);
@@ -959,6 +967,27 @@
                     mServicesBinding.remove(servicesBindingTag);
                     Slog.v(TAG, getCaption() + " connection lost: " + name);
                 }
+
+                @Override
+                public void onBindingDied(ComponentName name) {
+                    Slog.w(TAG, getCaption() + " binding died: " + name);
+                    synchronized (mMutex) {
+                        mServicesBinding.remove(servicesBindingTag);
+                        mContext.unbindService(this);
+                        if (!mServicesRebinding.contains(servicesBindingTag)) {
+                            mServicesRebinding.add(servicesBindingTag);
+                            mHandler.postDelayed(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        registerService(name, userid);
+                                    }
+                               }, ON_BINDING_DIED_REBIND_DELAY_MS);
+                        } else {
+                            Slog.v(TAG, getCaption() + " not rebinding as "
+                                    + "a previous rebind attempt was made: " + name);
+                        }
+                    }
+                }
             };
             if (!mContext.bindServiceAsUser(intent,
                 serviceConnection,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bec6fc2..cf01400 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4743,8 +4743,7 @@
     }
 
     void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
-        if (!mAccessibilityManager.isObservedEventType(
-                AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)) {
+        if (!mAccessibilityManager.isEnabled()) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
index c5f80bb..ba7fe78 100644
--- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
+++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
@@ -29,8 +29,8 @@
 import android.provider.Settings;
 import android.service.notification.Condition;
 import android.service.notification.IConditionProvider;
+import android.service.notification.ScheduleCalendar;
 import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.ScheduleInfo;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -45,7 +45,6 @@
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.List;
-import java.util.TimeZone;
 
 /**
  * Built-in zen condition provider for daily scheduled time-based conditions.
@@ -134,7 +133,7 @@
             return;
         }
         synchronized (mSubscriptions) {
-            mSubscriptions.put(conditionId, toScheduleCalendar(conditionId));
+            mSubscriptions.put(conditionId, ZenModeConfig.toScheduleCalendar(conditionId));
         }
         evaluateSubscriptions();
     }
@@ -243,15 +242,6 @@
         return cal != null && cal.isInSchedule(time);
     }
 
-    private static ScheduleCalendar toScheduleCalendar(Uri conditionId) {
-        final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(conditionId);
-        if (schedule == null || schedule.days == null || schedule.days.length == 0) return null;
-        final ScheduleCalendar sc = new ScheduleCalendar();
-        sc.setSchedule(schedule);
-        sc.setTimeZone(TimeZone.getDefault());
-        return sc;
-    }
-
     private void setRegistered(boolean registered) {
         if (mRegistered == registered) return;
         if (DEBUG) Slog.d(TAG, "setRegistered " + registered);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 1e9fab5..700ccad 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -98,11 +98,6 @@
     private final Metrics mMetrics = new Metrics();
     private final ConditionProviders.Config mServiceConfig;
 
-    protected final ArrayList<String> mDefaultRuleIds = new ArrayList<>();
-    private final String EVENTS_DEFAULT_RULE = "EVENTS_DEFAULT_RULE";
-    private final String SCHEDULED_DEFAULT_RULE_1 = "SCHEDULED_DEFAULT_RULE_1";
-    private final String SCHEDULED_DEFAULT_RULE_2 = "SCHEDULED_DEFAULT_RULE_2";
-
     @VisibleForTesting protected int mZenMode;
     private int mUser = UserHandle.USER_SYSTEM;
     @VisibleForTesting protected ZenModeConfig mConfig;
@@ -115,9 +110,8 @@
     public static final long SUPPRESSED_EFFECT_ALL = SUPPRESSED_EFFECT_CALLS
             | SUPPRESSED_EFFECT_NOTIFICATIONS;
 
-    protected String mDefaultRuleWeeknightsName;
+    protected String mDefaultRuleEveryNightName;
     protected String mDefaultRuleEventsName;
-    protected String mDefaultRuleWeekendsName;
 
     public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) {
         mContext = context;
@@ -230,12 +224,25 @@
             config = mDefaultConfig.copy();
             config.user = user;
         }
+        enforceDefaultRulesExist(config);
         synchronized (mConfig) {
             setConfigLocked(config, reason);
         }
         cleanUpZenRules();
     }
 
+    private void enforceDefaultRulesExist(ZenModeConfig config) {
+        for (String id : ZenModeConfig.DEFAULT_RULE_IDS) {
+            if (!config.automaticRules.containsKey(id)) {
+                if (id.equals(ZenModeConfig.EVENTS_DEFAULT_RULE_ID)) {
+                    appendDefaultEventRules(config);
+                } else if (id.equals(ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID)) {
+                    appendDefaultEveryNightRule(config);
+                }
+            }
+        }
+    }
+
     public int getZenModeListenerInterruptionFilter() {
         return NotificationManager.zenModeToInterruptionFilter(mZenMode);
     }
@@ -421,17 +428,12 @@
 
     public void setDefaultZenRules(Context context) {
         mDefaultConfig = readDefaultConfig(context.getResources());
-
-        mDefaultRuleIds.add(EVENTS_DEFAULT_RULE);
-        mDefaultRuleIds.add(SCHEDULED_DEFAULT_RULE_1);
-        mDefaultRuleIds.add(SCHEDULED_DEFAULT_RULE_2);
-
         appendDefaultRules(mDefaultConfig);
     }
 
     private void appendDefaultRules (ZenModeConfig config) {
         getDefaultRuleNames();
-        appendDefaultScheduleRules(config);
+        appendDefaultEveryNightRule(config);
         appendDefaultEventRules(config);
     }
 
@@ -450,7 +452,7 @@
     protected void updateDefaultZenRules() {
         ZenModeConfig configDefaultRules = new ZenModeConfig();
         appendDefaultRules(configDefaultRules); // "new" localized default rules
-        for (String ruleId : mDefaultRuleIds) {
+        for (String ruleId : ZenModeConfig.DEFAULT_RULE_IDS) {
             AutomaticZenRule currRule = getAutomaticZenRule(ruleId);
             ZenRule defaultRule = configDefaultRules.automaticRules.get(ruleId);
             // if default rule wasn't customized, use localized name instead of previous
@@ -812,10 +814,8 @@
 
     private void getDefaultRuleNames() {
         // on locale-change, these values differ
-        mDefaultRuleWeeknightsName = mContext.getResources()
-                .getString(R.string.zen_mode_default_weeknights_name);
-        mDefaultRuleWeekendsName = mContext.getResources()
-                .getString(R.string.zen_mode_default_weekends_name);
+        mDefaultRuleEveryNightName = mContext.getResources()
+                .getString(R.string.zen_mode_default_every_night_name);
         mDefaultRuleEventsName = mContext.getResources()
                 .getString(R.string.zen_mode_default_events_name);
     }
@@ -935,39 +935,23 @@
         return new ZenModeConfig();
     }
 
-    private void appendDefaultScheduleRules(ZenModeConfig config) {
+    private void appendDefaultEveryNightRule(ZenModeConfig config) {
         if (config == null) return;
 
         final ScheduleInfo weeknights = new ScheduleInfo();
-        weeknights.days = ZenModeConfig.WEEKNIGHT_DAYS;
+        weeknights.days = ZenModeConfig.ALL_DAYS;
         weeknights.startHour = 22;
         weeknights.endHour = 7;
         weeknights.exitAtAlarm = true;
-        final ZenRule rule1 = new ZenRule();
-        rule1.enabled = false;
-        rule1.name = mDefaultRuleWeeknightsName;
-        rule1.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
-        rule1.zenMode = Global.ZEN_MODE_ALARMS;
-        rule1.component = ScheduleConditionProvider.COMPONENT;
-        rule1.id = SCHEDULED_DEFAULT_RULE_1;
-        rule1.creationTime = System.currentTimeMillis();
-        config.automaticRules.put(rule1.id, rule1);
-
-        final ScheduleInfo weekends = new ScheduleInfo();
-        weekends.days = ZenModeConfig.WEEKEND_DAYS;
-        weekends.startHour = 23;
-        weekends.startMinute = 30;
-        weekends.endHour = 10;
-        weekends.exitAtAlarm = true;
-        final ZenRule rule2 = new ZenRule();
-        rule2.enabled = false;
-        rule2.name = mDefaultRuleWeekendsName;
-        rule2.conditionId = ZenModeConfig.toScheduleConditionId(weekends);
-        rule2.zenMode = Global.ZEN_MODE_ALARMS;
-        rule2.component = ScheduleConditionProvider.COMPONENT;
-        rule2.id = SCHEDULED_DEFAULT_RULE_2;
-        rule2.creationTime = System.currentTimeMillis();
-        config.automaticRules.put(rule2.id, rule2);
+        final ZenRule rule = new ZenRule();
+        rule.enabled = false;
+        rule.name = mDefaultRuleEveryNightName;
+        rule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
+        rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        rule.component = ScheduleConditionProvider.COMPONENT;
+        rule.id = ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID;
+        rule.creationTime = System.currentTimeMillis();
+        config.automaticRules.put(rule.id, rule);
     }
 
     private void appendDefaultEventRules(ZenModeConfig config) {
@@ -980,9 +964,9 @@
         rule.enabled = false;
         rule.name = mDefaultRuleEventsName;
         rule.conditionId = ZenModeConfig.toEventConditionId(events);
-        rule.zenMode = Global.ZEN_MODE_ALARMS;
+        rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         rule.component = EventConditionProvider.COMPONENT;
-        rule.id = EVENTS_DEFAULT_RULE;
+        rule.id = ZenModeConfig.EVENTS_DEFAULT_RULE_ID;
         rule.creationTime = System.currentTimeMillis();
         config.automaticRules.put(rule.id, rule);
     }
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 6a06be2..be66fe2 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -496,6 +496,26 @@
         }
     }
 
+    public boolean createProfileSnapshot(int appId, String packageName, String codePath)
+            throws InstallerException {
+        if (!checkBeforeRemote()) return false;
+        try {
+            return mInstalld.createProfileSnapshot(appId, packageName, codePath);
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
+    public void destroyProfileSnapshot(String packageName, String codePath)
+            throws InstallerException {
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.destroyProfileSnapshot(packageName, codePath);
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
     public void invalidateMounts() throws InstallerException {
         if (!checkBeforeRemote()) return;
         try {
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index e5e9a37..30072d4 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -40,14 +40,11 @@
 import android.content.pm.InstantAppResolveInfo;
 import android.content.pm.InstantAppResolveInfo.InstantAppDigest;
 import android.metrics.LogMaker;
-import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
-import android.util.Pair;
-import android.util.Slog;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
@@ -56,11 +53,9 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
-import java.util.concurrent.TimeoutException;
 
 /** @hide */
 public abstract class InstantAppResolver {
@@ -159,8 +154,9 @@
                     long startTime) {
                 final String packageName;
                 final String splitName;
-                final int versionCode;
+                final long versionCode;
                 final Intent failureIntent;
+                final Bundle extras;
                 if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
                     final AuxiliaryResolveInfo instantAppIntentInfo =
                             InstantAppResolver.filterInstantAppIntent(
@@ -172,17 +168,20 @@
                         splitName = instantAppIntentInfo.splitName;
                         versionCode = instantAppIntentInfo.resolveInfo.getVersionCode();
                         failureIntent = instantAppIntentInfo.failureIntent;
+                        extras = instantAppIntentInfo.resolveInfo.getExtras();
                     } else {
                         packageName = null;
                         splitName = null;
                         versionCode = -1;
                         failureIntent = null;
+                        extras = null;
                     }
                 } else {
                     packageName = null;
                     splitName = null;
                     versionCode = -1;
                     failureIntent = null;
+                    extras = null;
                 }
                 final Intent installerIntent = buildEphemeralInstallerIntent(
                         Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE,
@@ -197,6 +196,7 @@
                         requestObj.responseObj.installFailureActivity,
                         versionCode,
                         token,
+                        extras,
                         false /*needsPhaseTwo*/);
                 installerIntent.setComponent(new ComponentName(
                         instantAppInstaller.packageName, instantAppInstaller.name));
@@ -241,8 +241,9 @@
             @NonNull String instantAppPackageName,
             @Nullable String instantAppSplitName,
             @Nullable ComponentName installFailureActivity,
-            int versionCode,
+            long versionCode,
             @Nullable String token,
+            @Nullable Bundle extras,
             boolean needsPhaseTwo) {
         // Construct the intent that launches the instant installer
         int flags = origIntent.getFlags();
@@ -259,6 +260,9 @@
             intent.putExtra(Intent.EXTRA_EPHEMERAL_HOSTNAME, origIntent.getData().getHost());
         }
         intent.putExtra(Intent.EXTRA_INSTANT_APP_ACTION, origIntent.getAction());
+        if (extras != null) {
+            intent.putExtra(Intent.EXTRA_INSTANT_APP_EXTRAS, extras);
+        }
 
         // We have all of the data we need; just start the installer without a second phase
         if (!needsPhaseTwo) {
@@ -308,7 +312,8 @@
 
             intent.putExtra(Intent.EXTRA_PACKAGE_NAME, instantAppPackageName);
             intent.putExtra(Intent.EXTRA_SPLIT_NAME, instantAppSplitName);
-            intent.putExtra(Intent.EXTRA_VERSION_CODE, versionCode);
+            intent.putExtra(Intent.EXTRA_VERSION_CODE, (int) (versionCode & 0x7fffffff));
+            intent.putExtra(Intent.EXTRA_LONG_VERSION_CODE, versionCode);
             intent.putExtra(Intent.EXTRA_CALLING_PACKAGE, callingPackage);
             if (verificationBundle != null) {
                 intent.putExtra(Intent.EXTRA_VERIFICATION_BUNDLE, verificationBundle);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 711352f..5cf08dc 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -225,7 +225,7 @@
     @GuardedBy("mLock")
     private String mPackageName;
     @GuardedBy("mLock")
-    private int mVersionCode;
+    private long mVersionCode;
     @GuardedBy("mLock")
     private Signature[] mSignatures;
     @GuardedBy("mLock")
@@ -1006,7 +1006,7 @@
             // Use first package to define unknown values
             if (mPackageName == null) {
                 mPackageName = apk.packageName;
-                mVersionCode = apk.versionCode;
+                mVersionCode = apk.getLongVersionCode();
             }
             if (mSignatures == null) {
                 mSignatures = apk.signatures;
@@ -1057,7 +1057,7 @@
             // ensure we've got appropriate package name, version code and signatures
             if (mPackageName == null) {
                 mPackageName = pkgInfo.packageName;
-                mVersionCode = pkgInfo.versionCode;
+                mVersionCode = pkgInfo.getLongVersionCode();
             }
             if (mSignatures == null) {
                 mSignatures = pkgInfo.signatures;
@@ -1149,7 +1149,7 @@
                     + " specified package " + params.appPackageName
                     + " inconsistent with " + apk.packageName);
         }
-        if (mVersionCode != apk.versionCode) {
+        if (mVersionCode != apk.getLongVersionCode()) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag
                     + " version code " + apk.versionCode + " inconsistent with "
                     + mVersionCode);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4b6589c..f60cb06 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -111,7 +111,6 @@
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
-import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter;
 
 import android.Manifest;
 import android.annotation.IntDef;
@@ -187,6 +186,7 @@
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VerifierInfo;
 import android.content.pm.VersionedPackage;
+import android.content.pm.dex.IArtManager;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
@@ -242,6 +242,8 @@
 import android.util.ExceptionUtils;
 import android.util.Log;
 import android.util.LogPrinter;
+import android.util.LongSparseArray;
+import android.util.LongSparseLongArray;
 import android.util.MathUtils;
 import android.util.PackageUtils;
 import android.util.Pair;
@@ -263,16 +265,13 @@
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.PackageHelper;
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.IParcelFileDescriptorFactory;
-import com.android.internal.os.RoSystemProperties;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.os.Zygote;
 import com.android.internal.telephony.CarrierAppUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
@@ -292,6 +291,7 @@
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.pm.Settings.VersionInfo;
+import com.android.server.pm.dex.ArtManagerService;
 import com.android.server.pm.dex.DexLogger;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
@@ -307,30 +307,23 @@
 import com.android.server.storage.DeviceStorageMonitorInternal;
 
 import dalvik.system.CloseGuard;
-import dalvik.system.DexFile;
 import dalvik.system.VMRuntime;
 
 import libcore.io.IoUtils;
-import libcore.io.Streams;
-import libcore.util.EmptyArray;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
-import java.io.FileReader;
 import java.io.FilenameFilter;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -341,15 +334,12 @@
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -363,7 +353,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.zip.GZIPInputStream;
 
 /**
  * Keep track of all those APKs everywhere.
@@ -888,8 +877,8 @@
         public final @Nullable String apk;
         public final @NonNull SharedLibraryInfo info;
 
-        SharedLibraryEntry(String _path, String _apk, String name, int version, int type,
-                String declaringPackageName, int declaringPackageVersionCode) {
+        SharedLibraryEntry(String _path, String _apk, String name, long version, int type,
+                String declaringPackageName, long declaringPackageVersionCode) {
             path = _path;
             apk = _apk;
             info = new SharedLibraryInfo(name, version, type, new VersionedPackage(
@@ -898,8 +887,8 @@
     }
 
     // Currently known shared libraries.
-    final ArrayMap<String, SparseArray<SharedLibraryEntry>> mSharedLibraries = new ArrayMap<>();
-    final ArrayMap<String, SparseArray<SharedLibraryEntry>> mStaticLibsByDeclaringPackage =
+    final ArrayMap<String, LongSparseArray<SharedLibraryEntry>> mSharedLibraries = new ArrayMap<>();
+    final ArrayMap<String, LongSparseArray<SharedLibraryEntry>> mStaticLibsByDeclaringPackage =
             new ArrayMap<>();
 
     // All available activities, for your resolving pleasure.
@@ -939,6 +928,8 @@
 
     final PackageInstallerService mInstallerService;
 
+    final ArtManagerService mArtManagerService;
+
     private final PackageDexOptimizer mPackageDexOptimizer;
     // DexManager handles the usage of dex files (e.g. secondary files, whether or not a package
     // is used by other apps).
@@ -2661,7 +2652,7 @@
                                     + ps.name + "; removing system app.  Last known codePath="
                                     + ps.codePathString + ", installStatus=" + ps.installStatus
                                     + ", versionCode=" + ps.versionCode + "; scanned versionCode="
-                                    + scannedPkg.mVersionCode);
+                                    + scannedPkg.getLongVersionCode());
                             removePackageLI(scannedPkg, true);
                             mExpectingBetter.put(ps.name, ps.codePath);
                         }
@@ -3047,6 +3038,7 @@
             }
 
             mInstallerService = new PackageInstallerService(context, this);
+            mArtManagerService = new ArtManagerService(this, mInstaller, mInstallLock);
             final Pair<ComponentName, String> instantAppResolverComponent =
                     getInstantAppResolverLPr();
             if (instantAppResolverComponent != null) {
@@ -3859,7 +3851,7 @@
     public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage,
             int flags, int userId) {
         return getPackageInfoInternal(versionedPackage.getPackageName(),
-                versionedPackage.getVersionCode(), flags, Binder.getCallingUid(), userId);
+                versionedPackage.getLongVersionCode(), flags, Binder.getCallingUid(), userId);
     }
 
     /**
@@ -3868,7 +3860,7 @@
      * to clearing. Because it can only be provided by trusted code, it's value can be
      * trusted and will be used as-is; unlike userId which will be validated by this method.
      */
-    private PackageInfo getPackageInfoInternal(String packageName, int versionCode,
+    private PackageInfo getPackageInfoInternal(String packageName, long versionCode,
             int flags, int filterCallingUid, int userId) {
         if (!sUserManager.exists(userId)) return null;
         flags = updateFlagsForPackage(flags, userId, packageName);
@@ -4065,7 +4057,7 @@
                 if (index < 0) {
                     continue;
                 }
-                if (uidPs.pkg.usesStaticLibrariesVersions[index] == libEntry.info.getVersion()) {
+                if (uidPs.pkg.usesStaticLibrariesVersions[index] == libEntry.info.getLongVersion()) {
                     return false;
                 }
             }
@@ -4461,7 +4453,8 @@
             final int[] allUsers = sUserManager.getUserIds();
             final int libCount = mSharedLibraries.size();
             for (int i = 0; i < libCount; i++) {
-                final SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
+                final LongSparseArray<SharedLibraryEntry> versionedLib
+                        = mSharedLibraries.valueAt(i);
                 if (versionedLib == null) {
                     continue;
                 }
@@ -4478,7 +4471,8 @@
                     final VersionedPackage declaringPackage = libInfo.getDeclaringPackage();
                     // Resolve the package name - we use synthetic package names internally
                     final String internalPackageName = resolveInternalPackageNameLPr(
-                            declaringPackage.getPackageName(), declaringPackage.getVersionCode());
+                            declaringPackage.getPackageName(),
+                            declaringPackage.getLongVersionCode());
                     final PackageSetting ps = mSettings.getPackageLPr(internalPackageName);
                     // Skip unused static shared libs cached less than the min period
                     // to prevent pruning a lib needed by a subsequently installed package.
@@ -4489,7 +4483,7 @@
                         packagesToDelete = new ArrayList<>();
                     }
                     packagesToDelete.add(new VersionedPackage(internalPackageName,
-                            declaringPackage.getVersionCode()));
+                            declaringPackage.getLongVersionCode()));
                 }
             }
         }
@@ -4499,7 +4493,7 @@
             for (int i = 0; i < packageCount; i++) {
                 final VersionedPackage pkgToDelete = packagesToDelete.get(i);
                 // Delete the package synchronously (will fail of the lib used for any user).
-                if (deletePackageX(pkgToDelete.getPackageName(), pkgToDelete.getVersionCode(),
+                if (deletePackageX(pkgToDelete.getPackageName(), pkgToDelete.getLongVersionCode(),
                         UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS)
                                 == PackageManager.DELETE_SUCCEEDED) {
                     if (volume.getUsableSpace() >= neededSpace) {
@@ -4804,7 +4798,7 @@
 
             final int libCount = mSharedLibraries.size();
             for (int i = 0; i < libCount; i++) {
-                SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
+                LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
                 if (versionedLib == null) {
                     continue;
                 }
@@ -4828,7 +4822,7 @@
                     }
 
                     SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getName(),
-                            libInfo.getVersion(), libInfo.getType(),
+                            libInfo.getLongVersion(), libInfo.getType(),
                             libInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr(libInfo,
                             flags, userId));
 
@@ -4864,7 +4858,7 @@
                 if (libIdx < 0) {
                     continue;
                 }
-                if (ps.usesStaticLibrariesVersions[libIdx] != libInfo.getVersion()) {
+                if (ps.usesStaticLibrariesVersions[libIdx] != libInfo.getLongVersion()) {
                     continue;
                 }
                 if (versionedPackages == null) {
@@ -4945,7 +4939,7 @@
             Set<String> libs = null;
             final int libCount = mSharedLibraries.size();
             for (int i = 0; i < libCount; i++) {
-                SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
+                LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.valueAt(i);
                 if (versionedLib == null) {
                     continue;
                 }
@@ -8377,12 +8371,12 @@
                 // version of the new path against what we have stored to determine
                 // what to do.
                 if (DEBUG_INSTALL) Slog.d(TAG, "Path changing from " + ps.codePath);
-                if (pkg.mVersionCode <= ps.versionCode) {
+                if (pkg.getLongVersionCode() <= ps.versionCode) {
                     // The system package has been updated and the code path does not match
                     // Ignore entry. Skip it.
                     if (DEBUG_INSTALL) Slog.i(TAG, "Package " + ps.name + " at " + pkg.codePath
                             + " ignored: updated version " + ps.versionCode
-                            + " better than this " + pkg.mVersionCode);
+                            + " better than this " + pkg.getLongVersionCode());
                     if (!updatedPs.codePathString.equals(pkg.codePath)) {
                         Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg "
                                 + ps.name + " changing from " + updatedPs.codePathString
@@ -8394,7 +8388,7 @@
                         updatedPs.resourcePathString = pkg.codePath;
                     }
                     updatedPs.pkg = pkg;
-                    updatedPs.versionCode = pkg.mVersionCode;
+                    updatedPs.versionCode = pkg.getLongVersionCode();
 
                     // Update the disabled system child packages to point to the package too.
                     final int childCount = updatedPs.childPackageNames != null
@@ -8405,7 +8399,7 @@
                                 childPackageName);
                         if (updatedChildPkg != null) {
                             updatedChildPkg.pkg = pkg;
-                            updatedChildPkg.versionCode = pkg.mVersionCode;
+                            updatedChildPkg.versionCode = pkg.getLongVersionCode();
                         }
                     }
                 } else {
@@ -8423,7 +8417,7 @@
 
                     logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + pkg.codePath
                             + " reverting from " + ps.codePathString
-                            + ": new version " + pkg.mVersionCode
+                            + ": new version " + pkg.getLongVersionCode()
                             + " better than installed " + ps.versionCode);
 
                     InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
@@ -8478,7 +8472,7 @@
 
             throw new PackageManagerException(Log.WARN, "Package " + pkg.packageName + " at "
                     + pkg.codePath + " ignored: updated version " + updatedPs.versionCode
-                    + " better than this " + pkg.mVersionCode);
+                    + " better than this " + pkg.getLongVersionCode());
         }
 
         if (isUpdatedPkg) {
@@ -8533,11 +8527,11 @@
                  * already installed version, hide it. It will be scanned later
                  * and re-added like an update.
                  */
-                if (pkg.mVersionCode <= ps.versionCode) {
+                if (pkg.getLongVersionCode() <= ps.versionCode) {
                     shouldHideSystemApp = true;
                     logCriticalInfo(Log.INFO, "Package " + ps.name + " appeared at " + pkg.codePath
-                            + " but new version " + pkg.mVersionCode + " better than installed "
-                            + ps.versionCode + "; hiding system");
+                            + " but new version " + pkg.getLongVersionCode()
+                            + " better than installed " + ps.versionCode + "; hiding system");
                 } else {
                     /*
                      * The newly found system app is a newer version that the
@@ -8547,7 +8541,8 @@
                      */
                     logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + pkg.codePath
                             + " reverting from " + ps.codePathString + ": new version "
-                            + pkg.mVersionCode + " better than installed " + ps.versionCode);
+                            + pkg.getLongVersionCode() + " better than installed "
+                            + ps.versionCode);
                     InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
                             ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
                     synchronized (mInstallLock) {
@@ -9125,12 +9120,12 @@
         }
     }
 
-    private void findSharedNonSystemLibrariesRecursive(ArrayList<String> libs, int[] versions,
+    private void findSharedNonSystemLibrariesRecursive(ArrayList<String> libs, long[] versions,
             ArrayList<PackageParser.Package> collected, Set<String> collectedNames) {
         final int libNameCount = libs.size();
         for (int i = 0; i < libNameCount; i++) {
             String libName = libs.get(i);
-            int version = (versions != null && versions.length == libNameCount)
+            long version = (versions != null && versions.length == libNameCount)
                     ? versions[i] : PackageManager.VERSION_CODE_HIGHEST;
             PackageParser.Package libPkg = findSharedNonSystemLibrary(libName, version);
             if (libPkg != null) {
@@ -9139,7 +9134,7 @@
         }
     }
 
-    private PackageParser.Package findSharedNonSystemLibrary(String name, int version) {
+    private PackageParser.Package findSharedNonSystemLibrary(String name, long version) {
         synchronized (mPackages) {
             SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(name, version);
             if (libEntry != null) {
@@ -9149,8 +9144,8 @@
         }
     }
 
-    private SharedLibraryEntry getSharedLibraryEntryLPr(String name, int version) {
-        SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
+    private SharedLibraryEntry getSharedLibraryEntryLPr(String name, long version) {
+        LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
         if (versionedLib == null) {
             return null;
         }
@@ -9158,15 +9153,15 @@
     }
 
     private SharedLibraryEntry getLatestSharedLibraVersionLPr(PackageParser.Package pkg) {
-        SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(
+        LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(
                 pkg.staticSharedLibName);
         if (versionedLib == null) {
             return null;
         }
-        int previousLibVersion = -1;
+        long previousLibVersion = -1;
         final int versionCount = versionedLib.size();
         for (int i = 0; i < versionCount; i++) {
-            final int libVersion = versionedLib.keyAt(i);
+            final long libVersion = versionedLib.keyAt(i);
             if (libVersion < pkg.staticSharedLibVersion) {
                 previousLibVersion = Math.max(previousLibVersion, libVersion);
             }
@@ -9450,14 +9445,14 @@
     }
 
     private Set<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
-            @Nullable int[] requiredVersions, @Nullable String[][] requiredCertDigests,
+            @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
             @NonNull String packageName, @Nullable PackageParser.Package changingLib,
             boolean required, int targetSdk, @Nullable Set<String> outUsedLibraries)
             throws PackageManagerException {
         final int libCount = requestedLibraries.size();
         for (int i = 0; i < libCount; i++) {
             final String libName = requestedLibraries.get(i);
-            final int libVersion = requiredVersions != null ? requiredVersions[i]
+            final long libVersion = requiredVersions != null ? requiredVersions[i]
                     : SharedLibraryInfo.VERSION_UNDEFINED;
             final SharedLibraryEntry libEntry = getSharedLibraryEntryLPr(libName, libVersion);
             if (libEntry == null) {
@@ -9472,11 +9467,11 @@
                 }
             } else {
                 if (requiredVersions != null && requiredCertDigests != null) {
-                    if (libEntry.info.getVersion() != requiredVersions[i]) {
+                    if (libEntry.info.getLongVersion() != requiredVersions[i]) {
                         throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
                             "Package " + packageName + " requires unavailable static shared"
                                     + " library " + libName + " version "
-                                    + libEntry.info.getVersion() + "; failing!");
+                                    + libEntry.info.getLongVersion() + "; failing!");
                     }
 
                     PackageParser.Package libPkg = mPackages.get(libEntry.apk);
@@ -9500,7 +9495,7 @@
                     if (expectedCertDigests.length != libCertDigests.length) {
                         throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
                                 "Package " + packageName + " requires differently signed" +
-                                        " static sDexLoadReporter.java:45.19hared library; failing!");
+                                        " static shared library; failing!");
                     }
 
                     // Use a predictable order as signature order may vary
@@ -9719,6 +9714,7 @@
         // them in the case where we're not upgrading or booting for the first time.
         String primaryCpuAbiFromSettings = null;
         String secondaryCpuAbiFromSettings = null;
+        boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
 
         // writer
         synchronized (mPackages) {
@@ -9796,11 +9792,14 @@
                 }
             }
 
-            if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) == 0) {
+            if (!needToDeriveAbi) {
                 PackageSetting foundPs = mSettings.getPackageLPr(pkg.packageName);
                 if (foundPs != null) {
                     primaryCpuAbiFromSettings = foundPs.primaryCpuAbiString;
                     secondaryCpuAbiFromSettings = foundPs.secondaryCpuAbiString;
+                } else {
+                    // when re-adding a system package failed after uninstalling updates.
+                    needToDeriveAbi = true;
                 }
             }
 
@@ -9835,7 +9834,7 @@
                 pkgSetting = Settings.createNewSetting(pkg.packageName, origPackage,
                         disabledPkgSetting, realName, suid, destCodeFile, destResourceFile,
                         pkg.applicationInfo.nativeLibraryRootDir, pkg.applicationInfo.primaryCpuAbi,
-                        pkg.applicationInfo.secondaryCpuAbi, pkg.mVersionCode,
+                        pkg.applicationInfo.secondaryCpuAbi, pkg.getLongVersionCode(),
                         pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags, user,
                         true /*allowInstall*/, instantApp, virtualPreload,
                         parentPackageName, pkg.getChildPackageNames(),
@@ -10016,7 +10015,7 @@
         final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
 
         if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
-            if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0) {
+            if (needToDeriveAbi) {
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
                 final boolean extractNativeLibs = !pkg.isLibrary();
                 derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs, mAppLib32InstallDir);
@@ -10403,20 +10402,20 @@
                 }
 
                 // The version codes must be ordered as lib versions
-                int minVersionCode = Integer.MIN_VALUE;
-                int maxVersionCode = Integer.MAX_VALUE;
+                long minVersionCode = Long.MIN_VALUE;
+                long maxVersionCode = Long.MAX_VALUE;
 
-                SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(
+                LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(
                         pkg.staticSharedLibName);
                 if (versionedLib != null) {
                     final int versionCount = versionedLib.size();
                     for (int i = 0; i < versionCount; i++) {
                         SharedLibraryInfo libInfo = versionedLib.valueAt(i).info;
-                        final int libVersionCode = libInfo.getDeclaringPackage()
-                                .getVersionCode();
-                        if (libInfo.getVersion() <  pkg.staticSharedLibVersion) {
+                        final long libVersionCode = libInfo.getDeclaringPackage()
+                                .getLongVersionCode();
+                        if (libInfo.getLongVersion() <  pkg.staticSharedLibVersion) {
                             minVersionCode = Math.max(minVersionCode, libVersionCode + 1);
-                        } else if (libInfo.getVersion() >  pkg.staticSharedLibVersion) {
+                        } else if (libInfo.getLongVersion() >  pkg.staticSharedLibVersion) {
                             maxVersionCode = Math.min(maxVersionCode, libVersionCode - 1);
                         } else {
                             minVersionCode = maxVersionCode = libVersionCode;
@@ -10424,7 +10423,8 @@
                         }
                     }
                 }
-                if (pkg.mVersionCode < minVersionCode || pkg.mVersionCode > maxVersionCode) {
+                if (pkg.getLongVersionCode() < minVersionCode
+                        || pkg.getLongVersionCode() > maxVersionCode) {
                     throw new PackageManagerException("Static shared"
                             + " lib version codes must be ordered as lib versions");
                 }
@@ -10514,11 +10514,11 @@
         }
     }
 
-    private boolean addSharedLibraryLPw(String path, String apk, String name, int version,
-            int type, String declaringPackageName, int declaringVersionCode) {
-        SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
+    private boolean addSharedLibraryLPw(String path, String apk, String name, long version,
+            int type, String declaringPackageName, long declaringVersionCode) {
+        LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
         if (versionedLib == null) {
-            versionedLib = new SparseArray<>();
+            versionedLib = new LongSparseArray<>();
             mSharedLibraries.put(name, versionedLib);
             if (type == SharedLibraryInfo.TYPE_STATIC) {
                 mStaticLibsByDeclaringPackage.put(declaringPackageName, versionedLib);
@@ -10532,8 +10532,8 @@
         return true;
     }
 
-    private boolean removeSharedLibraryLPw(String name, int version) {
-        SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
+    private boolean removeSharedLibraryLPw(String name, long version) {
+        LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(name);
         if (versionedLib == null) {
             return false;
         }
@@ -10572,6 +10572,7 @@
                     // Set up information for our fall-back user intent resolution activity.
                     mPlatformPackage = pkg;
                     pkg.mVersionCode = mSdkVersion;
+                    pkg.mVersionCodeMajor = 0;
                     mAndroidApplication = pkg.applicationInfo;
                     if (!mResolverReplaced) {
                         mResolveActivity.applicationInfo = mAndroidApplication;
@@ -10613,7 +10614,7 @@
                 // names to allow install of multiple versions, so use name from manifest.
                 if (addSharedLibraryLPw(null, pkg.packageName, pkg.staticSharedLibName,
                         pkg.staticSharedLibVersion, SharedLibraryInfo.TYPE_STATIC,
-                        pkg.manifestPackageName, pkg.mVersionCode)) {
+                        pkg.manifestPackageName, pkg.getLongVersionCode())) {
                     hasStaticSharedLibs = true;
                 } else {
                     Slog.w(TAG, "Package " + pkg.packageName + " library "
@@ -10659,7 +10660,7 @@
                             if (!addSharedLibraryLPw(null, pkg.packageName, name,
                                     SharedLibraryInfo.VERSION_UNDEFINED,
                                     SharedLibraryInfo.TYPE_DYNAMIC,
-                                    pkg.packageName, pkg.mVersionCode)) {
+                                    pkg.packageName, pkg.getLongVersionCode())) {
                                 Slog.w(TAG, "Package " + pkg.packageName + " library "
                                         + name + " already exists; skipping");
                             }
@@ -14654,6 +14655,9 @@
                     verification.putExtra(PackageManager.EXTRA_VERIFICATION_VERSION_CODE,
                             pkgLite.versionCode);
 
+                    verification.putExtra(PackageManager.EXTRA_VERIFICATION_LONG_VERSION_CODE,
+                            pkgLite.getLongVersionCode());
+
                     if (verificationInfo != null) {
                         if (verificationInfo.originatingUri != null) {
                             verification.putExtra(Intent.EXTRA_ORIGINATING_URI,
@@ -16584,7 +16588,8 @@
                     // unless this is the exact same version code which is useful for
                     // development.
                     PackageParser.Package existingPkg = mPackages.get(pkg.packageName);
-                    if (existingPkg != null && existingPkg.mVersionCode != pkg.mVersionCode) {
+                    if (existingPkg != null &&
+                            existingPkg.getLongVersionCode() != pkg.getLongVersionCode()) {
                         res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "
                                 + "static-shared libs cannot be updated");
                         return;
@@ -16891,17 +16896,16 @@
         final boolean canViewInstantApps = canViewInstantApps(callingUid, userId);
         Preconditions.checkNotNull(versionedPackage);
         Preconditions.checkNotNull(observer);
-        Preconditions.checkArgumentInRange(versionedPackage.getVersionCode(),
+        Preconditions.checkArgumentInRange(versionedPackage.getLongVersionCode(),
                 PackageManager.VERSION_CODE_HIGHEST,
-                Integer.MAX_VALUE, "versionCode must be >= -1");
+                Long.MAX_VALUE, "versionCode must be >= -1");
 
         final String packageName = versionedPackage.getPackageName();
-        final int versionCode = versionedPackage.getVersionCode();
+        final long versionCode = versionedPackage.getLongVersionCode();
         final String internalPackageName;
         synchronized (mPackages) {
             // Normalize package name to handle renamed packages and static libs
-            internalPackageName = resolveInternalPackageNameLPr(versionedPackage.getPackageName(),
-                    versionedPackage.getVersionCode());
+            internalPackageName = resolveInternalPackageNameLPr(packageName, versionCode);
         }
 
         final int uid = Binder.getCallingUid();
@@ -17009,24 +17013,24 @@
         return pkg.packageName;
     }
 
-    private String resolveInternalPackageNameLPr(String packageName, int versionCode) {
+    private String resolveInternalPackageNameLPr(String packageName, long versionCode) {
         // Handle renamed packages
         String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
         packageName = normalizedPackageName != null ? normalizedPackageName : packageName;
 
         // Is this a static library?
-        SparseArray<SharedLibraryEntry> versionedLib =
+        LongSparseArray<SharedLibraryEntry> versionedLib =
                 mStaticLibsByDeclaringPackage.get(packageName);
         if (versionedLib == null || versionedLib.size() <= 0) {
             return packageName;
         }
 
         // Figure out which lib versions the caller can see
-        SparseIntArray versionsCallerCanSee = null;
+        LongSparseLongArray versionsCallerCanSee = null;
         final int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
         if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.SHELL_UID
                 && callingAppId != Process.ROOT_UID) {
-            versionsCallerCanSee = new SparseIntArray();
+            versionsCallerCanSee = new LongSparseLongArray();
             String libName = versionedLib.valueAt(0).info.getName();
             String[] uidPackages = getPackagesForUid(Binder.getCallingUid());
             if (uidPackages != null) {
@@ -17034,7 +17038,7 @@
                     PackageSetting ps = mSettings.getPackageLPr(uidPackage);
                     final int libIdx = ArrayUtils.indexOf(ps.usesStaticLibraries, libName);
                     if (libIdx >= 0) {
-                        final int libVersion = ps.usesStaticLibrariesVersions[libIdx];
+                        final long libVersion = ps.usesStaticLibrariesVersions[libIdx];
                         versionsCallerCanSee.append(libVersion, libVersion);
                     }
                 }
@@ -17052,10 +17056,10 @@
         for (int i = 0; i < versionCount; i++) {
             SharedLibraryEntry libEntry = versionedLib.valueAt(i);
             if (versionsCallerCanSee != null && versionsCallerCanSee.indexOfKey(
-                    libEntry.info.getVersion()) < 0) {
+                    libEntry.info.getLongVersion()) < 0) {
                 continue;
             }
-            final int libVersionCode = libEntry.info.getDeclaringPackage().getVersionCode();
+            final long libVersionCode = libEntry.info.getDeclaringPackage().getLongVersionCode();
             if (versionCode != PackageManager.VERSION_CODE_HIGHEST) {
                 if (libVersionCode == versionCode) {
                     return libEntry.apk;
@@ -17063,7 +17067,7 @@
             } else if (highestVersion == null) {
                 highestVersion = libEntry;
             } else if (libVersionCode  > highestVersion.info
-                    .getDeclaringPackage().getVersionCode()) {
+                    .getDeclaringPackage().getLongVersionCode()) {
                 highestVersion = libEntry;
             }
         }
@@ -17192,7 +17196,7 @@
      *  persisting settings for later use
      *  sending a broadcast if necessary
      */
-    int deletePackageX(String packageName, int versionCode, int userId, int deleteFlags) {
+    int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags) {
         final PackageRemovedInfo info = new PackageRemovedInfo(this);
         final boolean res;
 
@@ -17243,7 +17247,7 @@
                         if (!ArrayUtils.isEmpty(libClientPackages)) {
                             Slog.w(TAG, "Not removing package " + pkg.manifestPackageName
                                     + " hosting lib " + libEntry.info.getName() + " version "
-                                    + libEntry.info.getVersion() + " used by " + libClientPackages
+                                    + libEntry.info.getLongVersion() + " used by " + libClientPackages
                                     + " for user " + currUserId);
                             return PackageManager.DELETE_FAILED_USED_SHARED_LIBRARY;
                         }
@@ -20527,7 +20531,8 @@
                 final Iterator<String> it = mSharedLibraries.keySet().iterator();
                 while (it.hasNext()) {
                     String libName = it.next();
-                    SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(libName);
+                    LongSparseArray<SharedLibraryEntry> versionedLib
+                            = mSharedLibraries.get(libName);
                     if (versionedLib == null) {
                         continue;
                     }
@@ -20547,7 +20552,7 @@
                         }
                         pw.print(libEntry.info.getName());
                         if (libEntry.info.isStatic()) {
-                            pw.print(" version=" + libEntry.info.getVersion());
+                            pw.print(" version=" + libEntry.info.getLongVersion());
                         }
                         if (!checkin) {
                             pw.print(" -> ");
@@ -20916,7 +20921,7 @@
         final int count = mSharedLibraries.size();
         for (int i = 0; i < count; i++) {
             final String libName = mSharedLibraries.keyAt(i);
-            SparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(libName);
+            LongSparseArray<SharedLibraryEntry> versionedLib = mSharedLibraries.get(libName);
             if (versionedLib == null) {
                 continue;
             }
@@ -22273,6 +22278,11 @@
         return mInstallerService;
     }
 
+    @Override
+    public IArtManager getArtManager() {
+        return mArtManagerService;
+    }
+
     private boolean userNeedsBadging(int userId) {
         int index = mUserNeedsBadging.indexOfKey(userId);
         if (index < 0) {
@@ -22421,11 +22431,11 @@
      */
     private static void checkDowngrade(PackageParser.Package before, PackageInfoLite after)
             throws PackageManagerException {
-        if (after.versionCode < before.mVersionCode) {
+        if (after.getLongVersionCode() < before.getLongVersionCode()) {
             throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
                     "Update version code " + after.versionCode + " is older than current "
-                    + before.mVersionCode);
-        } else if (after.versionCode == before.mVersionCode) {
+                    + before.getLongVersionCode());
+        } else if (after.getLongVersionCode() == before.getLongVersionCode()) {
             if (after.baseRevisionCode < before.baseRevisionCode) {
                 throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
                         "Update base revision code " + after.baseRevisionCode
@@ -22612,12 +22622,12 @@
         }
 
         @Override
-        public int getVersionCodeForPackage(String packageName) throws RemoteException {
+        public long getVersionCodeForPackage(String packageName) throws RemoteException {
             try {
                 int callingUser = UserHandle.getUserId(Binder.getCallingUid());
                 PackageInfo pInfo = getPackageInfo(packageName, 0, callingUser);
                 if (pInfo != null) {
-                    return pInfo.versionCode;
+                    return pInfo.getLongVersionCode();
                 }
             } catch (Exception e) {
             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 6200444..a7cced7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1245,7 +1245,7 @@
         final PrintWriter pw = getOutPrintWriter();
         int flags = 0;
         int userId = UserHandle.USER_ALL;
-        int versionCode = PackageManager.VERSION_CODE_HIGHEST;
+        long versionCode = PackageManager.VERSION_CODE_HIGHEST;
 
         String opt;
         while ((opt = getNextOption()) != null) {
@@ -1257,7 +1257,7 @@
                     userId = UserHandle.parseUserArg(getNextArgRequired());
                     break;
                 case "--versionCode":
-                    versionCode = Integer.parseInt(getNextArgRequired());
+                    versionCode = Long.parseLong(getNextArgRequired());
                     break;
                 default:
                     pw.println("Error: Unknown option: " + opt);
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 258dd4d4..2b91b7d 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -50,9 +50,9 @@
     PackageSetting(String name, String realName, File codePath, File resourcePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
-            int pVersionCode, int pkgFlags, int privateFlags, String parentPackageName,
+            long pVersionCode, int pkgFlags, int privateFlags, String parentPackageName,
             List<String> childPackageNames, int sharedUserId, String[] usesStaticLibraries,
-            int[] usesStaticLibrariesVersions) {
+            long[] usesStaticLibrariesVersions) {
         super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString,
                 primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
                 pVersionCode, pkgFlags, privateFlags, parentPackageName, childPackageNames,
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index a838768..9733624 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -75,7 +75,7 @@
     String resourcePathString;
 
     String[] usesStaticLibraries;
-    int[] usesStaticLibrariesVersions;
+    long[] usesStaticLibrariesVersions;
 
     /**
      * The path under which native libraries have been unpacked. This path is
@@ -105,7 +105,7 @@
     long timeStamp;
     long firstInstallTime;
     long lastUpdateTime;
-    int versionCode;
+    long versionCode;
 
     boolean uidError;
 
@@ -149,9 +149,9 @@
     PackageSettingBase(String name, String realName, File codePath, File resourcePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
-            int pVersionCode, int pkgFlags, int pkgPrivateFlags,
+            long pVersionCode, int pkgFlags, int pkgPrivateFlags,
             String parentPackageName, List<String> childPackageNames,
-            String[] usesStaticLibraries, int[] usesStaticLibrariesVersions) {
+            String[] usesStaticLibraries, long[] usesStaticLibrariesVersions) {
         super(pkgFlags, pkgPrivateFlags);
         this.name = name;
         this.realName = realName;
@@ -180,7 +180,7 @@
 
     void init(File codePath, File resourcePath, String legacyNativeLibraryPathString,
               String primaryCpuAbiString, String secondaryCpuAbiString,
-              String cpuAbiOverrideString, int pVersionCode) {
+              String cpuAbiOverrideString, long pVersionCode) {
         this.codePath = codePath;
         this.codePathString = codePath.toString();
         this.resourcePath = resourcePath;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index af1a4d1..4a5772f 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -593,10 +593,10 @@
 
     PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
-            String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, int vc, int
+            String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int
             pkgFlags, int pkgPrivateFlags, String parentPackageName,
             List<String> childPackageNames, String[] usesStaticLibraries,
-            int[] usesStaticLibraryNames) {
+            long[] usesStaticLibraryNames) {
         PackageSetting p = mPackages.get(name);
         if (p != null) {
             if (p.appId == uid) {
@@ -673,11 +673,11 @@
     static @NonNull PackageSetting createNewSetting(String pkgName, PackageSetting originalPkg,
             PackageSetting disabledPkg, String realPkgName, SharedUserSetting sharedUser,
             File codePath, File resourcePath, String legacyNativeLibraryPath, String primaryCpuAbi,
-            String secondaryCpuAbi, int versionCode, int pkgFlags, int pkgPrivateFlags,
+            String secondaryCpuAbi, long versionCode, int pkgFlags, int pkgPrivateFlags,
             UserHandle installUser, boolean allowInstall, boolean instantApp,
             boolean virtualPreload, String parentPkgName, List<String> childPkgNames,
             UserManagerService userManager,
-            String[] usesStaticLibraries, int[] usesStaticLibrariesVersions) {
+            String[] usesStaticLibraries, long[] usesStaticLibrariesVersions) {
         final PackageSetting pkgSetting;
         if (originalPkg != null) {
             if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
@@ -788,7 +788,7 @@
             @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi,
             int pkgFlags, int pkgPrivateFlags, @Nullable List<String> childPkgNames,
             @NonNull UserManagerService userManager, @Nullable String[] usesStaticLibraries,
-            @Nullable int[] usesStaticLibrariesVersions) throws PackageManagerException {
+            @Nullable long[] usesStaticLibrariesVersions) throws PackageManagerException {
         final String pkgName = pkgSetting.name;
         if (pkgSetting.sharedUser != sharedUser) {
             PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -951,8 +951,8 @@
         p.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
         p.cpuAbiOverrideString = pkg.cpuAbiOverride;
         // Update version code if needed
-        if (pkg.mVersionCode != p.versionCode) {
-            p.versionCode = pkg.mVersionCode;
+        if (pkg.getLongVersionCode() != p.versionCode) {
+            p.versionCode = pkg.getLongVersionCode();
         }
         // Update signatures if needed.
         if (p.signatures.mSignatures == null) {
@@ -2289,9 +2289,9 @@
             String libName = parser.getAttributeValue(null, ATTR_NAME);
             String libVersionStr = parser.getAttributeValue(null, ATTR_VERSION);
 
-            int libVersion = -1;
+            long libVersion = -1;
             try {
-                libVersion = Integer.parseInt(libVersionStr);
+                libVersion = Long.parseLong(libVersionStr);
             } catch (NumberFormatException e) {
                 // ignore
             }
@@ -2299,7 +2299,7 @@
             if (libName != null && libVersion >= 0) {
                 outPs.usesStaticLibraries = ArrayUtils.appendElement(String.class,
                         outPs.usesStaticLibraries, libName);
-                outPs.usesStaticLibrariesVersions = ArrayUtils.appendInt(
+                outPs.usesStaticLibrariesVersions = ArrayUtils.appendLong(
                         outPs.usesStaticLibrariesVersions, libVersion);
             }
 
@@ -2308,7 +2308,7 @@
     }
 
     void writeUsesStaticLibLPw(XmlSerializer serializer, String[] usesStaticLibraries,
-            int[] usesStaticLibraryVersions) throws IOException {
+            long[] usesStaticLibraryVersions) throws IOException {
         if (ArrayUtils.isEmpty(usesStaticLibraries) || ArrayUtils.isEmpty(usesStaticLibraryVersions)
                 || usesStaticLibraries.length != usesStaticLibraryVersions.length) {
             return;
@@ -2316,10 +2316,10 @@
         final int libCount = usesStaticLibraries.length;
         for (int i = 0; i < libCount; i++) {
             final String libName = usesStaticLibraries[i];
-            final int libVersion = usesStaticLibraryVersions[i];
+            final long libVersion = usesStaticLibraryVersions[i];
             serializer.startTag(null, TAG_USES_STATIC_LIB);
             serializer.attribute(null, ATTR_NAME, libName);
-            serializer.attribute(null, ATTR_VERSION, Integer.toString(libVersion));
+            serializer.attribute(null, ATTR_VERSION, Long.toString(libVersion));
             serializer.endTag(null, TAG_USES_STATIC_LIB);
         }
     }
@@ -3563,10 +3563,10 @@
             resourcePathStr = codePathStr;
         }
         String version = parser.getAttributeValue(null, "version");
-        int versionCode = 0;
+        long versionCode = 0;
         if (version != null) {
             try {
-                versionCode = Integer.parseInt(version);
+                versionCode = Long.parseLong(version);
             } catch (NumberFormatException e) {
             }
         }
@@ -3679,7 +3679,7 @@
         long lastUpdateTime = 0;
         PackageSetting packageSetting = null;
         String version = null;
-        int versionCode = 0;
+        long versionCode = 0;
         String parentPackageName;
         try {
             name = parser.getAttributeValue(null, ATTR_NAME);
@@ -3707,7 +3707,7 @@
             version = parser.getAttributeValue(null, "version");
             if (version != null) {
                 try {
-                    versionCode = Integer.parseInt(version);
+                    versionCode = Long.parseLong(version);
                 } catch (NumberFormatException e) {
                 }
             }
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index ba97c42..7bab318 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -725,7 +725,7 @@
                 // This means if a system app's version code doesn't change on an OTA,
                 // we don't notice it's updated.  But that's fine since their version code *should*
                 // really change on OTAs.
-                if ((getPackageInfo().getVersionCode() == pi.versionCode)
+                if ((getPackageInfo().getVersionCode() == pi.getLongVersionCode())
                         && (getPackageInfo().getLastUpdateTime() == pi.lastUpdateTime)
                         && areAllActivitiesStillEnabled()) {
                     return false;
@@ -759,11 +759,11 @@
         if (ShortcutService.DEBUG) {
             Slog.d(TAG, String.format("Package %s %s, version %d -> %d", getPackageName(),
                     (isNewApp ? "added" : "updated"),
-                    getPackageInfo().getVersionCode(), pi.versionCode));
+                    getPackageInfo().getVersionCode(), pi.getLongVersionCode()));
         }
 
         getPackageInfo().updateFromPackageInfo(pi);
-        final int newVersionCode = getPackageInfo().getVersionCode();
+        final long newVersionCode = getPackageInfo().getVersionCode();
 
         // See if there are any shortcuts that were prevented restoring because the app was of a
         // lower version, and re-enable them.
@@ -1412,7 +1412,7 @@
             ShortcutService.writeAttr(out, ATTR_FLAGS, flags);
 
             // Set the publisher version code at every backup.
-            final int packageVersionCode = getPackageInfo().getVersionCode();
+            final long packageVersionCode = getPackageInfo().getVersionCode();
             if (packageVersionCode == 0) {
                 s.wtf("Package version code should be available at this point.");
                 // However, 0 is a valid version code, so we just go ahead with it...
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
index 3a9bbc8..b14e9c9 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -59,8 +59,8 @@
      * been installed yet.
      */
     private boolean mIsShadow;
-    private int mVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
-    private int mBackupSourceVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
+    private long mVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
+    private long mBackupSourceVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
     private long mLastUpdateTime;
     private ArrayList<byte[]> mSigHashes;
 
@@ -73,7 +73,7 @@
     private boolean mBackupAllowed;
     private boolean mBackupSourceBackupAllowed;
 
-    private ShortcutPackageInfo(int versionCode, long lastUpdateTime,
+    private ShortcutPackageInfo(long versionCode, long lastUpdateTime,
             ArrayList<byte[]> sigHashes, boolean isShadow) {
         mVersionCode = versionCode;
         mLastUpdateTime = lastUpdateTime;
@@ -96,11 +96,11 @@
         mIsShadow = shadow;
     }
 
-    public int getVersionCode() {
+    public long getVersionCode() {
         return mVersionCode;
     }
 
-    public int getBackupSourceVersionCode() {
+    public long getBackupSourceVersionCode() {
         return mBackupSourceVersionCode;
     }
 
@@ -123,7 +123,7 @@
      */
     public void updateFromPackageInfo(@NonNull PackageInfo pi) {
         if (pi != null) {
-            mVersionCode = pi.versionCode;
+            mVersionCode = pi.getLongVersionCode();
             mLastUpdateTime = pi.lastUpdateTime;
             mBackupAllowed = ShortcutService.shouldBackupApp(pi);
             mBackupAllowedInitialized = true;
@@ -145,7 +145,7 @@
             Slog.w(TAG, "Can't restore: package didn't or doesn't allow backup");
             return ShortcutInfo.DISABLED_REASON_BACKUP_NOT_SUPPORTED;
         }
-        if (!anyVersionOkay && (currentPackage.versionCode < mBackupSourceVersionCode)) {
+        if (!anyVersionOkay && (currentPackage.getLongVersionCode() < mBackupSourceVersionCode)) {
             Slog.w(TAG, String.format(
                     "Can't restore: package current version %d < backed up version %d",
                     currentPackage.versionCode, mBackupSourceVersionCode));
@@ -162,11 +162,12 @@
             Slog.e(TAG, "Can't get signatures: package=" + packageName);
             return null;
         }
-        final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.versionCode, pi.lastUpdateTime,
-                BackupUtils.hashSignatureArray(pi.signatures), /* shadow=*/ false);
+        final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.getLongVersionCode(),
+                pi.lastUpdateTime, BackupUtils.hashSignatureArray(pi.signatures),
+                /* shadow=*/ false);
 
         ret.mBackupSourceBackupAllowed = s.shouldBackupApp(pi);
-        ret.mBackupSourceVersionCode = pi.versionCode;
+        ret.mBackupSourceVersionCode = pi.getLongVersionCode();
         return ret;
     }
 
@@ -213,7 +214,7 @@
             throws IOException, XmlPullParserException {
 
         // Don't use the version code from the backup file.
-        final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION,
+        final long versionCode = ShortcutService.parseLongAttribute(parser, ATTR_VERSION,
                 ShortcutInfo.VERSION_CODE_UNKNOWN);
 
         final long lastUpdateTime = ShortcutService.parseLongAttribute(
@@ -225,7 +226,7 @@
 
         // We didn't used to save these attributes, and all backed up shortcuts were from
         // apps that support backups, so the default values take this fact into consideration.
-        final int backupSourceVersion = ShortcutService.parseIntAttribute(parser,
+        final long backupSourceVersion = ShortcutService.parseLongAttribute(parser,
                 ATTR_BACKUP_SOURCE_VERSION, ShortcutInfo.VERSION_CODE_UNKNOWN);
 
         // Note the only time these "true" default value is used is when restoring from an old
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 689099c..0629d9e 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -108,7 +108,7 @@
             return; // Not installed, no need to restore yet.
         }
         int restoreBlockReason;
-        int currentVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
+        long currentVersionCode = ShortcutInfo.VERSION_CODE_UNKNOWN;
 
         if (!mPackageInfo.hasSignatures()) {
             s.wtf("Attempted to restore package " + mPackageName + "/u" + mPackageUserId
@@ -116,7 +116,7 @@
             restoreBlockReason = ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH;
         } else {
             final PackageInfo pi = s.getPackageInfoWithSignatures(mPackageName, mPackageUserId);
-            currentVersionCode = pi.versionCode;
+            currentVersionCode = pi.getLongVersionCode();
             restoreBlockReason = mPackageInfo.canRestoreTo(s, pi, canRestoreAnyVersion());
         }
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index dbf413f..dc481ca 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -27,7 +27,6 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerNative;
-import android.app.AppGlobals;
 import android.app.IActivityManager;
 import android.app.IStopUserCallback;
 import android.app.KeyguardManager;
@@ -50,6 +49,7 @@
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IProgressListener;
 import android.os.IUserManager;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
@@ -71,10 +71,6 @@
 import android.os.storage.StorageManager;
 import android.security.GateKeeper;
 import android.service.gatekeeper.IGateKeeperService;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.text.TextUtils;
 import android.util.AtomicFile;
 import android.util.IntArray;
 import android.util.Log;
@@ -113,9 +109,9 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
@@ -378,22 +374,45 @@
     private final BroadcastReceiver mDisableQuietModeCallback = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK.equals(intent.getAction())) {
-                final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT);
-                final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_ID, 0);
-                setQuietModeEnabled(userHandle, false);
-                if (target != null) {
-                    try {
-                        mContext.startIntentSender(target, null, 0, 0, 0);
-                    } catch (IntentSender.SendIntentException e) {
-                        /* ignore */
-                    }
-                }
+            if (!ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK.equals(intent.getAction())) {
+                return;
             }
+            final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT);
+            final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL);
+            setQuietModeEnabled(userHandle, false, target);
         }
     };
 
     /**
+     * Start an {@link IntentSender} when user is unlocked after disabling quiet mode.
+     *
+     * @see {@link #trySetQuietModeDisabled(int, IntentSender)}
+     */
+    private class DisableQuietModeUserUnlockedCallback extends IProgressListener.Stub {
+        private final IntentSender mTarget;
+
+        public DisableQuietModeUserUnlockedCallback(IntentSender target) {
+            Preconditions.checkNotNull(target);
+            mTarget = target;
+        }
+
+        @Override
+        public void onStarted(int id, Bundle extras) {}
+
+        @Override
+        public void onProgress(int id, int progress, Bundle extras) {}
+
+        @Override
+        public void onFinished(int id, Bundle extras) {
+            try {
+                mContext.startIntentSender(mTarget, null, 0, 0, 0);
+            } catch (IntentSender.SendIntentException e) {
+                Slog.e(LOG_TAG, "Failed to start the target in the callback", e);
+            }
+        }
+    }
+
+    /**
      * Whether all users should be created ephemeral.
      */
     @GuardedBy("mUsersLock")
@@ -765,7 +784,7 @@
     }
 
     @Override
-    public void setQuietModeEnabled(int userHandle, boolean enableQuietMode) {
+    public void setQuietModeEnabled(int userHandle, boolean enableQuietMode, IntentSender target) {
         checkManageUsersPermission("silence profile");
         boolean changed = false;
         UserInfo profile, parent;
@@ -792,7 +811,11 @@
                     LocalServices.getService(ActivityManagerInternal.class)
                             .killForegroundAppsForUser(userHandle);
                 } else {
-                    ActivityManager.getService().startUserInBackground(userHandle);
+                    IProgressListener callback = target != null
+                            ? new DisableQuietModeUserUnlockedCallback(target)
+                            : null;
+                    ActivityManager.getService().startUserInBackgroundWithListener(
+                            userHandle, callback);
                 }
             } catch (RemoteException e) {
                 Slog.e(LOG_TAG, "fail to start/stop user for quiet mode", e);
@@ -820,12 +843,13 @@
     }
 
     @Override
-    public boolean trySetQuietModeDisabled(int userHandle, IntentSender target) {
+    public boolean trySetQuietModeDisabled(
+            @UserIdInt int userHandle, @Nullable IntentSender target) {
         checkManageUsersPermission("silence profile");
         if (StorageManager.isUserKeyUnlocked(userHandle)
                 || !mLockPatternUtils.isSecure(userHandle)) {
             // if the user is already unlocked, no need to show a profile challenge
-            setQuietModeEnabled(userHandle, false);
+            setQuietModeEnabled(userHandle, false, target);
             return true;
         }
 
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
new file mode 100644
index 0000000..2dbb34d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -0,0 +1,233 @@
+/*
+ * 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.server.pm.dex;
+
+import android.Manifest;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.dex.ArtManager;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.content.pm.IPackageManager;
+import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
+import com.android.server.pm.Installer;
+import com.android.server.pm.Installer.InstallerException;
+import java.io.File;
+import java.io.FileNotFoundException;
+
+/**
+ * A system service that provides access to runtime and compiler artifacts.
+ *
+ * This service is not accessed by users directly, instead one uses an instance of
+ * {@link ArtManager}, which can be accessed via {@link PackageManager} as follows:
+ * <p/>
+ * {@code context().getPackageManager().getArtManager();}
+ * <p class="note">
+ * Note: Accessing runtime artifacts may require extra permissions. For example querying the
+ * runtime profiles of apps requires {@link android.Manifest.permission#READ_RUNTIME_PROFILES}
+ * which is a system-level permission that will not be granted to normal apps.
+ */
+public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
+    private static final String TAG = "ArtManagerService";
+
+    private static boolean DEBUG = false;
+    private static boolean DEBUG_IGNORE_PERMISSIONS = false;
+
+    private final IPackageManager mPackageManager;
+    private final Object mInstallLock;
+    @GuardedBy("mInstallLock")
+    private final Installer mInstaller;
+
+    private final Handler mHandler;
+
+    public ArtManagerService(IPackageManager pm, Installer installer, Object installLock) {
+        mPackageManager = pm;
+        mInstaller = installer;
+        mInstallLock = installLock;
+        mHandler = new Handler(BackgroundThread.getHandler().getLooper());
+    }
+
+    @Override
+    public void snapshotRuntimeProfile(String packageName, String codePath,
+            ISnapshotRuntimeProfileCallback callback) {
+        // Sanity checks on the arguments.
+        Preconditions.checkStringNotEmpty(packageName);
+        Preconditions.checkStringNotEmpty(codePath);
+        Preconditions.checkNotNull(callback);
+
+        // Verify that the caller has the right permissions.
+        checkReadRuntimeProfilePermission();
+
+        if (DEBUG) {
+            Slog.d(TAG, "Requested snapshot for " + packageName + ":" + codePath);
+        }
+
+        PackageInfo info = null;
+        try {
+            // Note that we use the default user 0 to retrieve the package info.
+            // This doesn't really matter because for user 0 we always get a package back (even if
+            // it's not installed for the user 0). It is ok because we only care about the code
+            // paths and not if the package is enabled or not for the user.
+
+            // TODO(calin): consider adding an API to PMS which can retrieve the
+            // PackageParser.Package.
+            info = mPackageManager.getPackageInfo(packageName, /*flags*/ 0, /*userId*/ 0);
+        } catch (RemoteException ignored) {
+            // Should not happen.
+        }
+        if (info == null) {
+            postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_PACKAGE_NOT_FOUND);
+            return;
+        }
+
+        boolean pathFound = info.applicationInfo.getBaseCodePath().equals(codePath);
+        String[] splitCodePaths = info.applicationInfo.getSplitCodePaths();
+        if (!pathFound && (splitCodePaths != null)) {
+            for (String path : splitCodePaths) {
+                if (path.equals(codePath)) {
+                    pathFound = true;
+                    break;
+                }
+            }
+        }
+        if (!pathFound) {
+            postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND);
+            return;
+        }
+
+        // All good, create the profile snapshot.
+        createProfileSnapshot(packageName, codePath, callback, info);
+        // Destroy the snapshot, we no longer need it.
+        destroyProfileSnapshot(packageName, codePath);
+    }
+
+    private void createProfileSnapshot(String packageName, String codePath,
+            ISnapshotRuntimeProfileCallback callback, PackageInfo info) {
+        // Ask the installer to snapshot the profile.
+        synchronized (mInstallLock) {
+            try {
+                if (!mInstaller.createProfileSnapshot(UserHandle.getAppId(info.applicationInfo.uid),
+                        packageName, codePath)) {
+                    postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+                    return;
+                }
+            } catch (InstallerException e) {
+                postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+                return;
+            }
+        }
+
+        // Open the snapshot and invoke the callback.
+        File snapshotProfile = Environment.getProfileSnapshotPath(packageName, codePath);
+        ParcelFileDescriptor fd;
+        try {
+            fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY);
+            postSuccess(packageName, fd, callback);
+        } catch (FileNotFoundException e) {
+            Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":" + codePath, e);
+            postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+        }
+    }
+
+    private void destroyProfileSnapshot(String packageName, String codePath) {
+        if (DEBUG) {
+            Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + codePath);
+        }
+
+        synchronized (mInstallLock) {
+            try {
+                mInstaller.destroyProfileSnapshot(packageName, codePath);
+            } catch (InstallerException e) {
+                Slog.e(TAG, "Failed to destroy profile snapshot for " +
+                    packageName + ":" + codePath, e);
+            }
+        }
+    }
+
+    @Override
+    public boolean isRuntimeProfilingEnabled() {
+        // Verify that the caller has the right permissions.
+        checkReadRuntimeProfilePermission();
+
+        return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
+    }
+
+    /**
+     * Post {@link ISnapshotRuntimeProfileCallback#onError(int)} with the given error message
+     * on the internal {@code mHandler}.
+     */
+    private void postError(ISnapshotRuntimeProfileCallback callback, String packageName,
+            int errCode) {
+        if (DEBUG) {
+            Slog.d(TAG, "Failed to snapshot profile for " + packageName + " with error: " +
+                    errCode);
+        }
+        mHandler.post(() -> {
+            try {
+                callback.onError(errCode);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e);
+            }
+        });
+    }
+
+    private void postSuccess(String packageName, ParcelFileDescriptor fd,
+            ISnapshotRuntimeProfileCallback callback) {
+        if (DEBUG) {
+            Slog.d(TAG, "Successfully snapshot profile for " + packageName);
+        }
+        mHandler.post(() -> {
+            try {
+                callback.onSuccess(fd);
+            } catch (RemoteException e) {
+                Slog.w(TAG,
+                        "Failed to call onSuccess after profile snapshot for " + packageName, e);
+            }
+        });
+    }
+
+    /**
+     * Verify that the binder calling uid has {@code android.permission.READ_RUNTIME_PROFILE}.
+     * If not, it throws a {@link SecurityException}.
+     */
+    private void checkReadRuntimeProfilePermission() {
+        if (DEBUG_IGNORE_PERMISSIONS) {
+            return;
+        }
+        try {
+            int result = mPackageManager.checkUidPermission(
+                    Manifest.permission.READ_RUNTIME_PROFILES, Binder.getCallingUid());
+            if (result != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("You need "
+                        + Manifest.permission.READ_RUNTIME_PROFILES
+                        + " permission to snapshot profiles.");
+            }
+        } catch (RemoteException e) {
+            // Should not happen.
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7415ec3..9a7e72a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -211,6 +211,7 @@
 import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.IApplicationToken;
@@ -239,6 +240,7 @@
 import android.view.inputmethod.InputMethodManagerInternal;
 
 import com.android.internal.R;
+import com.android.internal.accessibility.AccessibilityShortcutController;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
@@ -246,6 +248,7 @@
 import com.android.internal.policy.IShortcutService;
 import com.android.internal.policy.PhoneWindow;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ScreenShapeHelper;
 import com.android.internal.widget.PointerLocationView;
 import com.android.server.GestureLauncherService;
@@ -3379,7 +3382,7 @@
     }
 
     boolean keyguardOn() {
-        return isKeyguardShowingAndNotOccluded() || inKeyguardRestrictedKeyInputMode();
+        return isKeyguardShowingAndNotOccluded();
     }
 
     private static final int[] WINDOW_TYPES_WHERE_HOME_DOESNT_WORK = {
@@ -4335,7 +4338,7 @@
     // TODO: Should probably be moved into DisplayFrames.
     public boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
             DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
-            Rect outOutsets) {
+            Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) {
         final int fl = PolicyControl.getWindowFlags(null, attrs);
         final int sysuiVis = PolicyControl.getSystemUiVisibility(null, attrs);
         final int systemUiVisibility = (sysuiVis | attrs.subtreeSystemUiVisibility);
@@ -4361,12 +4364,15 @@
 
         if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR))
                 == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
+            Rect frame;
             int availRight, availBottom;
             if (canHideNavigationBar() &&
                     (systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0) {
+                frame = displayFrames.mUnrestricted;
                 availRight = displayFrames.mUnrestricted.right;
                 availBottom = displayFrames.mUnrestricted.bottom;
             } else {
+                frame = displayFrames.mRestricted;
                 availRight = displayFrames.mRestricted.right;
                 availBottom = displayFrames.mRestricted.bottom;
             }
@@ -4397,10 +4403,12 @@
                 calculateRelevantTaskInsets(taskBounds, outStableInsets,
                         displayWidth, displayHeight);
             }
+            outDisplayCutout.set(displayFrames.mDisplayCutout.calculateRelativeTo(frame));
             return mForceShowSystemBars;
         }
         outContentInsets.setEmpty();
         outStableInsets.setEmpty();
+        outDisplayCutout.set(DisplayCutout.NO_CUTOUT);
         return mForceShowSystemBars;
     }
 
@@ -4519,7 +4527,7 @@
 
             w.computeFrameLw(pf /* parentFrame */, df /* displayFrame */, df /* overlayFrame */,
                     df /* contentFrame */, df /* visibleFrame */, dcf /* decorFrame */,
-                    df /* stableFrame */, df /* outsetFrame */);
+                    df /* stableFrame */, df /* outsetFrame */, displayFrames.mDisplayCutout);
             final Rect frame = w.getFrameLw();
 
             if (frame.left <= 0 && frame.top <= 0) {
@@ -4581,7 +4589,8 @@
         // Let the status bar determine its size.
         mStatusBar.computeFrameLw(pf /* parentFrame */, df /* displayFrame */,
                 vf /* overlayFrame */, vf /* contentFrame */, vf /* visibleFrame */,
-                dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */);
+                dcf /* decorFrame */, vf /* stableFrame */, vf /* outsetFrame */,
+                displayFrames.mDisplayCutout);
 
         // For layout, the status bar is always at the top with our fixed height.
         displayFrames.mStable.top = displayFrames.mUnrestricted.top + mStatusBarHeight;
@@ -4712,7 +4721,7 @@
         // And compute the final frame.
         mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
                 mTmpNavigationFrame, mTmpNavigationFrame, mTmpNavigationFrame, dcf,
-                mTmpNavigationFrame, mTmpNavigationFrame);
+                mTmpNavigationFrame, mTmpNavigationFrame, displayFrames.mDisplayCutout);
         if (DEBUG_LAYOUT) Slog.i(TAG, "mNavigationBar frame: " + mTmpNavigationFrame);
         return mNavigationBarController.checkHiddenLw();
     }
@@ -5218,7 +5227,7 @@
                 + " sf=" + sf.toShortString()
                 + " osf=" + (osf == null ? "null" : osf.toShortString()));
 
-        win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf);
+        win.computeFrameLw(pf, df, of, cf, vf, dcf, sf, osf, displayFrames.mDisplayCutout);
 
         // Dock windows carve out the bottom of the screen, so normal windows
         // can't appear underneath them.
@@ -6870,13 +6879,6 @@
         return mKeyguardOccluded;
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public boolean inKeyguardRestrictedKeyInputMode() {
-        if (mKeyguardDelegate == null) return false;
-        return mKeyguardDelegate.isInputRestricted();
-    }
-
     @Override
     public void dismissKeyguardLw(IKeyguardDismissCallback callback) {
         if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
@@ -7213,15 +7215,7 @@
     }
 
     static long[] getLongIntArray(Resources r, int resid) {
-        int[] ar = r.getIntArray(resid);
-        if (ar == null) {
-            return null;
-        }
-        long[] out = new long[ar.length];
-        for (int i=0; i<ar.length; i++) {
-            out[i] = ar[i];
-        }
-        return out;
+        return ArrayUtils.convertToLongArray(r.getIntArray(resid));
     }
 
     private void bindKeyguard() {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 5f067d4..bcb7212 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -77,6 +77,7 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.IApplicationToken;
 import android.view.IWindowManager;
 import android.view.InputEventReceiver;
@@ -210,10 +211,11 @@
          * @param stableFrame The frame around which stable system decoration is positioned.
          * @param outsetFrame The frame that includes areas that aren't part of the surface but we
          * want to treat them as such.
+         * @param displayCutout the display displayCutout
          */
         public void computeFrameLw(Rect parentFrame, Rect displayFrame,
                 Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame,
-                Rect stableFrame, @Nullable Rect outsetFrame);
+                Rect stableFrame, @Nullable Rect outsetFrame, DisplayCutout displayCutout);
 
         /**
          * Retrieve the current frame of the window that has been assigned by
@@ -1144,12 +1146,13 @@
      * @param outStableInsets The areas covered by stable system windows irrespective of their
      *                        current visibility. Expressed as positive insets.
      * @param outOutsets The areas that are not real display, but we would like to treat as such.
+     * @param outDisplayCutout The area that has been cut away from the display.
      * @return Whether to always consume the navigation bar.
      *         See {@link #isNavBarForcedShownLw(WindowState)}.
      */
     default boolean getInsetHintLw(WindowManager.LayoutParams attrs, Rect taskBounds,
             DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
-            Rect outOutsets) {
+            Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) {
         return false;
     }
 
@@ -1357,17 +1360,6 @@
     public boolean isKeyguardTrustedLw();
 
     /**
-     * inKeyguardRestrictedKeyInputMode
-     *
-     * if keyguard screen is showing or in restricted key input mode (i.e. in
-     * keyguard password emergency screen). When in such mode, certain keys,
-     * such as the Home key and the right soft keys, don't work.
-     *
-     * @return true if in keyguard restricted input mode.
-     */
-    public boolean inKeyguardRestrictedKeyInputMode();
-
-    /**
      * Ask the policy to dismiss the keyguard, if it is currently shown.
      *
      * @param callback Callback to be informed about the result.
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 3810192..6f005a3 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -49,11 +49,21 @@
 
     public static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE.
 
-    // Value of batterySaverGpsMode such that GPS isn't affected by battery saver mode.
+    /** Value of batterySaverGpsMode such that GPS isn't affected by battery saver mode. */
     public static final int GPS_MODE_NO_CHANGE = 0;
-    // Value of batterySaverGpsMode such that GPS is disabled when battery saver mode
-    // is enabled and the screen is off.
+
+    /**
+     * Value of batterySaverGpsMode such that GPS is disabled when battery saver mode
+     * is enabled and the screen is off.
+     */
     public static final int GPS_MODE_DISABLED_WHEN_SCREEN_OFF = 1;
+
+    /**
+     * Value of batterySaverGpsMode such that location should be disabled altogether
+     * when battery saver mode is enabled and the screen is off.
+     */
+    public static final int GPS_MODE_ALL_DISABLED_WHEN_SCREEN_OFF = 2;
+
     // Secure setting for GPS behavior when battery saver mode is on.
     public static final String SECURE_KEY_GPS_MODE = "batterySaverGpsMode";
 
@@ -329,7 +339,7 @@
         }
 
         mVibrationDisabled = parser.getBoolean(KEY_VIBRATION_DISABLED, true);
-        mAnimationDisabled = parser.getBoolean(KEY_ANIMATION_DISABLED, true);
+        mAnimationDisabled = parser.getBoolean(KEY_ANIMATION_DISABLED, false);
         mSoundTriggerDisabled = parser.getBoolean(KEY_SOUNDTRIGGER_DISABLED, true);
         mFullBackupDeferred = parser.getBoolean(KEY_FULLBACKUP_DEFERRED, true);
         mKeyValueBackupDeferred = parser.getBoolean(KEY_KEYVALUE_DEFERRED, true);
@@ -344,7 +354,7 @@
 
         // Get default value from Settings.Secure
         final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE,
-                GPS_MODE_NO_CHANGE);
+                GPS_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
         mGpsMode = parser.getInt(KEY_GPS_MODE, defaultGpsMode);
 
         // Non-device-specific parameters.
@@ -446,6 +456,12 @@
         }
     }
 
+    public int getGpsMode() {
+        synchronized (mLock) {
+            return mGpsMode;
+        }
+    }
+
     public ArrayMap<String, String> getFileValues(boolean interactive) {
         synchronized (mLock) {
             return interactive ? mFilesForInteractive : mFilesForNoninteractive;
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index a6bca0b..d4627c2 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -84,6 +84,23 @@
      */
     private boolean mPreviouslyEnabled;
 
+    @GuardedBy("mLock")
+    private boolean mIsInteractive;
+
+    /**
+     * Read-only list of plugins. No need for synchronization.
+     */
+    private final Plugin[] mPlugins;
+
+    /**
+     * Plugin interface. All methods are guaranteed to be called on the same (handler) thread.
+     */
+    public interface Plugin {
+        void onSystemReady(BatterySaverController caller);
+
+        void onBatterySaverChanged(BatterySaverController caller);
+    }
+
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -109,6 +126,12 @@
         mBatterySaverPolicy = policy;
         mBatterySaverPolicy.addListener(this);
         mFileUpdater = new FileUpdater(context);
+
+        // Initialize plugins.
+        final ArrayList<Plugin> plugins = new ArrayList<>();
+        plugins.add(new BatterySaverLocationPlugin(mContext));
+
+        mPlugins = plugins.toArray(new Plugin[plugins.size()]);
     }
 
     /**
@@ -121,7 +144,7 @@
     }
 
     /**
-     * Called by {@link PowerManagerService} on system ready..
+     * Called by {@link PowerManagerService} on system ready.
      */
     public void systemReady() {
         final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
@@ -130,6 +153,7 @@
 
         mFileUpdater.systemReady(LocalServices.getService(ActivityManagerInternal.class)
                 .isRuntimeRestarted());
+        mHandler.postSystemReady();
     }
 
     private PowerManager getPowerManager() {
@@ -154,6 +178,8 @@
         private static final int ARG_DONT_SEND_BROADCAST = 0;
         private static final int ARG_SEND_BROADCAST = 1;
 
+        private static final int MSG_SYSTEM_READY = 2;
+
         public MyHandler(Looper looper) {
             super(looper);
         }
@@ -163,12 +189,22 @@
                     ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, 0).sendToTarget();
         }
 
+        public void postSystemReady() {
+            obtainMessage(MSG_SYSTEM_READY, 0, 0).sendToTarget();
+        }
+
         @Override
         public void dispatchMessage(Message msg) {
             switch (msg.what) {
                 case MSG_STATE_CHANGED:
                     handleBatterySaverStateChanged(msg.arg1 == ARG_SEND_BROADCAST);
                     break;
+
+                case MSG_SYSTEM_READY:
+                    for (Plugin p : mPlugins) {
+                        p.onSystemReady(BatterySaverController.this);
+                    }
+                    break;
             }
         }
     }
@@ -188,12 +224,24 @@
     }
 
     /** @return whether battery saver is enabled or not. */
-    boolean isEnabled() {
+    public boolean isEnabled() {
         synchronized (mLock) {
             return mEnabled;
         }
     }
 
+    /** @return whether device is in interactive state. */
+    public boolean isInteractive() {
+        synchronized (mLock) {
+            return mIsInteractive;
+        }
+    }
+
+    /** @return Battery saver policy. */
+    public BatterySaverPolicy getBatterySaverPolicy() {
+        return mBatterySaverPolicy;
+    }
+
     /**
      * @return true if launch boost should currently be disabled.
      */
@@ -230,6 +278,7 @@
             listeners = mListeners.toArray(new LowPowerModeListener[mListeners.size()]);
 
             enabled = mEnabled;
+            mIsInteractive = isInteractive;
 
 
             if (enabled) {
@@ -250,6 +299,10 @@
             mFileUpdater.writeFiles(fileValues);
         }
 
+        for (Plugin p : mPlugins) {
+            p.onBatterySaverChanged(this);
+        }
+
         if (sendBroadcast) {
             if (enabled) {
                 // STOPSHIP Remove the toast.
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java
new file mode 100644
index 0000000..0af19b6
--- /dev/null
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java
@@ -0,0 +1,65 @@
+/*
+ * 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.server.power.batterysaver;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.util.Slog;
+
+import com.android.server.power.BatterySaverPolicy;
+import com.android.server.power.batterysaver.BatterySaverController.Plugin;
+
+public class BatterySaverLocationPlugin implements Plugin {
+    private static final String TAG = "BatterySaverLocationPlugin";
+
+    private static final boolean DEBUG = BatterySaverController.DEBUG;
+
+    private final Context mContext;
+
+    public BatterySaverLocationPlugin(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public void onBatterySaverChanged(BatterySaverController caller) {
+        if (DEBUG) {
+            Slog.d(TAG, "onBatterySaverChanged");
+        }
+        updateLocationState(caller);
+    }
+
+    @Override
+    public void onSystemReady(BatterySaverController caller) {
+        if (DEBUG) {
+            Slog.d(TAG, "onSystemReady");
+        }
+        updateLocationState(caller);
+    }
+
+    private void updateLocationState(BatterySaverController caller) {
+        final boolean kill =
+                (caller.getBatterySaverPolicy().getGpsMode()
+                        == BatterySaverPolicy.GPS_MODE_ALL_DISABLED_WHEN_SCREEN_OFF) &&
+                caller.isEnabled() && !caller.isInteractive();
+
+        if (DEBUG) {
+            Slog.d(TAG, "Battery saver " + (kill ? "stopping" : "restoring") + " location.");
+        }
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Global.LOCATION_GLOBAL_KILL_SWITCH, kill ? 1 : 0);
+    }
+}
diff --git a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java
index ab9ab67..a8c68c0 100644
--- a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java
+++ b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java
@@ -63,7 +63,7 @@
                 PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageNames[i],
                         PackageManager.GET_SIGNATURES, userId);
                 keyAttestationPackageInfos[i] = new KeyAttestationPackageInfo(packageNames[i],
-                        packageInfo.versionCode, packageInfo.signatures);
+                        packageInfo.getLongVersionCode(), packageInfo.signatures);
             }
         } catch (NameNotFoundException nnfe) {
             throw new RemoteException(nnfe.getMessage());
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
new file mode 100644
index 0000000..047e270
--- /dev/null
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -0,0 +1,61 @@
+/*
+ * 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.server.slice;
+
+import android.app.slice.ISliceManager;
+import android.content.Context;
+
+import com.android.server.SystemService;
+
+public class SliceManagerService extends ISliceManager.Stub {
+
+    public SliceManagerService(Context context) {
+
+    }
+
+    private void systemReady() {
+    }
+
+    private void onUnlockUser(int userHandle) {
+    }
+
+    public static class Lifecycle extends SystemService {
+        private SliceManagerService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            mService = new SliceManagerService(getContext());
+            publishBinderService(Context.SLICE_SERVICE, mService);
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+                mService.systemReady();
+            }
+        }
+
+        @Override
+        public void onUnlockUser(int userHandle) {
+            mService.onUnlockUser(userHandle);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 00c208d..1ce1400 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -155,6 +155,14 @@
         return ret;
     }
 
+    private final static long[] toLongArray(List<Long> list) {
+        long[] ret = new long[list.size()];
+        for (int i = 0; i < ret.length; i++) {
+            ret[i] = list.get(i);
+        }
+        return ret;
+    }
+
     // Assumes that sStatsdLock is held.
     private final void informAllUidsLocked(Context context) throws RemoteException {
         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -165,7 +173,7 @@
         }
 
         List<Integer> uids = new ArrayList();
-        List<Integer> versions = new ArrayList();
+        List<Long> versions = new ArrayList();
         List<String> apps = new ArrayList();
 
         // Add in all the apps for every user/profile.
@@ -174,12 +182,12 @@
             for (int j = 0; j < pi.size(); j++) {
                 if (pi.get(j).applicationInfo != null) {
                     uids.add(pi.get(j).applicationInfo.uid);
-                    versions.add(pi.get(j).versionCode);
+                    versions.add(pi.get(j).getLongVersionCode());
                     apps.add(pi.get(j).packageName);
                 }
             }
         }
-        sStatsd.informAllUidData(toIntArray(uids), toIntArray(versions), apps.toArray(new
+        sStatsd.informAllUidData(toIntArray(uids), toLongArray(versions), apps.toArray(new
                 String[apps.size()]));
         if (DEBUG) {
             Slog.w(TAG, "Sent data for " + uids.size() + " apps");
@@ -222,7 +230,7 @@
                         int uid = b.getInt(Intent.EXTRA_UID);
                         String app = intent.getData().getSchemeSpecificPart();
                         PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
-                        sStatsd.informOnePackage(app, uid, pi.versionCode);
+                        sStatsd.informOnePackage(app, uid, pi.getLongVersionCode());
                     }
                 } catch (Exception e) {
                     Slog.w(TAG, "Failed to inform statsd of an app update", e);
diff --git a/services/core/java/com/android/server/timezone/CheckToken.java b/services/core/java/com/android/server/timezone/CheckToken.java
index 5128360..4c4a8d7 100644
--- a/services/core/java/com/android/server/timezone/CheckToken.java
+++ b/services/core/java/com/android/server/timezone/CheckToken.java
@@ -46,8 +46,8 @@
         ByteArrayOutputStream baos = new ByteArrayOutputStream(12 /* (3 * sizeof(int)) */);
         try (DataOutputStream dos = new DataOutputStream(baos)) {
             dos.writeInt(mOptimisticLockId);
-            dos.writeInt(mPackageVersions.mUpdateAppVersion);
-            dos.writeInt(mPackageVersions.mDataAppVersion);
+            dos.writeLong(mPackageVersions.mUpdateAppVersion);
+            dos.writeLong(mPackageVersions.mDataAppVersion);
         } catch (IOException e) {
             throw new RuntimeException("Unable to write into a ByteArrayOutputStream", e);
         }
@@ -58,8 +58,8 @@
         ByteArrayInputStream bais = new ByteArrayInputStream(tokenBytes);
         try (DataInputStream dis = new DataInputStream(bais)) {
             int versionId = dis.readInt();
-            int updateAppVersion = dis.readInt();
-            int dataAppVersion = dis.readInt();
+            long updateAppVersion = dis.readLong();
+            long dataAppVersion = dis.readLong();
             return new CheckToken(versionId, new PackageVersions(updateAppVersion, dataAppVersion));
         }
     }
diff --git a/services/core/java/com/android/server/timezone/PackageManagerHelper.java b/services/core/java/com/android/server/timezone/PackageManagerHelper.java
index 804941a..f6e35e8 100644
--- a/services/core/java/com/android/server/timezone/PackageManagerHelper.java
+++ b/services/core/java/com/android/server/timezone/PackageManagerHelper.java
@@ -26,7 +26,7 @@
  */
 interface PackageManagerHelper {
 
-    int getInstalledPackageVersion(String packageName)
+    long getInstalledPackageVersion(String packageName)
             throws PackageManager.NameNotFoundException;
 
     boolean isPrivilegedApp(String packageName) throws PackageManager.NameNotFoundException;
diff --git a/services/core/java/com/android/server/timezone/PackageStatusStorage.java b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
index cac7f7b..5601c91 100644
--- a/services/core/java/com/android/server/timezone/PackageStatusStorage.java
+++ b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
@@ -76,7 +76,7 @@
      */
     private static final String ATTRIBUTE_DATA_APP_VERSION = "dataAppPackageVersion";
 
-    private static final int UNKNOWN_PACKAGE_VERSION = -1;
+    private static final long UNKNOWN_PACKAGE_VERSION = -1;
 
     private final AtomicFile mPackageStatusFile;
 
@@ -320,14 +320,14 @@
             serializer.attribute(namespace, ATTRIBUTE_CHECK_STATUS, statusAttributeValue);
             serializer.attribute(namespace, ATTRIBUTE_OPTIMISTIC_LOCK_ID,
                     Integer.toString(optimisticLockId));
-            int updateAppVersion = status == null
+            long updateAppVersion = status == null
                     ? UNKNOWN_PACKAGE_VERSION : packageVersions.mUpdateAppVersion;
             serializer.attribute(namespace, ATTRIBUTE_UPDATE_APP_VERSION,
-                    Integer.toString(updateAppVersion));
-            int dataAppVersion = status == null
+                    Long.toString(updateAppVersion));
+            long dataAppVersion = status == null
                     ? UNKNOWN_PACKAGE_VERSION : packageVersions.mDataAppVersion;
             serializer.attribute(namespace, ATTRIBUTE_DATA_APP_VERSION,
-                    Integer.toString(dataAppVersion));
+                    Long.toString(dataAppVersion));
             serializer.endTag(namespace, TAG_PACKAGE_STATUS);
             serializer.endDocument();
             serializer.flush();
diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java
index f0306b9..1c54320 100644
--- a/services/core/java/com/android/server/timezone/PackageTracker.java
+++ b/services/core/java/com/android/server/timezone/PackageTracker.java
@@ -445,8 +445,8 @@
     }
 
     private PackageVersions lookupInstalledPackageVersions() {
-        int updatePackageVersion;
-        int dataPackageVersion;
+        long updatePackageVersion;
+        long dataPackageVersion;
         try {
             updatePackageVersion =
                     mPackageManagerHelper.getInstalledPackageVersion(mUpdateAppPackageName);
diff --git a/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java b/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java
index b89dd38..6a330e6 100644
--- a/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java
+++ b/services/core/java/com/android/server/timezone/PackageTrackerHelperImpl.java
@@ -81,11 +81,11 @@
     }
 
     @Override
-    public int getInstalledPackageVersion(String packageName)
+    public long getInstalledPackageVersion(String packageName)
             throws PackageManager.NameNotFoundException {
         int flags = PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
         PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags);
-        return packageInfo.versionCode;
+        return packageInfo.getLongVersionCode();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/timezone/PackageVersions.java b/services/core/java/com/android/server/timezone/PackageVersions.java
index fc0d6e1..0084c1a 100644
--- a/services/core/java/com/android/server/timezone/PackageVersions.java
+++ b/services/core/java/com/android/server/timezone/PackageVersions.java
@@ -21,10 +21,10 @@
  */
 final class PackageVersions {
 
-    final int mUpdateAppVersion;
-    final int mDataAppVersion;
+    final long mUpdateAppVersion;
+    final long mDataAppVersion;
 
-    PackageVersions(int updateAppVersion, int dataAppVersion) {
+    PackageVersions(long updateAppVersion, long dataAppVersion) {
         this.mUpdateAppVersion = updateAppVersion;
         this.mDataAppVersion = dataAppVersion;
     }
@@ -48,8 +48,8 @@
 
     @Override
     public int hashCode() {
-        int result = mUpdateAppVersion;
-        result = 31 * result + mDataAppVersion;
+        int result = Long.hashCode(mUpdateAppVersion);
+        result = 31 * result + Long.hashCode(mDataAppVersion);
         return result;
     }
 
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 1f4e64e..7d55b68 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -59,6 +59,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import com.android.server.wm.WindowManagerInternal;
+import android.view.inputmethod.InputMethodManagerInternal;
 
 import com.android.internal.R;
 import com.android.internal.util.DumpUtils;
@@ -609,6 +610,14 @@
         }
 
         @Override
+        public void setVrInputMethod(ComponentName componentName) {
+            enforceCallerPermissionAnyOf(Manifest.permission.RESTRICTED_VR_ACCESS);
+            InputMethodManagerInternal imm =
+                    LocalServices.getService(InputMethodManagerInternal.class);
+            imm.startVrInputMethodNoCheck(componentName);
+        }
+
+        @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
 
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 1e334b8..4aa2b37 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -156,9 +156,10 @@
         return mWebViewProviderPackages;
     }
 
-    public int getFactoryPackageVersion(String packageName) throws NameNotFoundException {
+    public long getFactoryPackageVersion(String packageName) throws NameNotFoundException {
         PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
-        return pm.getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY).versionCode;
+        return pm.getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY)
+                .getLongVersionCode();
     }
 
     /**
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index d57edcd..405fe7d 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -36,7 +36,7 @@
 public interface SystemInterface {
     public WebViewProviderInfo[] getWebViewPackages();
     public int onWebViewProviderChanged(PackageInfo packageInfo);
-    public int getFactoryPackageVersion(String packageName) throws NameNotFoundException;
+    public long getFactoryPackageVersion(String packageName) throws NameNotFoundException;
 
     public String getUserChosenWebViewProvider(Context context);
     public void updateUserSetting(Context context, String newProviderName);
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdater.java b/services/core/java/com/android/server/webkit/WebViewUpdater.java
index 7fc907f..7e05e46 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdater.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdater.java
@@ -54,7 +54,7 @@
 
     private Context mContext;
     private SystemInterface mSystemInterface;
-    private int mMinimumVersionCode = -1;
+    private long mMinimumVersionCode = -1;
 
     // Keeps track of the number of running relro creations
     private int mNumRelroCreationsStarted = 0;
@@ -430,7 +430,7 @@
         if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) {
             return VALIDITY_INCORRECT_SDK_VERSION;
         }
-        if (!versionCodeGE(packageInfo.versionCode, getMinimumVersionCode())
+        if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode())
                 && !mSystemInterface.systemIsDebuggable()) {
             // Webview providers may be downgraded arbitrarily low, prevent that by enforcing
             // minimum version code. This check is only enforced for user builds.
@@ -461,9 +461,9 @@
      *
      * @return true if versionCode1 is higher than or equal to versionCode2.
      */
-    private static boolean versionCodeGE(int versionCode1, int versionCode2) {
-        int v1 = versionCode1 / 100000;
-        int v2 = versionCode2 / 100000;
+    private static boolean versionCodeGE(long versionCode1, long versionCode2) {
+        long v1 = versionCode1 / 100000;
+        long v2 = versionCode2 / 100000;
 
         return v1 >= v2;
     }
@@ -478,16 +478,16 @@
      * (mMinimumVersionCode) which is shared between threads. Furthermore, this method does not
      * hold mLock meaning that we must take extra care to ensure this method is thread-safe.
      */
-    private int getMinimumVersionCode() {
+    private long getMinimumVersionCode() {
         if (mMinimumVersionCode > 0) {
             return mMinimumVersionCode;
         }
 
-        int minimumVersionCode = -1;
+        long minimumVersionCode = -1;
         for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
             if (provider.availableByDefault && !provider.isFallback) {
                 try {
-                    int versionCode =
+                    long versionCode =
                         mSystemInterface.getFactoryPackageVersion(provider.packageName);
                     if (minimumVersionCode < 0 || versionCode < minimumVersionCode) {
                         minimumVersionCode = versionCode;
@@ -577,7 +577,7 @@
             String packageDetails = String.format(
                     "versionName: %s, versionCode: %d, targetSdkVersion: %d",
                     systemUserPackageInfo.versionName,
-                    systemUserPackageInfo.versionCode,
+                    systemUserPackageInfo.getLongVersionCode(),
                     systemUserPackageInfo.applicationInfo.targetSdkVersion);
             if (validity == VALIDITY_OK) {
                 boolean installedForAllUsers = isInstalledAndEnabledForAllUsers(
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index 0249713..209ce3f 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -21,8 +21,10 @@
 import static android.view.Surface.ROTATION_90;
 import static com.android.server.wm.proto.DisplayFramesProto.STABLE_BOUNDS;
 
+import android.annotation.NonNull;
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 
 import java.io.PrintWriter;
@@ -94,6 +96,14 @@
     /** During layout, the current screen borders along which input method windows are placed. */
     public final Rect mDock = new Rect();
 
+    /** Definition of the cutout */
+    @NonNull public DisplayCutout mDisplayCutout = DisplayCutout.NO_CUTOUT;
+
+    /**
+     * During layout, the frame that is display-cutout safe, i.e. that does not intersect with it.
+     */
+    public final Rect mDisplayCutoutSafe = new Rect();
+
     private final Rect mDisplayInfoOverscan = new Rect();
     private final Rect mRotatedDisplayInfoOverscan = new Rect();
     public int mDisplayWidth;
@@ -152,7 +162,9 @@
         mStable.set(mUnrestricted);
         mStableFullscreen.set(mUnrestricted);
         mCurrent.set(mUnrestricted);
-
+        mDisplayCutout = DisplayCutout.NO_CUTOUT;
+        mDisplayCutoutSafe.set(Integer.MIN_VALUE, Integer.MIN_VALUE,
+                Integer.MAX_VALUE, Integer.MAX_VALUE);
     }
 
     public int getInputMethodWindowVisibleHeight() {
@@ -182,6 +194,7 @@
         dumpFrame(mUnrestricted, "mUnrestricted", myPrefix, pw);
         dumpFrame(mDisplayInfoOverscan, "mDisplayInfoOverscan", myPrefix, pw);
         dumpFrame(mRotatedDisplayInfoOverscan, "mRotatedDisplayInfoOverscan", myPrefix, pw);
+        pw.println(myPrefix + "mDisplayCutout=" + mDisplayCutout);
     }
 
     private void dumpFrame(Rect frame, String name, String prefix, PrintWriter pw) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index ad7c300..63eea10 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -23,9 +23,8 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
+
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -45,13 +44,13 @@
 import android.util.MergedConfiguration;
 import android.util.Slog;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.IWindow;
 import android.view.IWindowId;
 import android.view.IWindowSession;
 import android.view.IWindowSessionCallback;
 import android.view.InputChannel;
 import android.view.Surface;
-import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 import android.view.WindowManager;
 
@@ -192,15 +191,17 @@
             int viewVisibility, Rect outContentInsets, Rect outStableInsets,
             InputChannel outInputChannel) {
         return addToDisplay(window, seq, attrs, viewVisibility, Display.DEFAULT_DISPLAY,
-                outContentInsets, outStableInsets, null /* outOutsets */, outInputChannel);
+                outContentInsets, outStableInsets, null /* outOutsets */, null /* cutout */,
+                outInputChannel);
     }
 
     @Override
     public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
-            Rect outOutsets, InputChannel outInputChannel) {
+            Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout,
+            InputChannel outInputChannel) {
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
-                outContentInsets, outStableInsets, outOutsets, outInputChannel);
+                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
     }
 
     @Override
@@ -214,7 +215,7 @@
     public int addToDisplayWithoutInputChannel(IWindow window, int seq, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets) {
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
-            outContentInsets, outStableInsets, null /* outOutsets */, null);
+            outContentInsets, outStableInsets, null /* outOutsets */, null /* cutout */, null);
     }
 
     @Override
@@ -232,6 +233,7 @@
             int requestedWidth, int requestedHeight, int viewFlags,
             int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
             Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
+            DisplayCutout.ParcelableWrapper cutout,
             MergedConfiguration mergedConfiguration, Surface outSurface) {
         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
@@ -239,7 +241,8 @@
         int res = mService.relayoutWindow(this, window, seq, attrs,
                 requestedWidth, requestedHeight, viewFlags, flags,
                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
-                outStableInsets, outsets, outBackdropFrame, mergedConfiguration, outSurface);
+                outStableInsets, outsets, outBackdropFrame, cutout,
+                mergedConfiguration, outSurface);
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 43a0893..c091157 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -236,7 +236,7 @@
             return null;
         }
         return new TaskSnapshot(buffer, top.getConfiguration().orientation,
-                minRect(mainWindow.mContentInsets, mainWindow.mStableInsets),
+                getInsetsFromTaskBounds(mainWindow, task),
                 isLowRamDevice /* reduced */, scaleFraction /* scale */);
     }
 
@@ -244,11 +244,18 @@
         return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT;
     }
 
-    private Rect minRect(Rect rect1, Rect rect2) {
-        return new Rect(Math.min(rect1.left, rect2.left),
-                Math.min(rect1.top, rect2.top),
-                Math.min(rect1.right, rect2.right),
-                Math.min(rect1.bottom, rect2.bottom));
+    private Rect getInsetsFromTaskBounds(WindowState state, Task task) {
+        final Rect r = new Rect();
+        r.set(state.getContentFrameLw());
+        r.intersectUnchecked(state.getStableFrameLw());
+
+        final Rect taskBounds = task.getBounds();
+
+        r.set(Math.max(0, r.left - taskBounds.left),
+                Math.max(0, r.top - taskBounds.top),
+                Math.max(0, taskBounds.right - r.right),
+                Math.max(0, taskBounds.bottom - r.bottom));
+        return r;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 41915a3..94fbd0e 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -57,6 +57,7 @@
 import android.os.SystemClock;
 import android.util.MergedConfiguration;
 import android.util.Slog;
+import android.view.DisplayCutout;
 import android.view.IWindowSession;
 import android.view.Surface;
 import android.view.SurfaceControl;
@@ -134,6 +135,7 @@
         window.setSession(session);
         final Surface surface = new Surface();
         final Rect tmpRect = new Rect();
+        final DisplayCutout.ParcelableWrapper tmpCutout = new DisplayCutout.ParcelableWrapper();
         final Rect tmpFrame = new Rect();
         final Rect taskBounds;
         final Rect tmpContentInsets = new Rect();
@@ -196,7 +198,7 @@
         try {
             final int res = session.addToDisplay(window, window.mSeq, layoutParams,
                     View.VISIBLE, token.getDisplayContent().getDisplayId(), tmpRect, tmpRect,
-                    tmpRect, null);
+                    tmpRect, tmpCutout, null);
             if (res < 0) {
                 Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
                 return null;
@@ -212,7 +214,7 @@
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame,
                     tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
-                    tmpMergedConfiguration, surface);
+                    tmpCutout, tmpMergedConfiguration, surface);
         } catch (RemoteException e) {
             // Local call.
         }
@@ -349,8 +351,9 @@
 
         // Let's remove all system decorations except the status bar, but only if the task is at the
         // very top of the screen.
+        final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
         rect.inset((int) (insets.left * mSnapshot.getScale()),
-                mTaskBounds.top != 0 ? (int) (insets.top * mSnapshot.getScale()) : 0,
+                isTop ? 0 : (int) (insets.top * mSnapshot.getScale()),
                 (int) (insets.right * mSnapshot.getScale()),
                 (int) (insets.bottom * mSnapshot.getScale()));
         return rect;
@@ -437,7 +440,8 @@
         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
                 Rect stableInsets, Rect outsets, boolean reportDraw,
                 MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
-                boolean alwaysConsumeNavBar, int displayId) {
+                boolean alwaysConsumeNavBar, int displayId,
+                DisplayCutout.ParcelableWrapper displayCutout) {
             if (mergedConfiguration != null && mOuter != null
                     && mOuter.mOrientationOnCreation
                             != mergedConfiguration.getMergedConfiguration().orientation) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 6467582..c371e88 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -21,6 +21,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static com.android.server.wm.proto.WindowContainerProto.CONFIGURATION_CONTAINER;
 import static com.android.server.wm.proto.WindowContainerProto.ORIENTATION;
+import static com.android.server.wm.proto.WindowContainerProto.VISIBLE;
 import static android.view.SurfaceControl.Transaction;
 
 import android.annotation.CallSuper;
@@ -809,6 +810,7 @@
         final long token = proto.start(fieldId);
         super.writeToProto(proto, CONFIGURATION_CONTAINER, trim);
         proto.write(ORIENTATION, mOrientation);
+        proto.write(VISIBLE, isVisible());
         proto.end(token);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 82f842c..3ad4df7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -65,6 +65,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
+
+import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN;
 import static com.android.server.LockGuard.INDEX_WINDOW;
 import static com.android.server.LockGuard.installLock;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -189,6 +191,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.AppTransitionAnimationSpec;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IAppTransitionAnimationSpecsFuture;
@@ -229,6 +232,7 @@
 import com.android.internal.policy.IShortcutService;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.LatencyTracker;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
 import com.android.internal.view.IInputMethodManager;
@@ -771,6 +775,8 @@
 
     private WindowContentFrameStats mTempWindowRenderStats;
 
+    private final LatencyTracker mLatencyTracker;
+
     /**
      * Whether the UI is currently running in touch mode (not showing
      * navigational focus because the user is directly pressing the screen).
@@ -1070,6 +1076,8 @@
         filter.addAction(Intent.ACTION_USER_REMOVED);
         mContext.registerReceiver(mBroadcastReceiver, filter);
 
+        mLatencyTracker = LatencyTracker.getInstance(context);
+
         mSettingsObserver = new SettingsObserver();
 
         mHoldingScreenWakeLock = mPowerManager.newWakeLock(
@@ -1129,9 +1137,9 @@
     }
 
     public int addWindow(Session session, IWindow client, int seq,
-            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
+            LayoutParams attrs, int viewVisibility, int displayId,
             Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
-            InputChannel outInputChannel) {
+            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
         int[] appOp = new int[1];
         int res = mPolicy.checkAddPermission(attrs, appOp);
         if (res != WindowManagerGlobal.ADD_OKAY) {
@@ -1466,7 +1474,7 @@
                 taskBounds = null;
             }
             if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, displayFrames, outContentInsets,
-                    outStableInsets, outOutsets)) {
+                    outStableInsets, outOutsets, outDisplayCutout)) {
                 res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;
             }
 
@@ -1846,11 +1854,12 @@
     }
 
     public int relayoutWindow(Session session, IWindow client, int seq,
-            WindowManager.LayoutParams attrs, int requestedWidth,
+            LayoutParams attrs, int requestedWidth,
             int requestedHeight, int viewVisibility, int flags,
             Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
             Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
-            MergedConfiguration mergedConfiguration, Surface outSurface) {
+            DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
+            Surface outSurface) {
         int result = 0;
         boolean configChanged;
         final boolean hasStatusBarPermission =
@@ -2135,6 +2144,7 @@
             win.mLastRelayoutContentInsets.set(win.mContentInsets);
             outVisibleInsets.set(win.mVisibleInsets);
             outStableInsets.set(win.mStableInsets);
+            outCutout.set(win.mDisplayCutout);
             outOutsets.set(win.mOutsets);
             outBackdropFrame.set(win.getBackdropFrame(win.mFrame));
             if (localLOGV) Slog.v(
@@ -3046,11 +3056,6 @@
     }
 
     @Override
-    public boolean inKeyguardRestrictedInputMode() {
-        return mPolicy.inKeyguardRestrictedKeyInputMode();
-    }
-
-    @Override
     public boolean isKeyguardLocked() {
         return mPolicy.isKeyguardLocked();
     }
@@ -5863,6 +5868,7 @@
             Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
         }
 
+        mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
         // TODO(multidisplay): rotation on non-default displays
         if (CUSTOM_SCREEN_ROTATION && displayContent.isDefaultDisplay) {
             mExitAnimId = exitAnim;
@@ -5987,6 +5993,7 @@
         if (configChanged) {
             mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayId).sendToTarget();
         }
+        mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN);
     }
 
     static int getPropertyInt(String[] tokens, int index, int defUnits, int defDps,
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9eab61c..d23a6c7 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -42,7 +42,6 @@
 import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
@@ -145,6 +144,7 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IApplicationToken;
@@ -321,6 +321,11 @@
     private final Rect mLastOutsets = new Rect();
     private boolean mOutsetsChanged = false;
 
+    /** Part of the display that has been cut away. See {@link DisplayCutout}. */
+    DisplayCutout mDisplayCutout = DisplayCutout.NO_CUTOUT;
+    private DisplayCutout mLastDisplayCutout = DisplayCutout.NO_CUTOUT;
+    private boolean mDisplayCutoutChanged;
+
     /**
      * Set to true if we are waiting for this window to receive its
      * given internal insets before laying out other windows based on it.
@@ -773,7 +778,7 @@
     @Override
     public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overscanFrame,
             Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
-            Rect outsetFrame) {
+            Rect outsetFrame, DisplayCutout displayCutout) {
         if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
             // This window is being replaced and either already got information that it's being
             // removed or we are still waiting for some information. Because of this we don't
@@ -1010,6 +1015,10 @@
         mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
         mStableFrame.offset(-layoutXDiff, -layoutYDiff);
 
+        // TODO(roosa): Figure out what frame exactly this needs to be calculated with.
+        mDisplayCutout = displayCutout.calculateRelativeTo(mFrame);
+
+
         mCompatFrame.set(mFrame);
         if (mEnforceSizeCompat) {
             // If there is a size compatibility scale being applied to the
@@ -1150,8 +1159,9 @@
         mOutsetsChanged |= !mLastOutsets.equals(mOutsets);
         mFrameSizeChanged |= (mLastFrame.width() != mFrame.width()) ||
                 (mLastFrame.height() != mFrame.height());
+        mDisplayCutoutChanged |= !mLastDisplayCutout.equals(mDisplayCutout);
         return mOverscanInsetsChanged || mContentInsetsChanged || mVisibleInsetsChanged
-                || mOutsetsChanged || mFrameSizeChanged;
+                || mOutsetsChanged || mFrameSizeChanged || mDisplayCutoutChanged;
     }
 
     /**
@@ -1196,6 +1206,7 @@
                 || winAnimator.mSurfaceResized
                 || mOutsetsChanged
                 || mFrameSizeChanged
+                || mDisplayCutoutChanged
                 || configChanged
                 || dragResizingChanged
                 || !isResizedWhileNotDragResizingReported()
@@ -1215,7 +1226,8 @@
                         + " dragResizingChanged=" + dragResizingChanged
                         + " resizedWhileNotDragResizingReported="
                         + isResizedWhileNotDragResizingReported()
-                        + " reportOrientationChanged=" + mReportOrientationChanged);
+                        + " reportOrientationChanged=" + mReportOrientationChanged
+                        + " displayCutoutChanged=" + mDisplayCutoutChanged);
             }
 
             // If it's a dead window left on screen, and the configuration changed, there is nothing
@@ -2850,6 +2862,7 @@
             final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING;
             final boolean reportOrientation = mReportOrientationChanged;
             final int displayId = getDisplayId();
+            final DisplayCutout displayCutout = mDisplayCutout;
             if (mAttrs.type != WindowManager.LayoutParams.TYPE_APPLICATION_STARTING
                     && mClient instanceof IWindow.Stub) {
                 // To prevent deadlock simulate one-way call if win.mClient is a local object.
@@ -2859,7 +2872,7 @@
                         try {
                             dispatchResized(frame, overscanInsets, contentInsets, visibleInsets,
                                     stableInsets, outsets, reportDraw, mergedConfiguration,
-                                    reportOrientation, displayId);
+                                    reportOrientation, displayId, displayCutout);
                         } catch (RemoteException e) {
                             // Not a remote call, RemoteException won't be raised.
                         }
@@ -2867,7 +2880,8 @@
                 });
             } else {
                 dispatchResized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
-                        outsets, reportDraw, mergedConfiguration, reportOrientation, displayId);
+                        outsets, reportDraw, mergedConfiguration, reportOrientation, displayId,
+                        displayCutout);
             }
 
             //TODO (multidisplay): Accessibility supported only for the default display.
@@ -2881,6 +2895,7 @@
             mStableInsetsChanged = false;
             mOutsetsChanged = false;
             mFrameSizeChanged = false;
+            mDisplayCutoutChanged = false;
             mResizedWhileNotDragResizingReported = true;
             mWinAnimator.mSurfaceResized = false;
             mReportOrientationChanged = false;
@@ -2923,14 +2938,16 @@
 
     private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
             Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
-            MergedConfiguration mergedConfiguration, boolean reportOrientation, int displayId)
+            MergedConfiguration mergedConfiguration, boolean reportOrientation, int displayId,
+            DisplayCutout displayCutout)
             throws RemoteException {
         final boolean forceRelayout = isDragResizeChanged() || mResizedWhileNotDragResizing
                 || reportOrientation;
 
         mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets,
                 reportDraw, mergedConfiguration, getBackdropFrame(frame), forceRelayout,
-                mPolicy.isNavBarForcedShownLw(this), displayId);
+                mPolicy.isNavBarForcedShownLw(this), displayId,
+                new DisplayCutout.ParcelableWrapper(displayCutout));
         mDragResizingChangeReported = true;
     }
 
@@ -3247,6 +3264,7 @@
                     pw.print(" stable="); mStableInsets.printShortString(pw);
                     pw.print(" surface="); mAttrs.surfaceInsets.printShortString(pw);
                     pw.print(" outsets="); mOutsets.printShortString(pw);
+                    pw.print(" cutout=" + mDisplayCutout);
                     pw.println();
             pw.print(prefix); pw.print("Lst insets: overscan=");
                     mLastOverscanInsets.printShortString(pw);
@@ -3255,6 +3273,7 @@
                     pw.print(" stable="); mLastStableInsets.printShortString(pw);
                     pw.print(" physical="); mLastOutsets.printShortString(pw);
                     pw.print(" outset="); mLastOutsets.printShortString(pw);
+                    pw.print(" cutout=" + mLastDisplayCutout);
                     pw.println();
         }
         pw.print(prefix); pw.print(mWinAnimator); pw.println(":");
@@ -4279,6 +4298,7 @@
         mLastVisibleInsets.set(mVisibleInsets);
         mLastStableInsets.set(mStableInsets);
         mLastOutsets.set(mOutsets);
+        mLastDisplayCutout = mDisplayCutout;
     }
 
     // TODO: Hack to work around the number of states AppWindowToken needs to access without having
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index c9d7b70..a3d4b71 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -125,6 +125,11 @@
     }
 
     void setExiting() {
+        if (mChildren.size() == 0) {
+            super.removeImmediately();
+            return;
+        }
+
         // This token is exiting, so allow it to be removed when it no longer contains any windows.
         mPersistOnEmpty = false;
 
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
new file mode 100644
index 0000000..5d76304
--- /dev/null
+++ b/services/core/jni/Android.bp
@@ -0,0 +1,121 @@
+cc_library_static {
+    name: "libservices.core",
+    defaults: ["libservices.core-libs"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+
+        "-DEGL_EGLEXT_PROTOTYPES",
+        "-DGL_GLEXT_PROTOTYPES",
+    ],
+
+    srcs: [
+        "BroadcastRadio/JavaRef.cpp",
+        "BroadcastRadio/NativeCallbackThread.cpp",
+        "BroadcastRadio/BroadcastRadioService.cpp",
+        "BroadcastRadio/Tuner.cpp",
+        "BroadcastRadio/TunerCallback.cpp",
+        "BroadcastRadio/convert.cpp",
+        "BroadcastRadio/regions.cpp",
+        "com_android_server_AlarmManagerService.cpp",
+        "com_android_server_am_BatteryStatsService.cpp",
+        "com_android_server_connectivity_Vpn.cpp",
+        "com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp",
+        "com_android_server_ConsumerIrService.cpp",
+        "com_android_server_HardwarePropertiesManagerService.cpp",
+        "com_android_server_hdmi_HdmiCecController.cpp",
+        "com_android_server_input_InputApplicationHandle.cpp",
+        "com_android_server_input_InputManagerService.cpp",
+        "com_android_server_input_InputWindowHandle.cpp",
+        "com_android_server_lights_LightsService.cpp",
+        "com_android_server_location_GnssLocationProvider.cpp",
+        "com_android_server_locksettings_SyntheticPasswordManager.cpp",
+        "com_android_server_power_PowerManagerService.cpp",
+        "com_android_server_SerialService.cpp",
+        "com_android_server_storage_AppFuseBridge.cpp",
+        "com_android_server_SystemServer.cpp",
+        "com_android_server_tv_TvUinputBridge.cpp",
+        "com_android_server_tv_TvInputHal.cpp",
+        "com_android_server_vr_VrManagerService.cpp",
+        "com_android_server_UsbDeviceManager.cpp",
+        "com_android_server_UsbDescriptorParser.cpp",
+        "com_android_server_UsbMidiDevice.cpp",
+        "com_android_server_UsbHostManager.cpp",
+        "com_android_server_VibratorService.cpp",
+        "com_android_server_PersistentDataBlockService.cpp",
+        "com_android_server_GraphicsStatsService.cpp",
+        "onload.cpp",
+    ],
+
+    include_dirs: [
+        "frameworks/base/libs",
+        "frameworks/native/services",
+        "system/gatekeeper/include",
+    ],
+}
+
+cc_defaults {
+    name: "libservices.core-libs",
+    shared_libs: [
+        "libandroid_runtime",
+        "libandroidfw",
+        "libaudioclient",
+        "libbase",
+        "libappfuse",
+        "libbinder",
+        "libcutils",
+        "libcrypto",
+        "liblog",
+        "libhardware",
+        "libhardware_legacy",
+        "libhidlbase",
+        "libkeystore_binder",
+        "libnativehelper",
+        "libutils",
+        "libui",
+        "libinput",
+        "libinputflinger",
+        "libinputservice",
+        "libschedulerservicehidl",
+        "libsensorservice",
+        "libsensorservicehidl",
+        "libgui",
+        "libusbhost",
+        "libsuspend",
+        "libEGL",
+        "libGLESv2",
+        "libnetutils",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "libutils",
+        "libhwui",
+        "android.hardware.audio.common@2.0",
+        "android.hardware.broadcastradio@1.0",
+        "android.hardware.broadcastradio@1.1",
+        "android.hardware.broadcastradio@1.2",
+        "android.hardware.contexthub@1.0",
+        "android.hardware.gnss@1.0",
+        "android.hardware.gnss@1.1",
+        "android.hardware.ir@1.0",
+        "android.hardware.light@2.0",
+        "android.hardware.power@1.0",
+        "android.hardware.power@1.1",
+        "android.hardware.tetheroffload.config@1.0",
+        "android.hardware.thermal@1.0",
+        "android.hardware.tv.cec@1.0",
+        "android.hardware.tv.input@1.0",
+        "android.hardware.vibrator@1.0",
+        "android.hardware.vibrator@1.1",
+        "android.hardware.vr@1.0",
+        "android.frameworks.schedulerservice@1.0",
+        "android.frameworks.sensorservice@1.0",
+    ],
+
+    static_libs: [
+        "android.hardware.broadcastradio@common-utils-lib",
+        "libscrypt_static",
+    ],
+}
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
deleted file mode 100644
index 8b9cf4b..0000000
--- a/services/core/jni/Android.mk
+++ /dev/null
@@ -1,114 +0,0 @@
-# This file is included by the top level services directory to collect source
-# files
-LOCAL_REL_DIR := core/jni
-
-LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter
-
-LOCAL_SRC_FILES += \
-    $(LOCAL_REL_DIR)/BroadcastRadio/JavaRef.cpp \
-    $(LOCAL_REL_DIR)/BroadcastRadio/NativeCallbackThread.cpp \
-    $(LOCAL_REL_DIR)/BroadcastRadio/BroadcastRadioService.cpp \
-    $(LOCAL_REL_DIR)/BroadcastRadio/Tuner.cpp \
-    $(LOCAL_REL_DIR)/BroadcastRadio/TunerCallback.cpp \
-    $(LOCAL_REL_DIR)/BroadcastRadio/convert.cpp \
-    $(LOCAL_REL_DIR)/BroadcastRadio/regions.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_AlarmManagerService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_am_BatteryStatsService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_HardwarePropertiesManagerService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecController.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_input_InputApplicationHandle.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_input_InputWindowHandle.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_lights_LightsService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_location_GnssLocationProvider.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_locksettings_SyntheticPasswordManager.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_SerialService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_storage_AppFuseBridge.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_SystemServer.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_tv_TvUinputBridge.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_tv_TvInputHal.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_vr_VrManagerService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_UsbDescriptorParser.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_UsbMidiDevice.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_PersistentDataBlockService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_GraphicsStatsService.cpp \
-    $(LOCAL_REL_DIR)/onload.cpp
-
-LOCAL_C_INCLUDES += \
-    $(JNI_H_INCLUDE) \
-    external/scrypt/lib/crypto \
-    frameworks/base/services \
-    frameworks/base/libs \
-    frameworks/base/core/jni \
-    frameworks/native/services \
-    system/core/libappfuse/include \
-    system/gatekeeper/include \
-    system/security/keystore/include \
-    $(call include-path-for, libhardware)/hardware \
-    $(call include-path-for, libhardware_legacy)/hardware_legacy \
-
-LOCAL_SHARED_LIBRARIES += \
-    libandroid_runtime \
-    libandroidfw \
-    libaudioclient \
-    libbase \
-    libappfuse \
-    libbinder \
-    libcutils \
-    libcrypto \
-    liblog \
-    libhardware \
-    libhardware_legacy \
-    libhidlbase \
-    libkeystore_binder \
-    libnativehelper \
-    libutils \
-    libui \
-    libinput \
-    libinputflinger \
-    libinputservice \
-    libschedulerservicehidl \
-    libsensorservice \
-    libsensorservicehidl \
-    libgui \
-    libusbhost \
-    libsuspend \
-    libEGL \
-    libGLESv2 \
-    libnetutils \
-    libhidlbase \
-    libhidltransport \
-    libhwbinder \
-    libutils \
-    libhwui \
-    android.hardware.audio.common@2.0 \
-    android.hardware.broadcastradio@1.0 \
-    android.hardware.broadcastradio@1.1 \
-    android.hardware.broadcastradio@1.2 \
-    android.hardware.contexthub@1.0 \
-    android.hardware.gnss@1.0 \
-    android.hardware.gnss@1.1 \
-    android.hardware.ir@1.0 \
-    android.hardware.light@2.0 \
-    android.hardware.power@1.0 \
-    android.hardware.power@1.1 \
-    android.hardware.tetheroffload.config@1.0 \
-    android.hardware.thermal@1.0 \
-    android.hardware.tv.cec@1.0 \
-    android.hardware.tv.input@1.0 \
-    android.hardware.vibrator@1.0 \
-    android.hardware.vibrator@1.1 \
-    android.hardware.vr@1.0 \
-    android.frameworks.schedulerservice@1.0 \
-    android.frameworks.sensorservice@1.0 \
-
-LOCAL_STATIC_LIBRARIES += \
-    android.hardware.broadcastradio@common-utils-lib \
-    libscrypt_static \
diff --git a/services/core/jni/com_android_server_input_InputApplicationHandle.cpp b/services/core/jni/com_android_server_input_InputApplicationHandle.cpp
index 232b2c2..514b6e1 100644
--- a/services/core/jni/com_android_server_input_InputApplicationHandle.cpp
+++ b/services/core/jni/com_android_server_input_InputApplicationHandle.cpp
@@ -65,11 +65,11 @@
             gInputApplicationHandleClassInfo.name));
     if (nameObj) {
         const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
-        mInfo->name.setTo(nameStr);
+        mInfo->name = nameStr;
         env->ReleaseStringUTFChars(nameObj, nameStr);
         env->DeleteLocalRef(nameObj);
     } else {
-        mInfo->name.setTo("<null>");
+        mInfo->name = "<null>";
     }
 
     mInfo->dispatchingTimeout = env->GetLongField(obj,
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 21300ed..27c2fac 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -32,6 +32,7 @@
 #include <atomic>
 #include <cinttypes>
 #include <limits.h>
+#include <android-base/stringprintf.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
 
@@ -65,6 +66,8 @@
 
 #define INDENT "  "
 
+using android::base::StringPrintf;
+
 namespace android {
 
 // The exponent used to calculate the pointer speed scaling factor.
@@ -198,7 +201,7 @@
 
     inline sp<InputManager> getInputManager() const { return mInputManager; }
 
-    void dump(String8& dump);
+    void dump(std::string& dump);
 
     void setVirtualDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
     void setDisplayViewport(int32_t viewportType, const DisplayViewport& viewport);
@@ -240,7 +243,7 @@
     virtual void notifyConfigurationChanged(nsecs_t when);
     virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
             const sp<InputWindowHandle>& inputWindowHandle,
-            const String8& reason);
+            const std::string& reason);
     virtual void notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle);
     virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags);
     virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig);
@@ -347,28 +350,28 @@
     env->DeleteGlobalRef(mServiceObj);
 }
 
-void NativeInputManager::dump(String8& dump) {
-    dump.append("Input Manager State:\n");
+void NativeInputManager::dump(std::string& dump) {
+    dump += "Input Manager State:\n";
     {
-        dump.appendFormat(INDENT "Interactive: %s\n", toString(mInteractive.load()));
+        dump += StringPrintf(INDENT "Interactive: %s\n", toString(mInteractive.load()));
     }
     {
         AutoMutex _l(mLock);
-        dump.appendFormat(INDENT "System UI Visibility: 0x%0" PRIx32 "\n",
+        dump += StringPrintf(INDENT "System UI Visibility: 0x%0" PRIx32 "\n",
                 mLocked.systemUiVisibility);
-        dump.appendFormat(INDENT "Pointer Speed: %" PRId32 "\n", mLocked.pointerSpeed);
-        dump.appendFormat(INDENT "Pointer Gestures Enabled: %s\n",
+        dump += StringPrintf(INDENT "Pointer Speed: %" PRId32 "\n", mLocked.pointerSpeed);
+        dump += StringPrintf(INDENT "Pointer Gestures Enabled: %s\n",
                 toString(mLocked.pointerGesturesEnabled));
-        dump.appendFormat(INDENT "Show Touches: %s\n", toString(mLocked.showTouches));
-        dump.appendFormat(INDENT "Pointer Capture Enabled: %s\n", toString(mLocked.pointerCapture));
+        dump += StringPrintf(INDENT "Show Touches: %s\n", toString(mLocked.showTouches));
+        dump += StringPrintf(INDENT "Pointer Capture Enabled: %s\n", toString(mLocked.pointerCapture));
     }
-    dump.append("\n");
+    dump += "\n";
 
     mInputManager->getReader()->dump(dump);
-    dump.append("\n");
+    dump += "\n";
 
     mInputManager->getDispatcher()->dump(dump);
-    dump.append("\n");
+    dump += "\n";
 }
 
 bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
@@ -668,7 +671,7 @@
 }
 
 nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
-        const sp<InputWindowHandle>& inputWindowHandle, const String8& reason) {
+        const sp<InputWindowHandle>& inputWindowHandle, const std::string& reason) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
     ALOGD("notifyANR");
 #endif
@@ -680,7 +683,7 @@
             getInputApplicationHandleObjLocalRef(env, inputApplicationHandle);
     jobject inputWindowHandleObj =
             getInputWindowHandleObjLocalRef(env, inputWindowHandle);
-    jstring reasonObj = env->NewStringUTF(reason.string());
+    jstring reasonObj = env->NewStringUTF(reason.c_str());
 
     jlong newTimeout = env->CallLongMethod(mServiceObj,
                 gServiceClassInfo.notifyANR, inputApplicationHandleObj, inputWindowHandleObj,
@@ -1342,7 +1345,7 @@
     NativeInputManager* im = static_cast<NativeInputManager*>(data);
 
     ALOGW("Input channel object '%s' was disposed without first being unregistered with "
-            "the input manager!", inputChannel->getName().string());
+            "the input manager!", inputChannel->getName().c_str());
     im->unregisterInputChannel(env, inputChannel);
 }
 
@@ -1363,9 +1366,9 @@
     status_t status = im->registerInputChannel(
             env, inputChannel, inputWindowHandle, monitor);
     if (status) {
-        String8 message;
-        message.appendFormat("Failed to register input channel.  status=%d", status);
-        jniThrowRuntimeException(env, message.string());
+        std::string message;
+        message += StringPrintf("Failed to register input channel.  status=%d", status);
+        jniThrowRuntimeException(env, message.c_str());
         return;
     }
 
@@ -1390,9 +1393,9 @@
 
     status_t status = im->unregisterInputChannel(env, inputChannel);
     if (status && status != BAD_VALUE) { // ignore already unregistered channel
-        String8 message;
-        message.appendFormat("Failed to unregister input channel.  status=%d", status);
-        jniThrowRuntimeException(env, message.string());
+        std::string message;
+        message += StringPrintf("Failed to unregister input channel.  status=%d", status);
+        jniThrowRuntimeException(env, message.c_str());
     }
 }
 
@@ -1576,9 +1579,9 @@
 static jstring nativeDump(JNIEnv* env, jclass /* clazz */, jlong ptr) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
-    String8 dump;
+    std::string dump;
     im->dump(dump);
-    return env->NewStringUTF(dump.string());
+    return env->NewStringUTF(dump.c_str());
 }
 
 static void nativeMonitor(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
diff --git a/services/core/jni/com_android_server_input_InputWindowHandle.cpp b/services/core/jni/com_android_server_input_InputWindowHandle.cpp
index 2b2a6fa..c13aa38 100644
--- a/services/core/jni/com_android_server_input_InputWindowHandle.cpp
+++ b/services/core/jni/com_android_server_input_InputWindowHandle.cpp
@@ -103,11 +103,11 @@
             gInputWindowHandleClassInfo.name));
     if (nameObj) {
         const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
-        mInfo->name.setTo(nameStr);
+        mInfo->name = nameStr;
         env->ReleaseStringUTFChars(nameObj, nameStr);
         env->DeleteLocalRef(nameObj);
     } else {
-        mInfo->name.setTo("<null>");
+        mInfo->name = "<null>";
     }
 
     mInfo->layoutParamsFlags = env->GetIntField(obj,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 663083c..b979ff4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.BIND_DEVICE_ADMIN;
 import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.USER_OP_SUCCESS;
 import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
 import static android.app.admin.DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED;
 import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE;
@@ -150,6 +151,9 @@
 import android.security.IKeyChainService;
 import android.security.KeyChain;
 import android.security.KeyChain.KeyChainConnection;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
+import android.security.KeyStore;
 import android.service.persistentdata.PersistentDataBlockManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -736,6 +740,7 @@
         private static final String TAG_ORGANIZATION_NAME = "organization-name";
         private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
         private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
+        private static final String TAG_IS_LOGOUT_BUTTON_ENABLED = "is_logout_button_enabled";
 
         final DeviceAdminInfo info;
 
@@ -785,6 +790,7 @@
         boolean requireAutoTime = false; // Can only be set by a device owner.
         boolean forceEphemeralUsers = false; // Can only be set by a device owner.
         boolean isNetworkLoggingEnabled = false; // Can only be set by a device owner.
+        boolean isLogoutButtonEnabled = false; // Can only be set by a device owner.
 
         // one notification after enabling + one more after reboots
         static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 2;
@@ -1102,6 +1108,11 @@
                 out.text(organizationName);
                 out.endTag(null, TAG_ORGANIZATION_NAME);
             }
+            if (isLogoutButtonEnabled) {
+                out.startTag(null, TAG_IS_LOGOUT_BUTTON_ENABLED);
+                out.attribute(null, ATTR_VALUE, Boolean.toString(isLogoutButtonEnabled));
+                out.endTag(null, TAG_IS_LOGOUT_BUTTON_ENABLED);
+            }
         }
 
         void writePackageListToXml(XmlSerializer out, String outerTag,
@@ -1275,6 +1286,9 @@
                     } else {
                         Log.w(LOG_TAG, "Missing text when loading organization name");
                     }
+                } else if (TAG_IS_LOGOUT_BUTTON_ENABLED.equals(tag)) {
+                    isLogoutButtonEnabled = Boolean.parseBoolean(
+                            parser.getAttributeValue(null, ATTR_VALUE));
                 } else {
                     Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -5011,6 +5025,54 @@
     }
 
     @Override
+    public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm,
+            ParcelableKeyGenParameterSpec parcelableKeySpec) {
+        enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                DELEGATION_CERT_INSTALL);
+        final KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec();
+        if (TextUtils.isEmpty(keySpec.getKeystoreAlias())) {
+            throw new IllegalArgumentException("Empty alias provided.");
+        }
+        // As the caller will be granted access to the key, ensure no UID was specified, as
+        // it will not have the desired effect.
+        if (keySpec.getUid() != KeyStore.UID_SELF) {
+            Log.e(LOG_TAG, "Only the caller can be granted access to the generated keypair.");
+            return false;
+        }
+        final int callingUid = mInjector.binderGetCallingUid();
+
+        final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            try (KeyChainConnection keyChainConnection =
+                    KeyChain.bindAsUser(mContext, userHandle)) {
+                IKeyChainService keyChain = keyChainConnection.getService();
+                final boolean generationResult = keyChain.generateKeyPair(algorithm, parcelableKeySpec);
+                if (!generationResult) {
+                    Log.e(LOG_TAG, "KeyChain failed to generate a keypair.");
+                    return false;
+                }
+
+                // Set a grant for the caller here so that when the client calls
+                // requestPrivateKey, it will be able to get the key from Keystore.
+                // Note the use of the calling  UID, since the request for the private
+                // key will come from the client's process, so the grant has to be for
+                // that UID.
+                keyChain.setGrant(callingUid, keySpec.getKeystoreAlias(), true);
+                return true;
+            }
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "KeyChain error while generating a keypair", e);
+        } catch (InterruptedException e) {
+            Log.w(LOG_TAG, "Interrupted while generating keypair", e);
+            Thread.currentThread().interrupt();
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
+        }
+        return false;
+    }
+
+    @Override
     public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias,
             final IBinder response) {
         // Caller UID needs to be trusted, so we restrict this method to SYSTEM_UID callers.
@@ -8393,6 +8455,90 @@
     }
 
     @Override
+    public boolean stopUser(ComponentName who, UserHandle userHandle) {
+        Preconditions.checkNotNull(who, "ComponentName is null");
+
+        synchronized (this) {
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+        }
+
+        final int userId = userHandle.getIdentifier();
+        if (isManagedProfile(userId)) {
+            Log.w(LOG_TAG, "Managed profile cannot be stopped");
+            return false;
+        }
+
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            return mInjector.getIActivityManager().stopUser(userId, true /*force*/, null)
+                    == USER_OP_SUCCESS;
+        } catch (RemoteException e) {
+            // Same process, should not happen.
+            return false;
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
+        }
+    }
+
+    @Override
+    public boolean logoutUser(ComponentName who) {
+        Preconditions.checkNotNull(who, "ComponentName is null");
+
+        final int callingUserId = mInjector.userHandleGetCallingUserId();
+        synchronized (this) {
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            if (!isUserAffiliatedWithDeviceLocked(callingUserId)) {
+                throw new SecurityException("Admin " + who +
+                        " is neither the device owner or affiliated user's profile owner.");
+            }
+        }
+
+        if (isManagedProfile(callingUserId)) {
+            Log.w(LOG_TAG, "Managed profile cannot be logout");
+            return false;
+        }
+
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            if (!mInjector.getIActivityManager().switchUser(UserHandle.USER_SYSTEM)) {
+                Log.w(LOG_TAG, "Failed to switch to primary user");
+                return false;
+            }
+            return mInjector.getIActivityManager().stopUser(callingUserId, true /*force*/, null)
+                    == USER_OP_SUCCESS;
+        } catch (RemoteException e) {
+            // Same process, should not happen.
+            return false;
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
+        }
+    }
+
+    @Override
+    public List<UserHandle> getSecondaryUsers(ComponentName who) {
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        synchronized (this) {
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+        }
+
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            final List<UserInfo> userInfos = mInjector.getUserManager().getUsers(true
+                    /*excludeDying*/);
+            final List<UserHandle> userHandles = new ArrayList<>();
+            for (UserInfo userInfo : userInfos) {
+                UserHandle userHandle = userInfo.getUserHandle();
+                if (!userHandle.isSystem() && !isManagedProfile(userHandle.getIdentifier())) {
+                    userHandles.add(userInfo.getUserHandle());
+                }
+            }
+            return userHandles;
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
+        }
+    }
+
+    @Override
     public Bundle getApplicationRestrictions(ComponentName who, String callerPackage,
             String packageName) {
         enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
@@ -11433,4 +11579,37 @@
         }
         return false;
     }
+
+    @Override
+    public synchronized void setLogoutButtonEnabled(ComponentName admin, boolean enabled) {
+        if (!mHasFeature) {
+            return;
+        }
+        Preconditions.checkNotNull(admin);
+        getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+
+        if (enabled == isLogoutButtonEnabledInternalLocked()) {
+            // already in the requested state
+            return;
+        }
+        ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+        deviceOwner.isLogoutButtonEnabled = enabled;
+        saveSettingsLocked(mInjector.userHandleGetCallingUserId());
+    }
+
+    @Override
+    public boolean isLogoutButtonEnabled() {
+        if (!mHasFeature) {
+            return false;
+        }
+        synchronized (this) {
+            return isLogoutButtonEnabledInternalLocked();
+        }
+    }
+
+    private boolean isLogoutButtonEnabledInternalLocked() {
+        ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
+        return (deviceOwner != null) && deviceOwner.isLogoutButtonEnabled;
+    }
+
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 33f4e34..4c994b9 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -215,6 +215,9 @@
             "com.android.server.timezone.RulesManagerService$Lifecycle";
     private static final String IOT_SERVICE_CLASS =
             "com.google.android.things.services.IoTSystemService";
+    private static final String SLICE_MANAGER_SERVICE_CLASS =
+            "com.android.server.slice.SliceManagerService$Lifecycle";
+
     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
 
     private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file";
@@ -730,6 +733,7 @@
         boolean disableVrManager = SystemProperties.getBoolean("config.disable_vrmanager", false);
         boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
                 false);
+        boolean disableSlices = SystemProperties.getBoolean("config.disable_slices", false);
         boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false);
 
         boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
@@ -1523,6 +1527,12 @@
             }
         }
 
+        if (!disableSlices) {
+            traceBeginAndSlog("StartSliceManagerService");
+            mSystemServiceManager.startService(SLICE_MANAGER_SERVICE_CLASS);
+            traceEnd();
+        }
+
         if (!disableCameraService) {
             traceBeginAndSlog("StartCameraServiceProxy");
             mSystemServiceManager.startService(CameraServiceProxy.class);
diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
index 1925c39..6cf4fa9a 100644
--- a/services/net/java/android/net/ip/ConnectivityPacketTracker.java
+++ b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
@@ -19,7 +19,7 @@
 import static android.system.OsConstants.*;
 
 import android.net.NetworkUtils;
-import android.net.util.BlockingSocketReader;
+import android.net.util.PacketReader;
 import android.net.util.ConnectivityPacketSummary;
 import android.os.Handler;
 import android.system.ErrnoException;
@@ -65,7 +65,7 @@
 
     private final String mTag;
     private final LocalLog mLog;
-    private final BlockingSocketReader mPacketListener;
+    private final PacketReader mPacketListener;
     private boolean mRunning;
     private String mDisplayName;
 
@@ -101,7 +101,7 @@
         mDisplayName = null;
     }
 
-    private final class PacketListener extends BlockingSocketReader {
+    private final class PacketListener extends PacketReader {
         private final int mIfIndex;
         private final byte mHwAddr[];
 
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 70983c8..b305b33 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -163,10 +163,10 @@
     // TODO: Find an lighter weight approach.
     private class LoggingCallbackWrapper extends Callback {
         private static final String PREFIX = "INVOKE ";
-        private Callback mCallback;
+        private final Callback mCallback;
 
         public LoggingCallbackWrapper(Callback callback) {
-            mCallback = callback;
+            mCallback = (callback != null) ? callback : new Callback();
         }
 
         private void log(String msg) {
@@ -815,6 +815,15 @@
         pw.println(Objects.toString(provisioningConfig, "N/A"));
         pw.decreaseIndent();
 
+        final IpReachabilityMonitor iprm = mIpReachabilityMonitor;
+        if (iprm != null) {
+            pw.println();
+            pw.println(mTag + " current IpReachabilityMonitor state:");
+            pw.increaseIndent();
+            iprm.dump(pw);
+            pw.decreaseIndent();
+        }
+
         pw.println();
         pw.println(mTag + " StateMachine dump:");
         pw.increaseIndent();
@@ -1237,6 +1246,7 @@
             mIpReachabilityMonitor = new IpReachabilityMonitor(
                     mContext,
                     mInterfaceName,
+                    getHandler(),
                     mLog,
                     new IpReachabilityMonitor.Callback() {
                         @Override
@@ -1273,6 +1283,7 @@
             stopAllIP();
 
             resetLinkProperties();
+            mCallback.onLinkPropertiesChange(new LinkProperties(mLinkProperties));
             if (mStartTimeMillis > 0) {
                 recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE);
                 mStartTimeMillis = 0;
diff --git a/services/net/java/android/net/ip/IpNeighborMonitor.java b/services/net/java/android/net/ip/IpNeighborMonitor.java
new file mode 100644
index 0000000..6807334
--- /dev/null
+++ b/services/net/java/android/net/ip/IpNeighborMonitor.java
@@ -0,0 +1,236 @@
+/*
+ * 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.net.ip;
+
+import android.net.netlink.NetlinkConstants;
+import android.net.netlink.NetlinkErrorMessage;
+import android.net.netlink.NetlinkMessage;
+import android.net.netlink.NetlinkSocket;
+import android.net.netlink.RtNetlinkNeighborMessage;
+import android.net.netlink.StructNdMsg;
+import android.net.netlink.StructNlMsgHdr;
+import android.net.util.PacketReader;
+import android.net.util.SharedLog;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.system.ErrnoException;
+import android.system.NetlinkSocketAddress;
+import android.system.Os;
+import android.system.OsConstants;
+import android.util.Log;
+
+import com.android.internal.util.BitUtils;
+
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+
+import java.io.FileDescriptor;
+import java.net.InetAddress;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.StringJoiner;
+
+
+/**
+ * IpNeighborMonitor.
+ *
+ * Monitors the kernel rtnetlink neighbor notifications and presents to callers
+ * NeighborEvents describing each event. Callers can provide a consumer instance
+ * to both filter (e.g. by interface index and IP address) and handle the
+ * generated NeighborEvents.
+ *
+ * @hide
+ */
+public class IpNeighborMonitor extends PacketReader {
+    private static final String TAG = IpNeighborMonitor.class.getSimpleName();
+    private static final boolean DBG = false;
+    private static final boolean VDBG = false;
+
+    /**
+     * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
+     * for the given IP address on the specified interface index.
+     *
+     * @return 0 if the request was successfully passed to the kernel; otherwise return
+     *         a non-zero error code.
+     */
+    public static int startKernelNeighborProbe(int ifIndex, InetAddress ip) {
+        final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex;
+        if (DBG) { Log.d(TAG, msgSnippet); }
+
+        final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
+                1, ip, StructNdMsg.NUD_PROBE, ifIndex, null);
+
+        try {
+            NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_ROUTE, msg);
+        } catch (ErrnoException e) {
+            Log.e(TAG, "Error " + msgSnippet + ": " + e);
+            return -e.errno;
+        }
+
+        return 0;
+    }
+
+    public static class NeighborEvent {
+        final long elapsedMs;
+        final short msgType;
+        final int ifindex;
+        final InetAddress ip;
+        final short nudState;
+        final byte[] linkLayerAddr;
+
+        public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip,
+                short nudState, byte[] linkLayerAddr) {
+            this.elapsedMs = elapsedMs;
+            this.msgType = msgType;
+            this.ifindex = ifindex;
+            this.ip = ip;
+            this.nudState = nudState;
+            this.linkLayerAddr = linkLayerAddr;
+        }
+
+        boolean isConnected() {
+            return (msgType != NetlinkConstants.RTM_DELNEIGH) &&
+                   StructNdMsg.isNudStateConnected(nudState);
+        }
+
+        boolean isValid() {
+            return (msgType != NetlinkConstants.RTM_DELNEIGH) &&
+                   StructNdMsg.isNudStateValid(nudState);
+        }
+
+        @Override
+        public String toString() {
+            final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}");
+            return j.add("@" + elapsedMs)
+                    .add(NetlinkConstants.stringForNlMsgType(msgType))
+                    .add("if=" + ifindex)
+                    .add(ip.getHostAddress())
+                    .add(StructNdMsg.stringForNudState(nudState))
+                    .add("[" + NetlinkConstants.hexify(linkLayerAddr) + "]")
+                    .toString();
+        }
+    }
+
+    public interface NeighborEventConsumer {
+        // Every neighbor event received on the netlink socket is passed in
+        // here. Subclasses should filter for events of interest.
+        public void accept(NeighborEvent event);
+    }
+
+    private final SharedLog mLog;
+    private final NeighborEventConsumer mConsumer;
+
+    public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) {
+        super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE);
+        mLog = log.forSubComponent(TAG);
+        mConsumer = (cb != null) ? cb : (event) -> { /* discard */ };
+    }
+
+    @Override
+    protected FileDescriptor createFd() {
+        FileDescriptor fd = null;
+
+        try {
+            fd = NetlinkSocket.forProto(OsConstants.NETLINK_ROUTE);
+            Os.bind(fd, (SocketAddress)(new NetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH)));
+            Os.connect(fd, (SocketAddress)(new NetlinkSocketAddress(0, 0)));
+
+            if (VDBG) {
+                final NetlinkSocketAddress nlAddr = (NetlinkSocketAddress) Os.getsockname(fd);
+                Log.d(TAG, "bound to sockaddr_nl{"
+                        + BitUtils.uint32(nlAddr.getPortId()) + ", "
+                        + nlAddr.getGroupsMask()
+                        + "}");
+            }
+        } catch (ErrnoException|SocketException e) {
+            logError("Failed to create rtnetlink socket", e);
+            IoUtils.closeQuietly(fd);
+            return null;
+        }
+
+        return fd;
+    }
+
+    @Override
+    protected void handlePacket(byte[] recvbuf, int length) {
+        final long whenMs = SystemClock.elapsedRealtime();
+
+        final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length);
+        byteBuffer.order(ByteOrder.nativeOrder());
+
+        parseNetlinkMessageBuffer(byteBuffer, whenMs);
+    }
+
+    private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) {
+        while (byteBuffer.remaining() > 0) {
+            final int position = byteBuffer.position();
+            final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
+            if (nlMsg == null || nlMsg.getHeader() == null) {
+                byteBuffer.position(position);
+                mLog.e("unparsable netlink msg: " + NetlinkConstants.hexify(byteBuffer));
+                break;
+            }
+
+            final int srcPortId = nlMsg.getHeader().nlmsg_pid;
+            if (srcPortId !=  0) {
+                mLog.e("non-kernel source portId: " + BitUtils.uint32(srcPortId));
+                break;
+            }
+
+            if (nlMsg instanceof NetlinkErrorMessage) {
+                mLog.e("netlink error: " + nlMsg);
+                continue;
+            } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
+                mLog.i("non-rtnetlink neighbor msg: " + nlMsg);
+                continue;
+            }
+
+            evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs);
+        }
+    }
+
+    private void evaluateRtNetlinkNeighborMessage(
+            RtNetlinkNeighborMessage neighMsg, long whenMs) {
+        final short msgType = neighMsg.getHeader().nlmsg_type;
+        final StructNdMsg ndMsg = neighMsg.getNdHeader();
+        if (ndMsg == null) {
+            mLog.e("RtNetlinkNeighborMessage without ND message header!");
+            return;
+        }
+
+        final int ifindex = ndMsg.ndm_ifindex;
+        final InetAddress destination = neighMsg.getDestination();
+        final short nudState =
+                (msgType == NetlinkConstants.RTM_DELNEIGH)
+                ? StructNdMsg.NUD_NONE
+                : ndMsg.ndm_state;
+
+        final NeighborEvent event = new NeighborEvent(
+                whenMs, msgType, ifindex, destination, nudState, neighMsg.getLinkLayerAddress());
+
+        if (VDBG) {
+            Log.d(TAG, neighMsg.toString());
+        }
+        if (DBG) {
+            Log.d(TAG, event.toString());
+        }
+
+        mConsumer.accept(event);
+    }
+}
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index 714b35a..b31ffbb 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -22,30 +22,27 @@
 import android.net.LinkProperties.ProvisioningChange;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
+import android.net.ip.IpNeighborMonitor.NeighborEvent;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.IpReachabilityEvent;
-import android.net.netlink.NetlinkConstants;
-import android.net.netlink.NetlinkErrorMessage;
-import android.net.netlink.NetlinkMessage;
-import android.net.netlink.NetlinkSocket;
-import android.net.netlink.RtNetlinkNeighborMessage;
 import android.net.netlink.StructNdMsg;
-import android.net.netlink.StructNdaCacheInfo;
-import android.net.netlink.StructNlMsgHdr;
 import android.net.util.MultinetworkPolicyTracker;
 import android.net.util.SharedLog;
+import android.os.Handler;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
 import android.os.SystemClock;
 import android.system.ErrnoException;
-import android.system.NetlinkSocketAddress;
 import android.system.OsConstants;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.DumpUtils.Dump;
 
 import java.io.InterruptedIOException;
+import java.io.PrintWriter;
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
@@ -134,6 +131,8 @@
  *          state it may be best for the link to disconnect completely and
  *          reconnect afresh.
  *
+ * Accessing an instance of this class from multiple threads is NOT safe.
+ *
  * @hide
  */
 public class IpReachabilityMonitor {
@@ -169,64 +168,33 @@
         }
     }
 
-    private final Object mLock = new Object();
     private final String mInterfaceName;
     private final int mInterfaceIndex;
+    private final IpNeighborMonitor mIpNeighborMonitor;
     private final SharedLog mLog;
     private final Callback mCallback;
     private final Dependencies mDependencies;
     private final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
-    private final NetlinkSocketObserver mNetlinkSocketObserver;
-    private final Thread mObserverThread;
     private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
-    @GuardedBy("mLock")
     private LinkProperties mLinkProperties = new LinkProperties();
-    // TODO: consider a map to a private NeighborState class holding more
-    // information than a single NUD state entry.
-    @GuardedBy("mLock")
-    private Map<InetAddress, Short> mIpWatchList = new HashMap<>();
-    @GuardedBy("mLock")
-    private int mIpWatchListVersion;
-    private volatile boolean mRunning;
+    private Map<InetAddress, NeighborEvent> mNeighborWatchList = new HashMap<>();
     // Time in milliseconds of the last forced probe request.
     private volatile long mLastProbeTimeMs;
 
-    /**
-     * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND)
-     * for the given IP address on the specified interface index.
-     *
-     * @return 0 if the request was successfully passed to the kernel; otherwise return
-     *         a non-zero error code.
-     */
-    private static int probeNeighbor(int ifIndex, InetAddress ip) {
-        final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex;
-        if (DBG) { Log.d(TAG, msgSnippet); }
-
-        final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
-                1, ip, StructNdMsg.NUD_PROBE, ifIndex, null);
-
-        try {
-            NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_ROUTE, msg);
-        } catch (ErrnoException e) {
-            Log.e(TAG, "Error " + msgSnippet + ": " + e);
-            return -e.errno;
-        }
-
-        return 0;
+    public IpReachabilityMonitor(
+            Context context, String ifName, Handler h, SharedLog log, Callback callback) {
+        this(context, ifName, h, log, callback, null);
     }
 
-    public IpReachabilityMonitor(Context context, String ifName, SharedLog log, Callback callback) {
-        this(context, ifName, log, callback, null);
-    }
-
-    public IpReachabilityMonitor(Context context, String ifName, SharedLog log, Callback callback,
+    public IpReachabilityMonitor(
+            Context context, String ifName, Handler h, SharedLog log, Callback callback,
             MultinetworkPolicyTracker tracker) {
-        this(ifName, getInterfaceIndex(ifName), log, callback, tracker,
+        this(ifName, getInterfaceIndex(ifName), h, log, callback, tracker,
                 Dependencies.makeDefault(context, ifName));
     }
 
     @VisibleForTesting
-    IpReachabilityMonitor(String ifName, int ifIndex, SharedLog log, Callback callback,
+    IpReachabilityMonitor(String ifName, int ifIndex, Handler h, SharedLog log, Callback callback,
             MultinetworkPolicyTracker tracker, Dependencies dependencies) {
         mInterfaceName = ifName;
         mLog = log.forSubComponent(TAG);
@@ -234,47 +202,56 @@
         mMultinetworkPolicyTracker = tracker;
         mInterfaceIndex = ifIndex;
         mDependencies = dependencies;
-        mNetlinkSocketObserver = new NetlinkSocketObserver();
-        mObserverThread = new Thread(mNetlinkSocketObserver);
-        mObserverThread.start();
+
+        mIpNeighborMonitor = new IpNeighborMonitor(h, mLog,
+                (NeighborEvent event) -> {
+                    if (mInterfaceIndex != event.ifindex) return;
+                    if (!mNeighborWatchList.containsKey(event.ip)) return;
+
+                    final NeighborEvent prev = mNeighborWatchList.put(event.ip, event);
+
+                    // TODO: Consider what to do with other states that are not within
+                    // NeighborEvent#isValid() (i.e. NUD_NONE, NUD_INCOMPLETE).
+                    if (event.nudState == StructNdMsg.NUD_FAILED) {
+                        mLog.w("ALERT neighbor went from: " + prev + " to: " + event);
+                        handleNeighborLost(event);
+                    }
+                });
+        mIpNeighborMonitor.start();
     }
 
     public void stop() {
-        mRunning = false;
+        mIpNeighborMonitor.stop();
         clearLinkProperties();
-        mNetlinkSocketObserver.clearNetlinkSocket();
     }
 
-    // TODO: add a public dump() method that can be called during a bug report.
+    public void dump(PrintWriter pw) {
+        DumpUtils.dumpAsync(
+                mIpNeighborMonitor.getHandler(),
+                new Dump() {
+                    @Override
+                    public void dump(PrintWriter pw, String prefix) {
+                        pw.println(describeWatchList("\n"));
+                    }
+                },
+                pw, "", 1000);
+    }
 
-    private String describeWatchList() {
-        final String delimiter = ", ";
-        StringBuilder sb = new StringBuilder();
-        synchronized (mLock) {
-            sb.append("iface{" + mInterfaceName + "/" + mInterfaceIndex + "}, ");
-            sb.append("v{" + mIpWatchListVersion + "}, ");
-            sb.append("ntable=[");
-            boolean firstTime = true;
-            for (Map.Entry<InetAddress, Short> entry : mIpWatchList.entrySet()) {
-                if (firstTime) {
-                    firstTime = false;
-                } else {
-                    sb.append(delimiter);
-                }
-                sb.append(entry.getKey().getHostAddress() + "/" +
-                        StructNdMsg.stringForNudState(entry.getValue()));
-            }
-            sb.append("]");
+    private String describeWatchList() { return describeWatchList(" "); }
+
+    private String describeWatchList(String sep) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("iface{" + mInterfaceName + "/" + mInterfaceIndex + "}," + sep);
+        sb.append("ntable=[" + sep);
+        String delimiter = "";
+        for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) {
+            sb.append(delimiter).append(entry.getKey().getHostAddress() + "/" + entry.getValue());
+            delimiter = "," + sep;
         }
+        sb.append("]");
         return sb.toString();
     }
 
-    private boolean isWatching(InetAddress ip) {
-        synchronized (mLock) {
-            return mRunning && mIpWatchList.containsKey(ip);
-        }
-    }
-
     private static boolean isOnLink(List<RouteInfo> routes, InetAddress ip) {
         for (RouteInfo route : routes) {
             if (!route.hasGateway() && route.matches(ip)) {
@@ -284,13 +261,6 @@
         return false;
     }
 
-    private short getNeighborStateLocked(InetAddress ip) {
-        if (mIpWatchList.containsKey(ip)) {
-            return mIpWatchList.get(ip);
-        }
-        return StructNdMsg.NUD_NONE;
-    }
-
     public void updateLinkProperties(LinkProperties lp) {
         if (!mInterfaceName.equals(lp.getInterfaceName())) {
             // TODO: figure out whether / how to cope with interface changes.
@@ -299,70 +269,63 @@
             return;
         }
 
-        synchronized (mLock) {
-            mLinkProperties = new LinkProperties(lp);
-            Map<InetAddress, Short> newIpWatchList = new HashMap<>();
+        mLinkProperties = new LinkProperties(lp);
+        Map<InetAddress, NeighborEvent> newNeighborWatchList = new HashMap<>();
 
-            final List<RouteInfo> routes = mLinkProperties.getRoutes();
-            for (RouteInfo route : routes) {
-                if (route.hasGateway()) {
-                    InetAddress gw = route.getGateway();
-                    if (isOnLink(routes, gw)) {
-                        newIpWatchList.put(gw, getNeighborStateLocked(gw));
-                    }
+        final List<RouteInfo> routes = mLinkProperties.getRoutes();
+        for (RouteInfo route : routes) {
+            if (route.hasGateway()) {
+                InetAddress gw = route.getGateway();
+                if (isOnLink(routes, gw)) {
+                    newNeighborWatchList.put(gw, mNeighborWatchList.getOrDefault(gw, null));
                 }
             }
-
-            for (InetAddress nameserver : lp.getDnsServers()) {
-                if (isOnLink(routes, nameserver)) {
-                    newIpWatchList.put(nameserver, getNeighborStateLocked(nameserver));
-                }
-            }
-
-            mIpWatchList = newIpWatchList;
-            mIpWatchListVersion++;
         }
+
+        for (InetAddress dns : lp.getDnsServers()) {
+            if (isOnLink(routes, dns)) {
+                newNeighborWatchList.put(dns, mNeighborWatchList.getOrDefault(dns, null));
+            }
+        }
+
+        mNeighborWatchList = newNeighborWatchList;
         if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); }
     }
 
     public void clearLinkProperties() {
-        synchronized (mLock) {
-            mLinkProperties.clear();
-            mIpWatchList.clear();
-            mIpWatchListVersion++;
-        }
+        mLinkProperties.clear();
+        mNeighborWatchList.clear();
         if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); }
     }
 
-    private void handleNeighborLost(String msg) {
+    private void handleNeighborLost(NeighborEvent event) {
+        final LinkProperties whatIfLp = new LinkProperties(mLinkProperties);
+
         InetAddress ip = null;
-        final ProvisioningChange delta;
-        synchronized (mLock) {
-            LinkProperties whatIfLp = new LinkProperties(mLinkProperties);
+        for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) {
+            // TODO: Consider using NeighborEvent#isValid() here; it's more
+            // strict but may interact badly if other entries are somehow in
+            // NUD_INCOMPLETE (say, during network attach).
+            if (entry.getValue().nudState != StructNdMsg.NUD_FAILED) continue;
 
-            for (Map.Entry<InetAddress, Short> entry : mIpWatchList.entrySet()) {
-                if (entry.getValue() != StructNdMsg.NUD_FAILED) {
-                    continue;
-                }
-
-                ip = entry.getKey();
-                for (RouteInfo route : mLinkProperties.getRoutes()) {
-                    if (ip.equals(route.getGateway())) {
-                        whatIfLp.removeRoute(route);
-                    }
-                }
-
-                if (avoidingBadLinks() || !(ip instanceof Inet6Address)) {
-                    // We should do this unconditionally, but alas we cannot: b/31827713.
-                    whatIfLp.removeDnsServer(ip);
+            ip = entry.getKey();
+            for (RouteInfo route : mLinkProperties.getRoutes()) {
+                if (ip.equals(route.getGateway())) {
+                    whatIfLp.removeRoute(route);
                 }
             }
 
-            delta = LinkProperties.compareProvisioning(mLinkProperties, whatIfLp);
+            if (avoidingBadLinks() || !(ip instanceof Inet6Address)) {
+                // We should do this unconditionally, but alas we cannot: b/31827713.
+                whatIfLp.removeDnsServer(ip);
+            }
         }
 
+        final ProvisioningChange delta = LinkProperties.compareProvisioning(
+                mLinkProperties, whatIfLp);
+
         if (delta == ProvisioningChange.LOST_PROVISIONING) {
-            final String logMsg = "FAILURE: LOST_PROVISIONING, " + msg;
+            final String logMsg = "FAILURE: LOST_PROVISIONING, " + event;
             Log.w(TAG, logMsg);
             if (mCallback != null) {
                 // TODO: remove |ip| when the callback signature no longer has
@@ -378,12 +341,9 @@
     }
 
     public void probeAll() {
-        final List<InetAddress> ipProbeList;
-        synchronized (mLock) {
-            ipProbeList = new ArrayList<>(mIpWatchList.keySet());
-        }
+        final List<InetAddress> ipProbeList = new ArrayList<>(mNeighborWatchList.keySet());
 
-        if (!ipProbeList.isEmpty() && mRunning) {
+        if (!ipProbeList.isEmpty()) {
             // Keep the CPU awake long enough to allow all ARP/ND
             // probes a reasonable chance at success. See b/23197666.
             //
@@ -394,13 +354,10 @@
         }
 
         for (InetAddress target : ipProbeList) {
-            if (!mRunning) {
-                break;
-            }
-            final int returnValue = probeNeighbor(mInterfaceIndex, target);
+            final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceIndex, target);
             mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)",
-                     target.getHostAddress(), returnValue));
-            logEvent(IpReachabilityEvent.PROBE, returnValue);
+                     target.getHostAddress(), rval));
+            logEvent(IpReachabilityEvent.PROBE, rval);
         }
         mLastProbeTimeMs = SystemClock.elapsedRealtime();
     }
@@ -446,153 +403,4 @@
         int eventType = IpReachabilityEvent.nudFailureEventType(isFromProbe, isProvisioningLost);
         mMetricsLog.log(mInterfaceName, new IpReachabilityEvent(eventType));
     }
-
-    // TODO: simplify the number of objects by making this extend Thread.
-    private final class NetlinkSocketObserver implements Runnable {
-        private NetlinkSocket mSocket;
-
-        @Override
-        public void run() {
-            if (VDBG) { Log.d(TAG, "Starting observing thread."); }
-            mRunning = true;
-
-            try {
-                setupNetlinkSocket();
-            } catch (ErrnoException | SocketException e) {
-                Log.e(TAG, "Failed to suitably initialize a netlink socket", e);
-                mRunning = false;
-            }
-
-            while (mRunning) {
-                final ByteBuffer byteBuffer;
-                try {
-                    byteBuffer = recvKernelReply();
-                } catch (ErrnoException e) {
-                    if (mRunning) { Log.w(TAG, "ErrnoException: ", e); }
-                    break;
-                }
-                final long whenMs = SystemClock.elapsedRealtime();
-                if (byteBuffer == null) {
-                    continue;
-                }
-                parseNetlinkMessageBuffer(byteBuffer, whenMs);
-            }
-
-            clearNetlinkSocket();
-
-            mRunning = false; // Not a no-op when ErrnoException happened.
-            if (VDBG) { Log.d(TAG, "Finishing observing thread."); }
-        }
-
-        private void clearNetlinkSocket() {
-            if (mSocket != null) {
-                mSocket.close();
-            }
-        }
-
-            // TODO: Refactor the main loop to recreate the socket upon recoverable errors.
-        private void setupNetlinkSocket() throws ErrnoException, SocketException {
-            clearNetlinkSocket();
-            mSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
-
-            final NetlinkSocketAddress listenAddr = new NetlinkSocketAddress(
-                    0, OsConstants.RTMGRP_NEIGH);
-            mSocket.bind(listenAddr);
-
-            if (VDBG) {
-                final NetlinkSocketAddress nlAddr = mSocket.getLocalAddress();
-                Log.d(TAG, "bound to sockaddr_nl{"
-                        + ((long) (nlAddr.getPortId() & 0xffffffff)) + ", "
-                        + nlAddr.getGroupsMask()
-                        + "}");
-            }
-        }
-
-        private ByteBuffer recvKernelReply() throws ErrnoException {
-            try {
-                return mSocket.recvMessage(0);
-            } catch (InterruptedIOException e) {
-                // Interruption or other error, e.g. another thread closed our file descriptor.
-            } catch (ErrnoException e) {
-                if (e.errno != OsConstants.EAGAIN) {
-                    throw e;
-                }
-            }
-            return null;
-        }
-
-        private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) {
-            while (byteBuffer.remaining() > 0) {
-                final int position = byteBuffer.position();
-                final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer);
-                if (nlMsg == null || nlMsg.getHeader() == null) {
-                    byteBuffer.position(position);
-                    Log.e(TAG, "unparsable netlink msg: " + NetlinkConstants.hexify(byteBuffer));
-                    break;
-                }
-
-                final int srcPortId = nlMsg.getHeader().nlmsg_pid;
-                if (srcPortId !=  0) {
-                    Log.e(TAG, "non-kernel source portId: " + ((long) (srcPortId & 0xffffffff)));
-                    break;
-                }
-
-                if (nlMsg instanceof NetlinkErrorMessage) {
-                    Log.e(TAG, "netlink error: " + nlMsg);
-                    continue;
-                } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) {
-                    if (DBG) {
-                        Log.d(TAG, "non-rtnetlink neighbor msg: " + nlMsg);
-                    }
-                    continue;
-                }
-
-                evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs);
-            }
-        }
-
-        private void evaluateRtNetlinkNeighborMessage(
-                RtNetlinkNeighborMessage neighMsg, long whenMs) {
-            final StructNdMsg ndMsg = neighMsg.getNdHeader();
-            if (ndMsg == null || ndMsg.ndm_ifindex != mInterfaceIndex) {
-                return;
-            }
-
-            final InetAddress destination = neighMsg.getDestination();
-            if (!isWatching(destination)) {
-                return;
-            }
-
-            final short msgType = neighMsg.getHeader().nlmsg_type;
-            final short nudState = ndMsg.ndm_state;
-            final String eventMsg = "NeighborEvent{"
-                    + "elapsedMs=" + whenMs + ", "
-                    + destination.getHostAddress() + ", "
-                    + "[" + NetlinkConstants.hexify(neighMsg.getLinkLayerAddress()) + "], "
-                    + NetlinkConstants.stringForNlMsgType(msgType) + ", "
-                    + StructNdMsg.stringForNudState(nudState)
-                    + "}";
-
-            if (VDBG) {
-                Log.d(TAG, neighMsg.toString());
-            } else if (DBG) {
-                Log.d(TAG, eventMsg);
-            }
-
-            synchronized (mLock) {
-                if (mIpWatchList.containsKey(destination)) {
-                    final short value =
-                            (msgType == NetlinkConstants.RTM_DELNEIGH)
-                            ? StructNdMsg.NUD_NONE
-                            : nudState;
-                    mIpWatchList.put(destination, value);
-                }
-            }
-
-            if (nudState == StructNdMsg.NUD_FAILED) {
-                Log.w(TAG, "ALERT: " + eventMsg);
-                handleNeighborLost(eventMsg);
-            }
-        }
-    }
 }
diff --git a/services/net/java/android/net/netlink/NetlinkSocket.java b/services/net/java/android/net/netlink/NetlinkSocket.java
index f5f211d..5af3c29 100644
--- a/services/net/java/android/net/netlink/NetlinkSocket.java
+++ b/services/net/java/android/net/netlink/NetlinkSocket.java
@@ -16,16 +16,24 @@
 
 package android.net.netlink;
 
+import static android.system.OsConstants.AF_NETLINK;
+import static android.system.OsConstants.EIO;
+import static android.system.OsConstants.EPROTO;
+import static android.system.OsConstants.ETIMEDOUT;
+import static android.system.OsConstants.SO_RCVBUF;
+import static android.system.OsConstants.SO_RCVTIMEO;
+import static android.system.OsConstants.SO_SNDTIMEO;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOL_SOCKET;
+
 import android.system.ErrnoException;
 import android.system.NetlinkSocketAddress;
 import android.system.Os;
-import android.system.OsConstants;
 import android.system.StructTimeval;
 import android.util.Log;
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
 
-import java.io.Closeable;
 import java.io.FileDescriptor;
 import java.io.InterruptedIOException;
 import java.net.SocketAddress;
@@ -37,28 +45,27 @@
 /**
  * NetlinkSocket
  *
- * A small wrapper class to assist with AF_NETLINK socket operations.
+ * A small static class to assist with AF_NETLINK socket operations.
  *
  * @hide
  */
-public class NetlinkSocket implements Closeable {
+public class NetlinkSocket {
     private static final String TAG = "NetlinkSocket";
-    private static final int SOCKET_RECV_BUFSIZE = 64 * 1024;
-    private static final int DEFAULT_RECV_BUFSIZE = 8 * 1024;
 
-    final private FileDescriptor mDescriptor;
-    private NetlinkSocketAddress mAddr;
-    private long mLastRecvTimeoutMs;
-    private long mLastSendTimeoutMs;
+    public static final int DEFAULT_RECV_BUFSIZE = 8 * 1024;
+    public static final int SOCKET_RECV_BUFSIZE = 64 * 1024;
 
     public static void sendOneShotKernelMessage(int nlProto, byte[] msg) throws ErrnoException {
         final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage";
+        final long IO_TIMEOUT = 300L;
 
-        try (NetlinkSocket nlSocket = new NetlinkSocket(nlProto)) {
-            final long IO_TIMEOUT = 300L;
-            nlSocket.connectToKernel();
-            nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT);
-            final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT);
+        FileDescriptor fd;
+
+        try {
+            fd = forProto(nlProto);
+            connectToKernel(fd);
+            sendMessage(fd, msg, 0, msg.length, IO_TIMEOUT);
+            final ByteBuffer bytes = recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT);
             // recvMessage() guaranteed to not return null if it did not throw.
             final NetlinkMessage response = NetlinkMessage.parse(bytes);
             if (response != null && response instanceof NetlinkErrorMessage &&
@@ -81,61 +88,30 @@
                     errmsg = response.toString();
                 }
                 Log.e(TAG, errPrefix + ", errmsg=" + errmsg);
-                throw new ErrnoException(errmsg, OsConstants.EPROTO);
+                throw new ErrnoException(errmsg, EPROTO);
             }
         } catch (InterruptedIOException e) {
             Log.e(TAG, errPrefix, e);
-            throw new ErrnoException(errPrefix, OsConstants.ETIMEDOUT, e);
+            throw new ErrnoException(errPrefix, ETIMEDOUT, e);
         } catch (SocketException e) {
             Log.e(TAG, errPrefix, e);
-            throw new ErrnoException(errPrefix, OsConstants.EIO, e);
+            throw new ErrnoException(errPrefix, EIO, e);
         }
+
+        IoUtils.closeQuietly(fd);
     }
 
-    public NetlinkSocket(int nlProto) throws ErrnoException {
-        mDescriptor = Os.socket(
-                OsConstants.AF_NETLINK, OsConstants.SOCK_DGRAM, nlProto);
-
-        Os.setsockoptInt(
-                mDescriptor, OsConstants.SOL_SOCKET,
-                OsConstants.SO_RCVBUF, SOCKET_RECV_BUFSIZE);
+    public static FileDescriptor forProto(int nlProto) throws ErrnoException {
+        final FileDescriptor fd = Os.socket(AF_NETLINK, SOCK_DGRAM, nlProto);
+        Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, SOCKET_RECV_BUFSIZE);
+        return fd;
     }
 
-    public NetlinkSocketAddress getLocalAddress() throws ErrnoException {
-        return (NetlinkSocketAddress) Os.getsockname(mDescriptor);
+    public static void connectToKernel(FileDescriptor fd) throws ErrnoException, SocketException {
+        Os.connect(fd, (SocketAddress) (new NetlinkSocketAddress(0, 0)));
     }
 
-    public void bind(NetlinkSocketAddress localAddr) throws ErrnoException, SocketException {
-        Os.bind(mDescriptor, (SocketAddress)localAddr);
-    }
-
-    public void connectTo(NetlinkSocketAddress peerAddr)
-            throws ErrnoException, SocketException {
-        Os.connect(mDescriptor, (SocketAddress) peerAddr);
-    }
-
-    public void connectToKernel() throws ErrnoException, SocketException {
-        connectTo(new NetlinkSocketAddress(0, 0));
-    }
-
-    /**
-     * Wait indefinitely (or until underlying socket error) for a
-     * netlink message of at most DEFAULT_RECV_BUFSIZE size.
-     */
-    public ByteBuffer recvMessage()
-            throws ErrnoException, InterruptedIOException {
-        return recvMessage(DEFAULT_RECV_BUFSIZE, 0);
-    }
-
-    /**
-     * Wait up to |timeoutMs| (or until underlying socket error) for a
-     * netlink message of at most DEFAULT_RECV_BUFSIZE size.
-     */
-    public ByteBuffer recvMessage(long timeoutMs) throws ErrnoException, InterruptedIOException {
-        return recvMessage(DEFAULT_RECV_BUFSIZE, timeoutMs);
-    }
-
-    private void checkTimeout(long timeoutMs) {
+    private static void checkTimeout(long timeoutMs) {
         if (timeoutMs < 0) {
             throw new IllegalArgumentException("Negative timeouts not permitted");
         }
@@ -147,21 +123,14 @@
      *
      * Multi-threaded calls with different timeouts will cause unexpected results.
      */
-    public ByteBuffer recvMessage(int bufsize, long timeoutMs)
+    public static ByteBuffer recvMessage(FileDescriptor fd, int bufsize, long timeoutMs)
             throws ErrnoException, IllegalArgumentException, InterruptedIOException {
         checkTimeout(timeoutMs);
 
-        synchronized (mDescriptor) {
-            if (mLastRecvTimeoutMs != timeoutMs) {
-                Os.setsockoptTimeval(mDescriptor,
-                        OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO,
-                        StructTimeval.fromMillis(timeoutMs));
-                mLastRecvTimeoutMs = timeoutMs;
-            }
-        }
+        Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(timeoutMs));
 
         ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize);
-        int length = Os.read(mDescriptor, byteBuffer);
+        int length = Os.read(fd, byteBuffer);
         if (length == bufsize) {
             Log.w(TAG, "maximum read");
         }
@@ -172,39 +141,16 @@
     }
 
     /**
-     * Send a message to a peer to which this socket has previously connected.
-     *
-     * This blocks until completion or an error occurs.
-     */
-    public boolean sendMessage(byte[] bytes, int offset, int count)
-            throws ErrnoException, InterruptedIOException {
-        return sendMessage(bytes, offset, count, 0);
-    }
-
-    /**
      * Send a message to a peer to which this socket has previously connected,
      * waiting at most |timeoutMs| milliseconds for the send to complete.
      *
      * Multi-threaded calls with different timeouts will cause unexpected results.
      */
-    public boolean sendMessage(byte[] bytes, int offset, int count, long timeoutMs)
+    public static int sendMessage(
+            FileDescriptor fd, byte[] bytes, int offset, int count, long timeoutMs)
             throws ErrnoException, IllegalArgumentException, InterruptedIOException {
         checkTimeout(timeoutMs);
-
-        synchronized (mDescriptor) {
-            if (mLastSendTimeoutMs != timeoutMs) {
-                Os.setsockoptTimeval(mDescriptor,
-                        OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO,
-                        StructTimeval.fromMillis(timeoutMs));
-                mLastSendTimeoutMs = timeoutMs;
-            }
-        }
-
-        return (count == Os.write(mDescriptor, bytes, offset, count));
-    }
-
-    @Override
-    public void close() {
-        IoUtils.closeQuietly(mDescriptor);
+        Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(timeoutMs));
+        return Os.write(fd, bytes, offset, count);
     }
 }
diff --git a/services/net/java/android/net/netlink/StructNdMsg.java b/services/net/java/android/net/netlink/StructNdMsg.java
index b68ec0b..e34ec39 100644
--- a/services/net/java/android/net/netlink/StructNdMsg.java
+++ b/services/net/java/android/net/netlink/StructNdMsg.java
@@ -63,6 +63,11 @@
         return ((nudState & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE)) != 0);
     }
 
+    public static boolean isNudStateValid(short nudState) {
+        return (isNudStateConnected(nudState) ||
+                ((nudState & (NUD_PROBE|NUD_STALE|NUD_DELAY)) != 0));
+    }
+
     // Neighbor Cache Entry Flags
     public static byte NTF_USE       = (byte) 0x01;
     public static byte NTF_SELF      = (byte) 0x02;
@@ -143,7 +148,7 @@
     }
 
     public boolean nudValid() {
-        return (nudConnected() || ((ndm_state & (NUD_PROBE|NUD_STALE|NUD_DELAY)) != 0));
+        return isNudStateValid(ndm_state);
     }
 
     @Override
diff --git a/services/net/java/android/net/util/BlockingSocketReader.java b/services/net/java/android/net/util/PacketReader.java
similarity index 97%
rename from services/net/java/android/net/util/BlockingSocketReader.java
rename to services/net/java/android/net/util/PacketReader.java
index 99bf469..10da2a5 100644
--- a/services/net/java/android/net/util/BlockingSocketReader.java
+++ b/services/net/java/android/net/util/PacketReader.java
@@ -67,7 +67,7 @@
  *
  * @hide
  */
-public abstract class BlockingSocketReader {
+public abstract class PacketReader {
     private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
     private static final int UNREGISTER_THIS_FD = 0;
 
@@ -83,11 +83,11 @@
         IoUtils.closeQuietly(fd);
     }
 
-    protected BlockingSocketReader(Handler h) {
+    protected PacketReader(Handler h) {
         this(h, DEFAULT_RECV_BUF_SIZE);
     }
 
-    protected BlockingSocketReader(Handler h, int recvbufsize) {
+    protected PacketReader(Handler h, int recvbufsize) {
         mHandler = h;
         mQueue = mHandler.getLooper().getQueue();
         mPacket = new byte[Math.max(recvbufsize, DEFAULT_RECV_BUF_SIZE)];
@@ -115,6 +115,8 @@
         }
     }
 
+    public Handler getHandler() { return mHandler; }
+
     public final int recvBufSize() { return mPacket.length; }
 
     public final long numPacketsReceived() { return mPacketsReceived; }
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index e8ae020..364bbc0 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -25,6 +25,7 @@
 import static com.android.internal.print.DumpUtils.writePrinterId;
 import static com.android.internal.print.DumpUtils.writePrinterInfo;
 import static com.android.internal.print.DumpUtils.writeStringIfNotNull;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -81,7 +82,6 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.BackgroundThread;
-import com.android.internal.os.SomeArgs;
 import com.android.server.print.RemotePrintService.PrintServiceCallbacks;
 import com.android.server.print.RemotePrintServiceRecommendationService
         .RemotePrintServiceRecommendationServiceCallbacks;
@@ -462,7 +462,7 @@
 
             if (mPrinterDiscoverySession == null) {
                 // If we do not have a session, tell all service to create one.
-                mPrinterDiscoverySession = new PrinterDiscoverySessionMediator(mContext) {
+                mPrinterDiscoverySession = new PrinterDiscoverySessionMediator() {
                     @Override
                     public void onDestroyed() {
                         mPrinterDiscoverySession = null;
@@ -1141,12 +1141,8 @@
         // just died. Do this off the main thread since we do to allow
         // calls into the spooler on the main thread.
         if (Looper.getMainLooper().isCurrentThread()) {
-            BackgroundThread.getHandler().post(new Runnable() {
-                @Override
-                public void run() {
-                    failScheduledPrintJobsForServiceInternal(serviceName);
-                }
-            });
+            BackgroundThread.getHandler().sendMessage(obtainMessage(
+                    UserState::failScheduledPrintJobsForServiceInternal, this, serviceName));
         } else {
             failScheduledPrintJobsForServiceInternal(serviceName);
         }
@@ -1341,18 +1337,13 @@
 
         private final List<PrinterId> mStateTrackedPrinters = new ArrayList<PrinterId>();
 
-        private final Handler mSessionHandler;
-
         private boolean mIsDestroyed;
 
-        public PrinterDiscoverySessionMediator(Context context) {
-            mSessionHandler = new SessionHandler(context.getMainLooper());
+        PrinterDiscoverySessionMediator() {
             // Kick off the session creation.
-            List<RemotePrintService> services = new ArrayList<RemotePrintService>(
-                    mActiveServices.values());
-            mSessionHandler.obtainMessage(SessionHandler
-                    .MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION, services)
-                    .sendToTarget();
+            Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
+                    handleDispatchCreatePrinterDiscoverySession,
+                    this, new ArrayList<>(mActiveServices.values())));
         }
 
         public void addObserverLocked(@NonNull IPrinterDiscoveryObserver observer) {
@@ -1361,12 +1352,9 @@
 
             // Bring the added observer up to speed with the printers.
             if (!mPrinters.isEmpty()) {
-                List<PrinterInfo> printers = new ArrayList<PrinterInfo>(mPrinters.values());
-                SomeArgs args = SomeArgs.obtain();
-                args.arg1 = observer;
-                args.arg2 = printers;
-                mSessionHandler.obtainMessage(SessionHandler.MSG_PRINTERS_ADDED,
-                        args).sendToTarget();
+                Handler.getMain().sendMessage(obtainMessage(
+                        UserState.PrinterDiscoverySessionMediator::handlePrintersAdded,
+                        this, observer, new ArrayList<>(mPrinters.values())));
             }
         }
 
@@ -1403,14 +1391,9 @@
                 return;
             }
 
-            List<RemotePrintService> services = new ArrayList<RemotePrintService>(
-                    mActiveServices.values());
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = services;
-            args.arg2 = priorityList;
-            mSessionHandler.obtainMessage(SessionHandler
-                    .MSG_DISPATCH_START_PRINTER_DISCOVERY, args)
-                    .sendToTarget();
+            Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
+                    handleDispatchStartPrinterDiscovery, this,
+                    new ArrayList<>(mActiveServices.values()), priorityList));
         }
 
         public final void stopPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer) {
@@ -1426,11 +1409,9 @@
             if (!mStartedPrinterDiscoveryTokens.isEmpty()) {
                 return;
             }
-            List<RemotePrintService> services = new ArrayList<RemotePrintService>(
-                    mActiveServices.values());
-            mSessionHandler.obtainMessage(SessionHandler
-                    .MSG_DISPATCH_STOP_PRINTER_DISCOVERY, services)
-                    .sendToTarget();
+            Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
+                    handleDispatchStopPrinterDiscovery,
+                    this, new ArrayList<>(mActiveServices.values())));
         }
 
         public void validatePrintersLocked(@NonNull List<PrinterId> printerIds) {
@@ -1461,12 +1442,9 @@
                 // Schedule a notification of the service.
                 RemotePrintService service = mActiveServices.get(serviceName);
                 if (service != null) {
-                    SomeArgs args = SomeArgs.obtain();
-                    args.arg1 = service;
-                    args.arg2 = updateList;
-                    mSessionHandler.obtainMessage(SessionHandler
-                            .MSG_VALIDATE_PRINTERS, args)
-                            .sendToTarget();
+                    Handler.getMain().sendMessage(obtainMessage(
+                            UserState.PrinterDiscoverySessionMediator::handleValidatePrinters,
+                            this, service, updateList));
                 }
             }
         }
@@ -1493,12 +1471,8 @@
                 return;
             }
             // Ask the service to start tracking.
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = service;
-            args.arg2 = printerId;
-            mSessionHandler.obtainMessage(SessionHandler
-                    .MSG_START_PRINTER_STATE_TRACKING, args)
-                    .sendToTarget();
+            Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
+                    handleStartPrinterStateTracking, this, service, printerId));
         }
 
         public final void stopPrinterStateTrackingLocked(PrinterId printerId) {
@@ -1520,12 +1494,8 @@
                 return;
             }
             // Ask the service to start tracking.
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = service;
-            args.arg2 = printerId;
-            mSessionHandler.obtainMessage(SessionHandler
-                    .MSG_STOP_PRINTER_STATE_TRACKING, args)
-                    .sendToTarget();
+            Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
+                    handleStopPrinterStateTracking, this, service, printerId));
         }
 
         public void onDestroyed() {
@@ -1551,11 +1521,9 @@
                 stopPrinterDiscoveryLocked(IPrinterDiscoveryObserver.Stub.asInterface(token));
             }
             // Tell the services we are done.
-            List<RemotePrintService> services = new ArrayList<RemotePrintService>(
-                    mActiveServices.values());
-            mSessionHandler.obtainMessage(SessionHandler
-                    .MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION, services)
-                    .sendToTarget();
+            Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
+                    handleDispatchDestroyPrinterDiscoverySession,
+                    this, new ArrayList<>(mActiveServices.values())));
         }
 
         public void onPrintersAddedLocked(List<PrinterInfo> printers) {
@@ -1579,8 +1547,9 @@
                 }
             }
             if (addedPrinters != null) {
-                mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED,
-                        addedPrinters).sendToTarget();
+                Handler.getMain().sendMessage(obtainMessage(
+                        UserState.PrinterDiscoverySessionMediator::handleDispatchPrintersAdded,
+                        this, addedPrinters));
             }
         }
 
@@ -1604,8 +1573,9 @@
                 }
             }
             if (removedPrinterIds != null) {
-                mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED,
-                        removedPrinterIds).sendToTarget();
+                Handler.getMain().sendMessage(obtainMessage(
+                        UserState.PrinterDiscoverySessionMediator::handleDispatchPrintersRemoved,
+                        this, removedPrinterIds));
             }
         }
 
@@ -1646,8 +1616,9 @@
 
                 ArrayList<PrinterInfo> addedPrinters = new ArrayList<>(1);
                 addedPrinters.add(newPrinter);
-                mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED,
-                        addedPrinters).sendToTarget();
+                Handler.getMain().sendMessage(obtainMessage(
+                        UserState.PrinterDiscoverySessionMediator::handleDispatchPrintersAdded,
+                        this, addedPrinters));
             }
         }
 
@@ -1661,26 +1632,20 @@
                 return;
             }
             // Tell the service to create a session.
-            mSessionHandler.obtainMessage(
-                    SessionHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION,
-                    service).sendToTarget();
+            Handler.getMain().sendMessage(obtainMessage(
+                    RemotePrintService::createPrinterDiscoverySession, service));
             // Start printer discovery if necessary.
             if (!mStartedPrinterDiscoveryTokens.isEmpty()) {
-                mSessionHandler.obtainMessage(
-                        SessionHandler.MSG_START_PRINTER_DISCOVERY,
-                        service).sendToTarget();
+                Handler.getMain().sendMessage(obtainMessage(
+                        RemotePrintService::startPrinterDiscovery, service, null));
             }
             // Start tracking printers if necessary
             final int trackedPrinterCount = mStateTrackedPrinters.size();
             for (int i = 0; i < trackedPrinterCount; i++) {
                 PrinterId printerId = mStateTrackedPrinters.get(i);
                 if (printerId.getServiceName().equals(service.getComponentName())) {
-                    SomeArgs args = SomeArgs.obtain();
-                    args.arg1 = service;
-                    args.arg2 = printerId;
-                    mSessionHandler.obtainMessage(SessionHandler
-                            .MSG_START_PRINTER_STATE_TRACKING, args)
-                            .sendToTarget();
+                    Handler.getMain().sendMessage(obtainMessage(
+                            RemotePrintService::startPrinterStateTracking, service, printerId));
                 }
             }
         }
@@ -1781,9 +1746,9 @@
                 for (int i = 0; i < removedPrinterCount; i++) {
                     mPrinters.remove(removedPrinterIds.get(i));
                 }
-                mSessionHandler.obtainMessage(
-                        SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED,
-                        removedPrinterIds).sendToTarget();
+                Handler.getMain().sendMessage(obtainMessage(
+                        UserState.PrinterDiscoverySessionMediator::handleDispatchPrintersRemoved,
+                        this, removedPrinterIds));
             }
         }
 
@@ -1873,134 +1838,6 @@
                 Log.e(LOG_TAG, "Error sending removed printers", re);
             }
         }
-
-        private final class SessionHandler extends Handler {
-            public static final int MSG_PRINTERS_ADDED = 1;
-            public static final int MSG_PRINTERS_REMOVED = 2;
-            public static final int MSG_DISPATCH_PRINTERS_ADDED = 3;
-            public static final int MSG_DISPATCH_PRINTERS_REMOVED = 4;
-
-            public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 5;
-            public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 6;
-            public static final int MSG_START_PRINTER_DISCOVERY = 7;
-            public static final int MSG_STOP_PRINTER_DISCOVERY = 8;
-            public static final int MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION = 9;
-            public static final int MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION = 10;
-            public static final int MSG_DISPATCH_START_PRINTER_DISCOVERY = 11;
-            public static final int MSG_DISPATCH_STOP_PRINTER_DISCOVERY = 12;
-            public static final int MSG_VALIDATE_PRINTERS = 13;
-            public static final int MSG_START_PRINTER_STATE_TRACKING = 14;
-            public static final int MSG_STOP_PRINTER_STATE_TRACKING = 15;
-            public static final int MSG_DESTROY_SERVICE = 16;
-
-            SessionHandler(Looper looper) {
-                super(looper, null, false);
-            }
-
-            @Override
-            @SuppressWarnings("unchecked")
-            public void handleMessage(Message message) {
-                switch (message.what) {
-                    case MSG_PRINTERS_ADDED: {
-                        SomeArgs args = (SomeArgs) message.obj;
-                        IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1;
-                        List<PrinterInfo> addedPrinters = (List<PrinterInfo>) args.arg2;
-                        args.recycle();
-                        handlePrintersAdded(observer, addedPrinters);
-                    } break;
-
-                    case MSG_PRINTERS_REMOVED: {
-                        SomeArgs args = (SomeArgs) message.obj;
-                        IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1;
-                        List<PrinterId> removedPrinterIds = (List<PrinterId>) args.arg2;
-                        args.recycle();
-                        handlePrintersRemoved(observer, removedPrinterIds);
-                    }
-
-                    case MSG_DISPATCH_PRINTERS_ADDED: {
-                        List<PrinterInfo> addedPrinters = (List<PrinterInfo>) message.obj;
-                        handleDispatchPrintersAdded(addedPrinters);
-                    } break;
-
-                    case MSG_DISPATCH_PRINTERS_REMOVED: {
-                        List<PrinterId> removedPrinterIds = (List<PrinterId>) message.obj;
-                        handleDispatchPrintersRemoved(removedPrinterIds);
-                    } break;
-
-                    case MSG_CREATE_PRINTER_DISCOVERY_SESSION: {
-                        RemotePrintService service = (RemotePrintService) message.obj;
-                        service.createPrinterDiscoverySession();
-                    } break;
-
-                    case MSG_DESTROY_PRINTER_DISCOVERY_SESSION: {
-                        RemotePrintService service = (RemotePrintService) message.obj;
-                        service.destroyPrinterDiscoverySession();
-                    } break;
-
-                    case MSG_START_PRINTER_DISCOVERY: {
-                        RemotePrintService service = (RemotePrintService) message.obj;
-                        service.startPrinterDiscovery(null);
-                    } break;
-
-                    case MSG_STOP_PRINTER_DISCOVERY: {
-                        RemotePrintService service = (RemotePrintService) message.obj;
-                        service.stopPrinterDiscovery();
-                    } break;
-
-                    case MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION: {
-                        List<RemotePrintService> services = (List<RemotePrintService>) message.obj;
-                        handleDispatchCreatePrinterDiscoverySession(services);
-                    } break;
-
-                    case MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION: {
-                        List<RemotePrintService> services = (List<RemotePrintService>) message.obj;
-                        handleDispatchDestroyPrinterDiscoverySession(services);
-                    } break;
-
-                    case MSG_DISPATCH_START_PRINTER_DISCOVERY: {
-                        SomeArgs args = (SomeArgs) message.obj;
-                        List<RemotePrintService> services = (List<RemotePrintService>) args.arg1;
-                        List<PrinterId> printerIds = (List<PrinterId>) args.arg2;
-                        args.recycle();
-                        handleDispatchStartPrinterDiscovery(services, printerIds);
-                    } break;
-
-                    case MSG_DISPATCH_STOP_PRINTER_DISCOVERY: {
-                        List<RemotePrintService> services = (List<RemotePrintService>) message.obj;
-                        handleDispatchStopPrinterDiscovery(services);
-                    } break;
-
-                    case MSG_VALIDATE_PRINTERS: {
-                        SomeArgs args = (SomeArgs) message.obj;
-                        RemotePrintService service = (RemotePrintService) args.arg1;
-                        List<PrinterId> printerIds = (List<PrinterId>) args.arg2;
-                        args.recycle();
-                        handleValidatePrinters(service, printerIds);
-                    } break;
-
-                    case MSG_START_PRINTER_STATE_TRACKING: {
-                        SomeArgs args = (SomeArgs) message.obj;
-                        RemotePrintService service = (RemotePrintService) args.arg1;
-                        PrinterId printerId = (PrinterId) args.arg2;
-                        args.recycle();
-                        handleStartPrinterStateTracking(service, printerId);
-                    } break;
-
-                    case MSG_STOP_PRINTER_STATE_TRACKING: {
-                        SomeArgs args = (SomeArgs) message.obj;
-                        RemotePrintService service = (RemotePrintService) args.arg1;
-                        PrinterId printerId = (PrinterId) args.arg2;
-                        args.recycle();
-                        handleStopPrinterStateTracking(service, printerId);
-                    } break;
-
-                    case MSG_DESTROY_SERVICE: {
-                        RemotePrintService service = (RemotePrintService) message.obj;
-                        service.destroy();
-                    } break;
-                }
-            }
-        }
     }
 
     private final class PrintJobForAppCache {
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index e5ab44f..218a2b8 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -38,7 +38,7 @@
 LOCAL_JAVA_LIBRARIES := \
     android.hidl.manager-V1.0-java \
     android.test.mock \
-    legacy-android-test \
+    android.test.base android.test.runner \
 
 LOCAL_PACKAGE_NAME := FrameworksServicesTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
index 5676510..7160d7f 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
@@ -19,31 +19,16 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 
-import android.app.ActivityOptions;
-import android.app.IApplicationThread;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ResolveInfo;
-import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
-import android.service.voice.IVoiceInteractionSession;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import com.android.internal.app.IVoiceInteractor;
 import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
 import com.android.server.am.ActivityStarter.Factory;
 
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyObject;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
@@ -73,59 +58,10 @@
         super.setUp();
         mService = createActivityManagerService();
         mFactory = mock(Factory.class);
-        mStarter = mock(ActivityStarter.class);
-        doReturn(mStarter).when(mFactory).getStarter(any(), any(), any(), any());
         mController = new ActivityStartController(mService, mService.mStackSupervisor, mFactory);
-    }
-
-    /**
-     * Ensures that the starter is correctly invoked on
-     * {@link ActivityStartController#startActivity}
-     */
-    @Test
-    @Presubmit
-    public void testStartActivity() {
-        final Random random = new Random();
-
-        final IApplicationThread applicationThread = mock(IApplicationThread.class);
-        final Intent intent = mock(Intent.class);
-        final Intent ephemeralIntent = mock(Intent.class);
-        final String resolvedType = "TestType";
-        final ActivityInfo aInfo = mock(ActivityInfo.class);
-        final ResolveInfo rInfo = mock(ResolveInfo.class);
-        final IVoiceInteractionSession voiceInteractionSession =
-                mock(IVoiceInteractionSession.class);
-        final IVoiceInteractor voiceInteractor = mock(IVoiceInteractor.class);
-        final IBinder resultTo = mock(IBinder.class);
-        final String resultWho = "resultWho";
-        final int requestCode = random.nextInt();
-        final int callingPid = random.nextInt();
-        final int callingUid = random.nextInt();
-        final String callingPackage = "callingPackage";
-        final int realCallingPid = random.nextInt();
-        final int realCallingUid = random.nextInt();
-        final int startFlags = random.nextInt();
-        final ActivityOptions options = mock(ActivityOptions.class);
-        final boolean ignoreTargetSecurity = random.nextBoolean();
-        final boolean componentSpecified = random.nextBoolean();
-        final ActivityRecord[] outActivity = new ActivityRecord[1];
-        final TaskRecord inTask = mock(TaskRecord.class);
-        final String reason ="reason";
-
-        mController.startActivity(applicationThread, intent, ephemeralIntent, resolvedType,
-                aInfo, rInfo, voiceInteractionSession, voiceInteractor, resultTo, resultWho,
-                requestCode, callingPid, callingUid, callingPackage, realCallingPid, realCallingUid,
-                startFlags, options, ignoreTargetSecurity, componentSpecified, outActivity, inTask,
-                reason);
-
-        // The starter should receive a start command with the originally provided parameters
-        verify(mStarter, times(1)).startActivityLocked(eq(applicationThread), eq(intent),
-                eq(ephemeralIntent), eq(resolvedType), eq(aInfo), eq(rInfo),
-                eq(voiceInteractionSession), eq(voiceInteractor), eq(resultTo), eq(resultWho),
-                eq(requestCode), eq(callingPid), eq(callingUid), eq(callingPackage),
-                eq(realCallingPid), eq(realCallingUid), eq(startFlags), eq(options),
-                eq(ignoreTargetSecurity), eq(componentSpecified), eq(outActivity), eq(inTask),
-                eq(reason));
+        mStarter = spy(new ActivityStarter(mController, mService, mService.mStackSupervisor,
+                mock(ActivityStartInterceptor.class)));
+        doReturn(mStarter).when(mFactory).obtainStarter();
     }
 
     /**
@@ -149,7 +85,7 @@
         final boolean resume = random.nextBoolean();
         mController.doPendingActivityLaunches(resume);
 
-        verify(mStarter, times(1)).startActivity(eq(activity), eq(source), eq(null),
+        verify(mStarter, times(1)).startResolvedActivity(eq(activity), eq(source), eq(null),
                 eq(null), eq(startFlags), eq(resume), eq(null), eq(null), eq(null));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 471726b..e8194dd 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -166,10 +166,9 @@
      * Excercises how the {@link ActivityStarter} reacts to various preconditions. The caller
      * provides a bitmask of all the set conditions (such as {@link #PRECONDITION_NO_CALLER_APP})
      * and the launch flags specified in the intent. The method constructs a call to
-     * {@link ActivityStarter#startActivityLocked} based on these preconditions and ensures the
-     * result matches the expected. It is important to note that the method also checks side effects
-     * of the start, such as ensuring {@link ActivityOptions#abort()} is called in the relevant
-     * scenarios.
+     * {@link ActivityStarter#execute} based on these preconditions and ensures the result matches
+     * the expected. It is important to note that the method also checks side effects of the start,
+     * such as ensuring {@link ActivityOptions#abort()} is called in the relevant scenarios.
      * @param preconditions A bitmask representing the preconditions for the launch
      * @param launchFlags The launch flags to be provided by the launch {@link Intent}.
      * @param expectedResult The expected result from the launch.
@@ -254,14 +253,13 @@
         final int requestCode = containsConditions(preconditions, PRECONDITION_REQUEST_CODE)
                 ? 1 : 0;
 
-        final int result = starter.startActivityLocked(caller, intent,
-                null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
-                null /*voiceSession*/, null /*voiceInteractor*/, resultTo,
-                null /*resultWho*/, requestCode, 0 /*callingPid*/, 0 /*callingUid*/,
-                null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
-                0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/,
-                false /*componentSpecified*/, null /*outActivity*/,
-                null /*inTask*/, "testLaunchActivityPermissionDenied");
+        final int result = starter.setCaller(caller)
+                .setIntent(intent)
+                .setActivityInfo(aInfo)
+                .setResultTo(resultTo)
+                .setRequestCode(requestCode)
+                .setReason("testLaunchActivityPermissionDenied")
+                .execute();
 
         // In some cases the expected result internally is different than the published result. We
         // must use ActivityStarter#getExternalResult to translate.
@@ -269,15 +267,18 @@
 
         // Ensure that {@link ActivityOptions} are aborted with unsuccessful result.
         if (expectedResult != START_SUCCESS) {
+            final ActivityStarter optionStarter = new ActivityStarter(mController, mService,
+                    mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
             final ActivityOptions options = spy(ActivityOptions.makeBasic());
-            final int optionResult = starter.startActivityLocked(caller, intent,
-                    null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
-                    null /*voiceSession*/, null /*voiceInteractor*/, resultTo,
-                    null /*resultWho*/, requestCode, 0 /*callingPid*/, 0 /*callingUid*/,
-                    null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
-                    0 /*startFlags*/, options /*options*/, false /*ignoreTargetSecurity*/,
-                    false /*componentSpecified*/, null /*outActivity*/,
-                    null /*inTask*/, "testLaunchActivityPermissionDenied");
+
+            final int optionResult = optionStarter.setCaller(caller)
+                    .setIntent(intent)
+                    .setActivityInfo(aInfo)
+                    .setResultTo(resultTo)
+                    .setRequestCode(requestCode)
+                    .setReason("testLaunchActivityPermissionDenied")
+                    .setActivityOptions(options)
+                    .execute();
             verify(options, times(1)).abort();
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
index 54df744..d2ae22b 100644
--- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
@@ -30,6 +30,7 @@
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
 import static android.os.Process.SYSTEM_UID;
+import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
 
 import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_LOCKED;
 import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_PINNED;
@@ -53,6 +54,7 @@
 import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
+import android.telecom.TelecomManager;
 import android.util.Pair;
 
 import com.android.internal.statusbar.IStatusBarService;
@@ -90,6 +92,7 @@
     @Mock private LockPatternUtils mLockPatternUtils;
     @Mock private LockTaskNotify mLockTaskNotify;
     @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+    @Mock private TelecomManager mTelecomManager;
     @Mock private RecentTasks mRecentTasks;
 
     private LockTaskController mLockTaskController;
@@ -118,6 +121,7 @@
         mLockTaskController.setWindowManager(mWindowManager);
         mLockTaskController.mStatusBarService = mStatusBarService;
         mLockTaskController.mDevicePolicyManager = mDevicePolicyManager;
+        mLockTaskController.mTelecomManager = mTelecomManager;
         mLockTaskController.mLockPatternUtils = mLockPatternUtils;
         mLockTaskController.mLockTaskNotify = mLockTaskNotify;
 
@@ -209,7 +213,7 @@
 
     @Test
     public void testLockTaskViolation() throws Exception {
-        // GIVEN one task records with whitelisted auth that is in lock task mode
+        // GIVEN one task record with whitelisted auth that is in lock task mode
         TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
@@ -234,6 +238,38 @@
     }
 
     @Test
+    public void testLockTaskViolation_emergencyCall() throws Exception {
+        // GIVEN one task record with whitelisted auth that is in lock task mode
+        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+        // GIVEN tasks necessary for emergency calling
+        TaskRecord keypad = getTaskRecord(new Intent().setComponent(EMERGENCY_DIALER_COMPONENT),
+                TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+        TaskRecord callAction = getTaskRecord(new Intent(Intent.ACTION_CALL_EMERGENCY),
+                TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+        TaskRecord dialer = getTaskRecord("com.example.dialer", TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+        when(mTelecomManager.getSystemDialerPackage())
+                .thenReturn(dialer.intent.getComponent().getPackageName());
+
+        // GIVEN keyguard is allowed for lock task mode
+        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_KEYGUARD);
+
+        // THEN the above tasks should all be allowed
+        assertFalse(mLockTaskController.isLockTaskModeViolation(keypad));
+        assertFalse(mLockTaskController.isLockTaskModeViolation(callAction));
+        assertFalse(mLockTaskController.isLockTaskModeViolation(dialer));
+
+        // GIVEN keyguard is disallowed for lock task mode (default)
+        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NONE);
+
+        // THEN the above tasks should all be blocked
+        assertTrue(mLockTaskController.isLockTaskModeViolation(keypad));
+        assertTrue(mLockTaskController.isLockTaskModeViolation(callAction));
+        assertTrue(mLockTaskController.isLockTaskModeViolation(dialer));
+    }
+
+    @Test
     public void testStopLockTaskMode() throws Exception {
         // GIVEN one task record with whitelisted auth that is in lock task mode
         TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
@@ -568,10 +604,15 @@
     }
 
     private TaskRecord getTaskRecord(String pkg, int lockTaskAuth) {
+        final Intent intent = new Intent()
+                .setComponent(ComponentName.createRelative(pkg, TEST_CLASS_NAME));
+        return getTaskRecord(intent, lockTaskAuth);
+    }
+
+    private TaskRecord getTaskRecord(Intent intent, int lockTaskAuth) {
         TaskRecord tr = mock(TaskRecord.class);
         tr.mLockTaskAuth = lockTaskAuth;
-        tr.intent = new Intent()
-                .setComponent(ComponentName.createRelative(pkg, TEST_CLASS_NAME));
+        tr.intent = intent;
         tr.userId = TEST_USER_ID;
         return tr;
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
new file mode 100644
index 0000000..5520bd7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
@@ -0,0 +1,125 @@
+/*
+ * 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.server.am;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.platform.test.annotations.Presubmit;
+import android.service.voice.IVoiceInteractionSession;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.app.IVoiceInteractor;
+import com.android.server.am.TaskRecord.TaskRecordFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Tests for exercising {@link TaskRecord}.
+ *
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.am.TaskRecordTests
+ */
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class TaskRecordTests {
+
+    @Before
+    public void setUp() throws Exception {
+        TaskRecord.setTaskRecordFactory(null);
+    }
+
+    @Test
+    public void testDefaultTaskFactoryNotNull() throws Exception {
+        assertNotNull(TaskRecord.getTaskRecordFactory());
+    }
+
+    @Test
+    public void testCreateTestRecordUsingCustomizedFactory() throws Exception {
+        TestTaskRecordFactory factory = new TestTaskRecordFactory();
+        TaskRecord.setTaskRecordFactory(factory);
+
+        assertFalse(factory.mCreated);
+
+        TaskRecord.create(null, 0, null, null, null, null);
+
+        assertTrue(factory.mCreated);
+    }
+
+    private static class TestTaskRecordFactory extends TaskRecordFactory {
+        private boolean mCreated = false;
+
+        @Override
+        TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+                Intent intent,
+                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
+            mCreated = true;
+            return null;
+        }
+
+        @Override
+        TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+                Intent intent,
+                ActivityManager.TaskDescription taskDescription) {
+            mCreated = true;
+            return null;
+        }
+
+        @Override
+        TaskRecord create(ActivityManagerService service, int taskId, Intent intent,
+                Intent affinityIntent, String affinity, String rootAffinity,
+                ComponentName realActivity,
+                ComponentName origActivity, boolean rootWasReset, boolean autoRemoveRecents,
+                boolean askedCompatMode, int userId, int effectiveUid, String lastDescription,
+                ArrayList<ActivityRecord> activities, long lastTimeMoved,
+                boolean neverRelinquishIdentity,
+                ActivityManager.TaskDescription lastTaskDescription,
+                int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor,
+                int callingUid, String callingPackage, int resizeMode,
+                boolean supportsPictureInPicture,
+                boolean realActivitySuspended, boolean userSetupComplete, int minWidth,
+                int minHeight) {
+            mCreated = true;
+            return null;
+        }
+
+        @Override
+        TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
+                throws IOException, XmlPullParserException {
+            mCreated = true;
+            return null;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java
index 87c587a..c40b411 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/BackupManagerMonitorUtilsTest.java
@@ -20,6 +20,7 @@
 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_ID;
 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME;
 import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION;
+import static android.app.backup.BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -102,11 +103,40 @@
         ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
         verify(mMonitorMock).onEvent(bundleCaptor.capture());
         Bundle eventBundle = bundleCaptor.getValue();
-        assertThat(eventBundle.size()).isEqualTo(6);
+        assertThat(eventBundle.size()).isEqualTo(7);
         assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_ID)).isEqualTo(1);
         assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_CATEGORY)).isEqualTo(2);
         assertThat(eventBundle.getString(EXTRA_LOG_EVENT_PACKAGE_NAME)).isEqualTo("test.package");
         assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_PACKAGE_VERSION)).isEqualTo(3);
+        assertThat(eventBundle.getLong(EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)).isEqualTo(3);
+        assertThat(eventBundle.getInt("key1")).isEqualTo(4);
+        assertThat(eventBundle.getString("key2")).isEqualTo("value2");
+    }
+
+    @Test
+    public void monitorEvent_packageAndExtrasAreNotNull_fillsBundleCorrectlyLong() throws Exception {
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.packageName = "test.package";
+        packageInfo.versionCode = 3;
+        packageInfo.versionCodeMajor = 10;
+        Bundle extras = new Bundle();
+        extras.putInt("key1", 4);
+        extras.putString("key2", "value2");
+
+        IBackupManagerMonitor result = BackupManagerMonitorUtils.monitorEvent(mMonitorMock, 1,
+                packageInfo, 2, extras);
+
+        assertThat(result).isEqualTo(mMonitorMock);
+        ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+        verify(mMonitorMock).onEvent(bundleCaptor.capture());
+        Bundle eventBundle = bundleCaptor.getValue();
+        assertThat(eventBundle.size()).isEqualTo(7);
+        assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_ID)).isEqualTo(1);
+        assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_CATEGORY)).isEqualTo(2);
+        assertThat(eventBundle.getString(EXTRA_LOG_EVENT_PACKAGE_NAME)).isEqualTo("test.package");
+        assertThat(eventBundle.getInt(EXTRA_LOG_EVENT_PACKAGE_VERSION)).isEqualTo(3);
+        assertThat(eventBundle.getLong(EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION)).isEqualTo(
+                (10L << 32) | 3);
         assertThat(eventBundle.getInt("key1")).isEqualTo(4);
         assertThat(eventBundle.getString("key2")).isEqualTo("value2");
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 7e11e87..e2ba4d5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -103,7 +103,7 @@
             long callingIdentity = clearCallingIdentity();
             Throwable throwableToPropagate = null;
             try {
-                action.run();
+                action.runOrThrow();
             } catch (Throwable throwable) {
                 throwableToPropagate = throwable;
             } finally {
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 6a1d268..926009e 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -134,7 +134,7 @@
         mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
         mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
                 Settings.System.SCREEN_BRIGHTNESS));
-        List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+        List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         mTracker.stop();
 
         assertEquals(1, events.size());
@@ -169,7 +169,9 @@
         final long sensorTime = mInjector.currentTimeMillis();
         mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
                 Settings.System.SCREEN_BRIGHTNESS));
-        List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+        List<BrightnessChangeEvent> eventsNoPackage
+                = mTracker.getEvents(0, false).getList();
+        List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         mTracker.stop();
 
         assertEquals(1, events.size());
@@ -184,6 +186,9 @@
         assertEquals(3333, event.colorTemperature);
         assertEquals("a.package", event.packageName);
         assertEquals(0, event.userId);
+
+        assertEquals(1, eventsNoPackage.size());
+        assertNull(eventsNoPackage.get(0).packageName);
     }
 
     @Test
@@ -200,7 +205,7 @@
                 (int) mInjector.mSystemIntSettings.get(Settings.System.SCREEN_BRIGHTNESS));
         mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
                 Settings.System.SCREEN_BRIGHTNESS));
-        List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+        List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         // No events because we filtered out our change.
         assertEquals(0, events.size());
 
@@ -217,7 +222,7 @@
                 secondUserUpdateBrightness);
         mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
                 Settings.System.SCREEN_BRIGHTNESS));
-        events = mTracker.getEvents(0).getList();
+        events = mTracker.getEvents(0, true).getList();
 
         assertEquals(2, events.size());
         // First event is change from system update (20) to first user update (20)
@@ -242,7 +247,7 @@
             mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
                     Settings.System.SCREEN_BRIGHTNESS));
         }
-        List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+        List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         mTracker.stop();
 
         // Should be capped at 100 events, and they should be the most recent 100.
@@ -266,7 +271,7 @@
         }
         mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
                 Settings.System.SCREEN_BRIGHTNESS));
-        List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+        List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         mTracker.stop();
 
         assertEquals(1, events.size());
@@ -317,7 +322,7 @@
                 + Long.toString(twoMonthsAgo) + "," + Long.toString(twoMonthsAgo) + "\"/>"
                 + "</events>";
         tracker.readEventsLocked(getInputStream(eventFile));
-        List<BrightnessChangeEvent> events = tracker.getEvents(0).getList();
+        List<BrightnessChangeEvent> events = tracker.getEvents(0, true).getList();
         assertEquals(1, events.size());
         BrightnessChangeEvent event = events.get(0);
         assertEquals(someTimeAgo, event.timeStamp);
@@ -330,7 +335,7 @@
         assertEquals(1.0f, event.batteryLevel, 0.01);
         assertEquals("com.example.app", event.packageName);
 
-        events = tracker.getEvents(1).getList();
+        events = tracker.getEvents(1, true).getList();
         assertEquals(1, events.size());
         event = events.get(0);
         assertEquals(someTimeAgo, event.timeStamp);
@@ -359,7 +364,7 @@
         } catch (IOException e) {
             // Expected;
         }
-        assertEquals(0, tracker.getEvents(0).getList().size());
+        assertEquals(0, tracker.getEvents(0, true).getList().size());
 
         // Missing lux value.
         eventFile =
@@ -374,7 +379,7 @@
         } catch (IOException e) {
             // Expected;
         }
-        assertEquals(0, tracker.getEvents(0).getList().size());
+        assertEquals(0, tracker.getEvents(0, true).getList().size());
     }
 
     @Test
@@ -405,7 +410,7 @@
         BrightnessTracker tracker = new BrightnessTracker(InstrumentationRegistry.getContext(),
                 mInjector);
         tracker.readEventsLocked(input);
-        List<BrightnessChangeEvent> events = tracker.getEvents(0).getList();
+        List<BrightnessChangeEvent> events = tracker.getEvents(0, true).getList();
 
         assertEquals(1, events.size());
         BrightnessChangeEvent event = events.get(0);
@@ -442,12 +447,12 @@
                 Settings.System.SCREEN_BRIGHTNESS));
         final long eventTime = mInjector.currentTimeMillis();
 
-        List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+        List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         assertEquals(2, events.size());
 
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         mTracker.writeEventsLocked(baos);
-        events = mTracker.getEvents(0).getList();
+        events = mTracker.getEvents(0, true).getList();
         mTracker.stop();
 
         assertEquals(1, events.size());
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 025ebc3..926a911 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -1075,6 +1075,7 @@
         final PackageInfo ret = new PackageInfo();
         ret.packageName = pi.packageName;
         ret.versionCode = pi.versionCode;
+        ret.versionCodeMajor = pi.versionCodeMajor;
         ret.lastUpdateTime = pi.lastUpdateTime;
 
         ret.applicationInfo = new ApplicationInfo(pi.applicationInfo);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index fd105bc..0995f2e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -577,7 +577,7 @@
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         pkg.usesStaticLibraries = new ArrayList<>(
                 Arrays.asList("foo.bar1", "foo.bar2", "foo.bar3"));
-        pkg.usesStaticLibrariesVersions = new int[] {2, 4, 6};
+        pkg.usesStaticLibrariesVersions = new long[] {2, 4, 6};
         settings.insertPackageSettingLPw(ps, pkg);
         assertEquals(pkg, ps.pkg);
         assertArrayEquals(pkg.usesStaticLibraries.toArray(new String[0]), ps.usesStaticLibraries);
@@ -602,6 +602,11 @@
                 Arrays.equals(a, b));
     }
 
+    private void assertArrayEquals(long[] a, long[] b) {
+        assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b),
+                Arrays.equals(a, b));
+    }
+
     private void verifyUserState(PackageUserState userState, PackageUserState oldUserState,
             boolean userStateChanged) {
         verifyUserState(userState, oldUserState, userStateChanged, false /*notLaunched*/,
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 1f9a243..36cc3a0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -471,7 +471,7 @@
         pkg.usesStaticLibraries.add("foo23");
         pkg.usesStaticLibrariesCertDigests = new String[1][];
         pkg.usesStaticLibrariesCertDigests[0] = new String[] { "digest" };
-        pkg.usesStaticLibrariesVersions = new int[] { 100 };
+        pkg.usesStaticLibrariesVersions = new long[] { 100 };
 
         pkg.libraryNames = new ArrayList<>();
         pkg.libraryNames.add("foo10");
diff --git a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
index a70441d..d1e0132 100644
--- a/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
+++ b/services/tests/servicestests/src/com/android/server/policy/FakeWindowState.java
@@ -20,6 +20,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.IApplicationToken;
 import android.view.WindowManager;
 
@@ -34,6 +35,8 @@
     public final Rect stableFrame = new Rect();
     public Rect outsetFrame = new Rect();
 
+    public DisplayCutout displayCutout;
+
     public WindowManager.LayoutParams attrs;
     public int displayId;
     public boolean isVoiceInteraction;
@@ -56,7 +59,7 @@
     @Override
     public void computeFrameLw(Rect parentFrame, Rect displayFrame, Rect overlayFrame,
             Rect contentFrame, Rect visibleFrame, Rect decorFrame, Rect stableFrame,
-            @Nullable Rect outsetFrame) {
+            @Nullable Rect outsetFrame, DisplayCutout displayCutout) {
         this.parentFrame.set(parentFrame);
         this.displayFrame.set(displayFrame);
         this.overscanFrame.set(overlayFrame);
@@ -65,6 +68,7 @@
         this.decorFrame.set(decorFrame);
         this.stableFrame.set(stableFrame);
         this.outsetFrame = outsetFrame == null ? null : new Rect(outsetFrame);
+        this.displayCutout = displayCutout;
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
index 7d73e82..0ea8d4f 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
@@ -1246,13 +1246,13 @@
     }
 
     private void configureUpdateAppPackageVersion(String updateAppPackageName,
-            int updataAppPackageVersion) throws Exception {
+            long updataAppPackageVersion) throws Exception {
         when(mMockPackageManagerHelper.getInstalledPackageVersion(updateAppPackageName))
                 .thenReturn(updataAppPackageVersion);
     }
 
     private void configureDataAppPackageVersion(String dataAppPackageName,
-            int dataAppPackageVersion) throws Exception {
+            long dataAppPackageVersion) throws Exception {
         when(mMockPackageManagerHelper.getInstalledPackageVersion(dataAppPackageName))
                 .thenReturn(dataAppPackageVersion);
     }
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index cd3ae1a..26853a9 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -165,7 +165,7 @@
     }
 
     @Override
-    public int getFactoryPackageVersion(String packageName) throws NameNotFoundException {
+    public long getFactoryPackageVersion(String packageName) throws NameNotFoundException {
         PackageInfo pi = null;
         Map<Integer, PackageInfo> userPackages = mPackages.get(packageName);
         if (userPackages == null) throw new NameNotFoundException();
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index 2224de5..401f585 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -1512,8 +1512,8 @@
         // Ensure the API is correct before running waitForAndGetProvider
         assertEquals(firstPackage.packageName,
                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName);
-        assertEquals(firstPackage.versionCode,
-                mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionCode);
+        assertEquals(firstPackage.getLongVersionCode(),
+                mWebViewUpdateServiceImpl.getCurrentWebViewPackage().getLongVersionCode());
         assertEquals(firstPackage.versionName,
                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionName);
 
@@ -1524,8 +1524,8 @@
         // Ensure the API is still correct after running waitForAndGetProvider
         assertEquals(firstPackage.packageName,
                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().packageName);
-        assertEquals(firstPackage.versionCode,
-                mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionCode);
+        assertEquals(firstPackage.getLongVersionCode(),
+                mWebViewUpdateServiceImpl.getCurrentWebViewPackage().getLongVersionCode());
         assertEquals(firstPackage.versionName,
                 mWebViewUpdateServiceImpl.getCurrentWebViewPackage().versionName);
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
index 0a644b60..353aa7d 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
@@ -23,6 +23,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.util.MergedConfiguration;
+import android.view.DisplayCutout;
 import android.view.DragEvent;
 import android.view.IWindow;
 
@@ -37,7 +38,8 @@
     @Override
     public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
             Rect stableInsets, Rect outsets, boolean reportDraw, MergedConfiguration mergedConfig,
-            Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId)
+            Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId,
+            DisplayCutout.ParcelableWrapper displayCutout)
             throws RemoteException {
 
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index c735341..481c898 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -411,11 +411,6 @@
     }
 
     @Override
-    public boolean inKeyguardRestrictedKeyInputMode() {
-        return false;
-    }
-
-    @Override
     public void dismissKeyguardLw(@Nullable IKeyguardDismissCallback callback) {
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index fdcf57b..337fd50 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -22,12 +22,12 @@
 
 import android.app.ActivityManager.TaskDescription;
 import android.content.res.Configuration;
+import android.graphics.Point;
 import android.graphics.Rect;
-import android.os.Debug;
 import android.platform.test.annotations.Presubmit;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IWindow;
@@ -38,6 +38,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import java.util.Arrays;
+
 /**
  * Tests for the {@link WindowState#computeFrameLw} method and other window frame machinery.
  *
@@ -158,7 +160,7 @@
         // When mFrame extends past cf, the content insets are
         // the difference between mFrame and ContentFrame. Visible
         // and stable frames work the same way.
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame,0, 0, 1000, 1000);
         assertRect(w.mContentInsets, 0, topContentInset, 0, bottomContentInset);
         assertRect(w.mVisibleInsets, 0, topVisibleInset, 0, bottomVisibleInset);
@@ -173,7 +175,7 @@
         w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
         w.mRequestedWidth = 100;
         w.mRequestedHeight = 100;
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 100, 100, 200, 200);
         assertRect(w.mContentInsets, 0, 0, 0, 0);
         // In this case the frames are shrunk to the window frame.
@@ -194,7 +196,7 @@
 
         // Here the window has FILL_PARENT, FILL_PARENT
         // so we expect it to fill the entire available frame.
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 0, 0, 1000, 1000);
 
         // It can select various widths and heights within the bounds.
@@ -202,14 +204,14 @@
         // and we use mRequestedWidth/mRequestedHeight
         w.mAttrs.width = 300;
         w.mAttrs.height = 300;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         // Explicit width and height without requested width/height
         // gets us nothing.
         assertRect(w.mFrame, 0, 0, 0, 0);
 
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         // With requestedWidth/Height we can freely choose our size within the
         // parent bounds.
         assertRect(w.mFrame, 0, 0, 300, 300);
@@ -222,14 +224,14 @@
         w.mRequestedWidth = -1;
         w.mAttrs.width = 100;
         w.mAttrs.height = 100;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 0, 0, 100, 100);
         w.mAttrs.flags = 0;
 
         // But sizes too large will be clipped to the containing frame
         w.mRequestedWidth = 1200;
         w.mRequestedHeight = 1200;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 0, 0, 1000, 1000);
 
         // Before they are clipped though windows will be shifted
@@ -237,7 +239,7 @@
         w.mAttrs.y = 300;
         w.mRequestedWidth = 1000;
         w.mRequestedHeight = 1000;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 0, 0, 1000, 1000);
 
         // If there is room to move around in the parent frame the window will be shifted according
@@ -247,16 +249,16 @@
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 700, 0, 1000, 300);
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 700, 700, 1000, 1000);
         // Window specified  x and y are interpreted as offsets in the opposite
         // direction of gravity
         w.mAttrs.x = 100;
         w.mAttrs.y = 100;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, 600, 600, 900, 900);
     }
 
@@ -277,7 +279,7 @@
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
 
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null, DisplayCutout.NO_CUTOUT);
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
@@ -289,7 +291,7 @@
         final int cfRight = logicalWidth / 2;
         final int cfBottom = logicalHeight / 2;
         final Rect cf = new Rect(0, 0, cfRight, cfBottom);
-        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null);
+        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
         int contentInsetRight = taskRight - cfRight;
         int contentInsetBottom = taskBottom - cfBottom;
@@ -306,7 +308,7 @@
         final int insetRight = insetLeft + (taskRight - taskLeft);
         final int insetBottom = insetTop + (taskBottom - taskTop);
         task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
-        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null);
+        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null, DisplayCutout.NO_CUTOUT);
         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
         contentInsetRight = insetRight - cfRight;
         contentInsetBottom = insetBottom - cfBottom;
@@ -338,13 +340,13 @@
 
         final Rect policyCrop = new Rect();
 
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, DisplayCutout.NO_CUTOUT);
         w.calculatePolicyCrop(policyCrop);
         assertRect(policyCrop, 0, cf.top, logicalWidth, cf.bottom);
 
         dcf.setEmpty();
         // Likewise with no decor frame we would get no crop
-        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
+        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null, DisplayCutout.NO_CUTOUT);
         w.calculatePolicyCrop(policyCrop);
         assertRect(policyCrop, 0, 0, logicalWidth, logicalHeight);
 
@@ -357,7 +359,7 @@
         w.mAttrs.height = logicalHeight / 2;
         w.mRequestedWidth = logicalWidth / 2;
         w.mRequestedHeight = logicalHeight / 2;
-        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, DisplayCutout.NO_CUTOUT);
 
         w.calculatePolicyCrop(policyCrop);
         // Normally the crop is shrunk from the decor frame
@@ -394,7 +396,7 @@
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
         w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
                 pf /* contentFrame */, pf /* visibleFrame */, pf /* decorFrame */,
-                pf /* stableFrame */, null /* outsetFrame */);
+                pf /* stableFrame */, null /* outsetFrame */, DisplayCutout.NO_CUTOUT);
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
@@ -413,12 +415,31 @@
 
         w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
                 cf /* contentFrame */, cf /* visibleFrame */, pf /* decorFrame */,
-                cf /* stableFrame */, null /* outsetFrame */);
+                cf /* stableFrame */, null /* outsetFrame */, DisplayCutout.NO_CUTOUT);
         assertEquals(cf, w.mFrame);
         assertEquals(cf, w.getContentFrameLw());
         assertRect(w.mContentInsets, 0, 0, 0, 0);
     }
 
+    @Test
+    public void testDisplayCutout() {
+        // Regular fullscreen task and window
+        Task task = new TaskWithBounds(null);
+        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
+        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
+
+        final Rect pf = new Rect(0, 0, 1000, 1000);
+        // Create a display cutout of size 50x50, aligned top-center
+        final DisplayCutout cutout = createDisplayCutoutFromRect(500, 0, 550, 50);
+
+        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf, cutout);
+
+        assertEquals(w.mDisplayCutout.getSafeInsetTop(), 50);
+        assertEquals(w.mDisplayCutout.getSafeInsetBottom(), 0);
+        assertEquals(w.mDisplayCutout.getSafeInsetLeft(), 0);
+        assertEquals(w.mDisplayCutout.getSafeInsetRight(), 0);
+    }
+
     private WindowStateWithTask createWindow(Task task, int width, int height) {
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
         attrs.width = width;
@@ -426,4 +447,13 @@
 
         return new WindowStateWithTask(attrs, task);
     }
+
+    private DisplayCutout createDisplayCutoutFromRect(int left, int top, int right, int bottom) {
+        return DisplayCutout.fromBoundingPolygon(Arrays.asList(
+                new Point(left, top),
+                new Point (left, bottom),
+                new Point (right, bottom),
+                new Point (left, bottom)
+        ));
+    }
 }
diff --git a/services/tests/shortcutmanagerutils/Android.mk b/services/tests/shortcutmanagerutils/Android.mk
index c7657f6..0848fd5 100644
--- a/services/tests/shortcutmanagerutils/Android.mk
+++ b/services/tests/shortcutmanagerutils/Android.mk
@@ -21,7 +21,8 @@
 
 LOCAL_JAVA_LIBRARIES := \
     mockito-target \
-    legacy-android-test
+    legacy-android-test \
+    android.test.runner.stubs
 
 LOCAL_MODULE_TAGS := optional
 
diff --git a/services/tests/notification/Android.mk b/services/tests/uiservicestests/Android.mk
similarity index 89%
rename from services/tests/notification/Android.mk
rename to services/tests/uiservicestests/Android.mk
index 597a584..40e7878 100644
--- a/services/tests/notification/Android.mk
+++ b/services/tests/uiservicestests/Android.mk
@@ -1,5 +1,5 @@
 #########################################################################
-# Build FrameworksNotificationTests package
+# Build FrameworksUiServicesTests package
 #########################################################################
 
 LOCAL_PATH:= $(call my-dir)
@@ -25,12 +25,12 @@
     platform-test-annotations \
     testables
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 
 LOCAL_JACK_FLAGS := --multi-dex native
 LOCAL_DX_FLAGS := --multi-dex
 
-LOCAL_PACKAGE_NAME := FrameworksNotificationTests
+LOCAL_PACKAGE_NAME := FrameworksUiServicesTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
 
 LOCAL_CERTIFICATE := platform
diff --git a/services/tests/notification/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
similarity index 91%
rename from services/tests/notification/AndroidManifest.xml
rename to services/tests/uiservicestests/AndroidManifest.xml
index c20020a..621b457 100644
--- a/services/tests/notification/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -15,7 +15,7 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.frameworks.tests.notification">
+        package="com.android.frameworks.tests.uiservices">
 
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
@@ -32,6 +32,6 @@
 
     <instrumentation
         android:name="android.testing.TestableInstrumentation"
-        android:targetPackage="com.android.frameworks.tests.notification"
+        android:targetPackage="com.android.frameworks.tests.uiservices"
         android:label="Notification Tests" />
 </manifest>
diff --git a/services/tests/notification/AndroidTest.xml b/services/tests/uiservicestests/AndroidTest.xml
similarity index 82%
rename from services/tests/notification/AndroidTest.xml
rename to services/tests/uiservicestests/AndroidTest.xml
index 448bc3d..d3b9d4a 100644
--- a/services/tests/notification/AndroidTest.xml
+++ b/services/tests/uiservicestests/AndroidTest.xml
@@ -13,16 +13,16 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Runs Frameworks Notification Tests.">
+<configuration description="Runs Frameworks UI Services Tests.">
     <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
-        <option name="test-file-name" value="FrameworksNotificationTests.apk" />
+        <option name="test-file-name" value="FrameworksUiServicesTests.apk" />
     </target_preparer>
 
     <option name="test-suite-tag" value="apct" />
     <option name="test-suite-tag" value="framework-base-presubmit" />
-    <option name="test-tag" value="FrameworksNotificationTests" />
+    <option name="test-tag" value="FrameworksUiServicesTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="com.android.frameworks.tests.notification" />
+        <option name="package" value="com.android.frameworks.tests.uiservices" />
         <option name="runner" value="android.testing.TestableInstrumentation" />
     </test>
 </configuration>
diff --git a/services/tests/notification/src/com/android/server/notification/AlertRateLimiterTest.java b/services/tests/uiservicestests/src/com/android/server/notification/AlertRateLimiterTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/AlertRateLimiterTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/AlertRateLimiterTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationChannelExtractorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationChannelTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationChannelTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationStatsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationStatsTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationTestCase.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTestCase.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationTestCase.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationTestCase.java
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
similarity index 99%
rename from services/tests/notification/src/com/android/server/notification/ScheduleCalendarTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
index cbda12d..4eb4220 100644
--- a/services/tests/notification/src/com/android/server/notification/ScheduleCalendarTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -21,10 +21,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.service.notification.ScheduleCalendar;
 import android.service.notification.ZenModeConfig;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Slog;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/services/tests/notification/src/com/android/server/notification/ScheduleConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
similarity index 92%
rename from services/tests/notification/src/com/android/server/notification/ScheduleConditionProviderTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
index ddf46a0..610592f 100644
--- a/services/tests/notification/src/com/android/server/notification/ScheduleConditionProviderTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
@@ -1,46 +1,51 @@
 package com.android.server.notification;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.spy;
 
 import android.content.Intent;
 import android.net.Uri;
-import android.os.Looper;
 import android.service.notification.Condition;
+import android.service.notification.ScheduleCalendar;
 import android.service.notification.ZenModeConfig;
-import android.support.test.InstrumentationRegistry;
-import android.test.ServiceTestCase;
-import android.testing.TestableContext;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.MockitoAnnotations;
 
 import java.util.Calendar;
 import java.util.GregorianCalendar;
 
-public class ScheduleConditionProviderTest extends ServiceTestCase<ScheduleConditionProvider> {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ScheduleConditionProviderTest extends NotificationTestCase {
 
     ScheduleConditionProvider mService;
 
-    @Rule
-    public final TestableContext mContext =
-            new TestableContext(InstrumentationRegistry.getContext(), null);
-
-    public ScheduleConditionProviderTest() {
-        super(ScheduleConditionProvider.class);
-    }
-
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        Looper.prepare();
 
         Intent startIntent =
                 new Intent("com.android.server.notification.ScheduleConditionProvider");
         startIntent.setPackage("android");
-        bindService(startIntent);
-        mService = spy(getService());
+        ScheduleConditionProvider service = new ScheduleConditionProvider();
+        service.attach(
+                getContext(),
+                null,               // ActivityThread not actually used in Service
+                ScheduleConditionProvider.class.getName(),
+                null,               // token not needed when not talking with the activity manager
+                null,
+                null                // mocked services don't talk with the activity manager
+                );
+        service.onCreate();
+        service.onBind(startIntent);
+        mService = spy(service);
    }
 
     @Test
diff --git a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/ValidateNotificationPeopleTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 4527879..3d20a64 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -16,10 +16,12 @@
 
 package com.android.server.usage;
 
+import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
 import static android.app.usage.UsageStatsManager.REASON_FORCED;
 import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
 import static android.app.usage.UsageStatsManager.REASON_USAGE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
@@ -378,8 +380,12 @@
                     Slog.d(TAG, "   Checking idle state for " + packageName);
                 }
                 if (isSpecial) {
+                    synchronized (mAppIdleLock) {
+                        mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
+                                STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT);
+                    }
                     maybeInformListeners(packageName, userId, elapsedRealtime,
-                            STANDBY_BUCKET_ACTIVE);
+                            STANDBY_BUCKET_EXEMPTED);
                 } else {
                     synchronized (mAppIdleLock) {
                         String bucketingReason = mAppIdleHistory.getAppStandbyReason(packageName,
@@ -389,7 +395,8 @@
                             continue;
                         }
                         // If the bucket was moved up due to usage, let the timeouts apply.
-                        if (REASON_USAGE.equals(bucketingReason)
+                        if (REASON_DEFAULT.equals(bucketingReason)
+                                || REASON_USAGE.equals(bucketingReason)
                                 || REASON_TIMEOUT.equals(bucketingReason)) {
                             int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId,
                                     elapsedRealtime);
@@ -886,6 +893,11 @@
                 String packageName = pi.packageName;
                 if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
                     mAppIdleHistory.reportUsage(packageName, userId, elapsedRealtime);
+                    if (isAppSpecial(packageName, UserHandle.getAppId(pi.applicationInfo.uid),
+                            userId)) {
+                        mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
+                                STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT);
+                    }
                 }
             }
         }
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index dd2e192..e76d211 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -29,18 +29,21 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.usb.descriptors.UsbDescriptorParser;
+import com.android.server.usb.descriptors.UsbDeviceDescriptor;
 import com.android.server.usb.descriptors.report.TextReportCanvas;
 import com.android.server.usb.descriptors.tree.UsbDescriptorsTree;
 
-import java.util.Collection;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.HashMap;
+import java.util.LinkedList;
 
 /**
  * UsbHostManager manages USB state in host mode.
  */
 public class UsbHostManager {
     private static final String TAG = UsbHostManager.class.getSimpleName();
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
 
     private final Context mContext;
 
@@ -63,6 +66,76 @@
     @GuardedBy("mHandlerLock")
     private ComponentName mUsbDeviceConnectionHandler;
 
+    /*
+     * Member used for tracking connections & disconnections
+     */
+    static final SimpleDateFormat sFormat = new SimpleDateFormat("MM-dd HH:mm:ss:SSS");
+    private static final int MAX_CONNECT_RECORDS = 32;
+    private int mNumConnects;    // TOTAL # of connect/disconnect
+    private final LinkedList<ConnectionRecord> mConnections = new LinkedList<ConnectionRecord>();
+    private ConnectionRecord mLastConnect;
+
+    /*
+     * ConnectionRecord
+     * Stores connection/disconnection data.
+     */
+    class ConnectionRecord {
+        long mTimestamp;        // Same time-base as system log.
+        String mDeviceAddress;
+
+        static final int CONNECT = 0;
+        static final int DISCONNECT = 1;
+        final int mMode;
+        final byte[] mDescriptors;
+
+        ConnectionRecord(String deviceAddress, int mode, byte[] descriptors) {
+            mTimestamp = System.currentTimeMillis();
+            mDeviceAddress = deviceAddress;
+            mMode = mode;
+            mDescriptors = descriptors;
+        }
+
+        private String formatTime() {
+            return (new StringBuilder(sFormat.format(new Date(mTimestamp)))).toString();
+        }
+
+        void dumpShort(IndentingPrintWriter pw) {
+            if (mMode == CONNECT) {
+                pw.println(formatTime() + " Connect " + mDeviceAddress);
+                UsbDescriptorParser parser = new UsbDescriptorParser(mDeviceAddress, mDescriptors);
+
+                UsbDeviceDescriptor deviceDescriptor = parser.getDeviceDescriptor();
+
+                pw.println("manfacturer:0x" + Integer.toHexString(deviceDescriptor.getVendorID())
+                        + " product:" + Integer.toHexString(deviceDescriptor.getProductID()));
+                pw.println("isHeadset[in: " + parser.isInputHeadset()
+                        + " , out: " + parser.isOutputHeadset() + "]");
+            } else {
+                pw.println(formatTime() + " Disconnect " + mDeviceAddress);
+            }
+        }
+
+        void dumpLong(IndentingPrintWriter pw) {
+            if (mMode == CONNECT) {
+                pw.println(formatTime() + " Connect " + mDeviceAddress);
+                UsbDescriptorParser parser = new UsbDescriptorParser(mDeviceAddress, mDescriptors);
+                StringBuilder stringBuilder = new StringBuilder();
+                UsbDescriptorsTree descriptorTree = new UsbDescriptorsTree();
+                descriptorTree.parse(parser);
+                descriptorTree.report(new TextReportCanvas(parser, stringBuilder));
+
+                stringBuilder.append("isHeadset[in: " + parser.isInputHeadset()
+                        + " , out: " + parser.isOutputHeadset() + "]");
+                pw.println(stringBuilder.toString());
+            } else {
+                pw.println(formatTime() + " Disconnect " + mDeviceAddress);
+            }
+        }
+    }
+
+    /*
+     * UsbHostManager
+     */
     public UsbHostManager(Context context, UsbAlsaManager alsaManager,
             UsbSettingsManager settingsManager) {
         mContext = context;
@@ -124,6 +197,19 @@
 
     }
 
+    private void addConnectionRecord(String deviceAddress, int mode, byte[] rawDescriptors) {
+        mNumConnects++;
+        while (mConnections.size() >= MAX_CONNECT_RECORDS) {
+            mConnections.removeFirst();
+        }
+        ConnectionRecord rec =
+                new ConnectionRecord(deviceAddress, mode, rawDescriptors);
+        mConnections.add(rec);
+        if (mode == ConnectionRecord.CONNECT) {
+            mLastConnect = rec;
+        }
+    }
+
     /* Called from JNI in monitorUsbHostBus() to report new USB devices
        Returns true if successful, i.e. the USB Audio device descriptors are
        correctly parsed and the unique device is added to the audio device list.
@@ -174,6 +260,10 @@
                         + " , out: " + isOutputHeadset + "]");
 
                 mUsbAlsaManager.usbDeviceAdded(newDevice, isInputHeadset, isOutputHeadset);
+
+                // Tracking
+                addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
+                        parser.getRawDescriptors());
             } else {
                 Slog.e(TAG, "Error parsing USB device descriptors for " + deviceAddress);
                 return false;
@@ -196,6 +286,9 @@
                 mUsbAlsaManager.usbDeviceRemoved(device);
                 mSettingsManager.usbDeviceRemoved(device);
                 getCurrentUserSettings().usbDeviceRemoved(device);
+
+                // Tracking
+                addConnectionRecord(deviceAddress, ConnectionRecord.DISCONNECT, null);
             }
         }
     }
@@ -249,26 +342,15 @@
                 pw.println("  " + name + ": " + mDevices.get(name));
             }
 
-            Collection<UsbDevice> devices = mDevices.values();
-            if (devices.size() != 0) {
-                pw.println("USB Peripheral Descriptors");
-                for (UsbDevice device : devices) {
-                    StringBuilder stringBuilder = new StringBuilder();
+            pw.println("" + mNumConnects + " total connects/disconnects");
+            pw.println("Last " + mConnections.size() + " connections/disconnections");
+            for (ConnectionRecord rec : mConnections) {
+                rec.dumpShort(pw);
+            }
 
-                    UsbDescriptorParser parser = new UsbDescriptorParser(device.getDeviceName());
-                    if (parser.parseDevice()) {
-                        UsbDescriptorsTree descriptorTree = new UsbDescriptorsTree();
-                        descriptorTree.parse(parser);
-
-                        descriptorTree.report(new TextReportCanvas(parser, stringBuilder));
-
-                        stringBuilder.append("isHeadset[in: " + parser.isInputHeadset()
-                                + " , out: " + parser.isOutputHeadset() + "]");
-                    } else {
-                        stringBuilder.append("Error Parsing USB Descriptors");
-                    }
-                    pw.println(stringBuilder.toString());
-                }
+            if (mLastConnect != null) {
+                pw.println("Last Connected USB Device:");
+                mLastConnect.dumpLong(pw);
             }
         }
 
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 6c6bd01..78c7fdc 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -32,7 +32,7 @@
 
     // Descriptor Objects
     private static final int DESCRIPTORS_ALLOC_SIZE = 128;
-    private ArrayList<UsbDescriptor> mDescriptors = new ArrayList<UsbDescriptor>();
+    private final ArrayList<UsbDescriptor> mDescriptors;
 
     private UsbDeviceDescriptor mDeviceDescriptor;
     private UsbConfigDescriptor mCurConfigDescriptor;
@@ -45,6 +45,28 @@
 
     public UsbDescriptorParser(String deviceAddr) {
         mDeviceAddr = deviceAddr;
+        mDescriptors = new ArrayList<UsbDescriptor>(DESCRIPTORS_ALLOC_SIZE);
+    }
+
+    /**
+     * Connect this parser to an existing set of already parsed descriptors.
+     * This is useful for reporting.
+     */
+    public UsbDescriptorParser(String deviceAddr, ArrayList<UsbDescriptor> descriptors) {
+        mDeviceAddr = deviceAddr;
+        mDescriptors = descriptors;
+        //TODO some error checking here....
+        mDeviceDescriptor = (UsbDeviceDescriptor) descriptors.get(0);
+    }
+
+    /**
+     * Connect this parser to an byte array containing unparsed (raw) device descriptors
+     * to be parsed (and parse them). Useful for parsing a stored descriptor buffer.
+     */
+    public UsbDescriptorParser(String deviceAddr, byte[] rawDescriptors) {
+        mDeviceAddr = deviceAddr;
+        mDescriptors = new ArrayList<UsbDescriptor>(DESCRIPTORS_ALLOC_SIZE);
+        parseDescriptors(rawDescriptors);
     }
 
     public String getDeviceAddr() {
@@ -196,8 +218,6 @@
         if (DEBUG) {
             Log.d(TAG, "parseDescriptors() - start");
         }
-        // This will allow us to (probably) alloc mDescriptors just once.
-        mDescriptors = new ArrayList<UsbDescriptor>(DESCRIPTORS_ALLOC_SIZE);
 
         ByteStream stream = new ByteStream(descriptors);
         while (stream.available() > 0) {
@@ -241,7 +261,7 @@
             ? parseDescriptors(rawDescriptors) : false;
     }
 
-    private byte[] getRawDescriptors() {
+    public byte[] getRawDescriptors() {
         return getRawDescriptors_native(mDeviceAddr);
     }
 
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 51c805d..cd3fdee 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -66,6 +66,7 @@
     private SoundTriggerDbHelper mDbHelper;
     private SoundTriggerHelper mSoundTriggerHelper;
     private final TreeMap<UUID, SoundModel> mLoadedModels;
+    private Object mCallbacksLock;
     private final TreeMap<UUID, LocalSoundTriggerRecognitionStatusCallback> mIntentCallbacks;
     private PowerManager.WakeLock mWakelock;
 
@@ -75,6 +76,7 @@
         mServiceStub = new SoundTriggerServiceStub();
         mLocalSoundTriggerService = new LocalSoundTriggerService(context);
         mLoadedModels = new TreeMap<UUID, SoundModel>();
+        mCallbacksLock = new Object();
         mIntentCallbacks = new TreeMap<UUID, LocalSoundTriggerRecognitionStatusCallback>();
         mLock = new Object();
     }
@@ -211,7 +213,9 @@
                 // don't know if the other model is loaded.
                 if (oldModel != null && !oldModel.equals(soundModel)) {
                     mSoundTriggerHelper.unloadGenericSoundModel(soundModel.uuid);
-                    mIntentCallbacks.remove(soundModel.uuid);
+                    synchronized (mCallbacksLock) {
+                        mIntentCallbacks.remove(soundModel.uuid);
+                    }
                 }
                 mLoadedModels.put(soundModel.uuid, soundModel);
             }
@@ -240,7 +244,9 @@
                 // don't know if the other model is loaded.
                 if (oldModel != null && !oldModel.equals(soundModel)) {
                     mSoundTriggerHelper.unloadKeyphraseSoundModel(soundModel.keyphrases[0].id);
-                    mIntentCallbacks.remove(soundModel.uuid);
+                    synchronized (mCallbacksLock) {
+                        mIntentCallbacks.remove(soundModel.uuid);
+                    }
                 }
                 mLoadedModels.put(soundModel.uuid, soundModel);
             }
@@ -262,8 +268,10 @@
                     Slog.e(TAG, soundModelId + " is not loaded");
                     return STATUS_ERROR;
                 }
-                LocalSoundTriggerRecognitionStatusCallback callback = mIntentCallbacks.get(
-                        soundModelId.getUuid());
+                LocalSoundTriggerRecognitionStatusCallback callback = null;
+                synchronized (mCallbacksLock) {
+                    callback = mIntentCallbacks.get(soundModelId.getUuid());
+                }
                 if (callback != null) {
                     Slog.e(TAG, soundModelId + " is already running");
                     return STATUS_ERROR;
@@ -291,7 +299,9 @@
                     Slog.e(TAG, "Failed to start model: " + ret);
                     return ret;
                 }
-                mIntentCallbacks.put(soundModelId.getUuid(), callback);
+                synchronized (mCallbacksLock) {
+                    mIntentCallbacks.put(soundModelId.getUuid(), callback);
+                }
             }
             return STATUS_OK;
         }
@@ -310,8 +320,10 @@
                     Slog.e(TAG, soundModelId + " is not loaded");
                     return STATUS_ERROR;
                 }
-                LocalSoundTriggerRecognitionStatusCallback callback = mIntentCallbacks.get(
-                        soundModelId.getUuid());
+                LocalSoundTriggerRecognitionStatusCallback callback = null;
+                synchronized (mCallbacksLock) {
+                     callback = mIntentCallbacks.get(soundModelId.getUuid());
+                }
                 if (callback == null) {
                     Slog.e(TAG, soundModelId + " is not running");
                     return STATUS_ERROR;
@@ -334,7 +346,9 @@
                     Slog.e(TAG, "Failed to stop model: " + ret);
                     return ret;
                 }
-                mIntentCallbacks.remove(soundModelId.getUuid());
+                synchronized (mCallbacksLock) {
+                    mIntentCallbacks.remove(soundModelId.getUuid());
+                }
             }
             return STATUS_OK;
         }
@@ -379,14 +393,14 @@
         public boolean isRecognitionActive(ParcelUuid parcelUuid) {
             enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
             if (!isInitialized()) return false;
-            synchronized (mLock) {
+            synchronized (mCallbacksLock) {
                 LocalSoundTriggerRecognitionStatusCallback callback =
                         mIntentCallbacks.get(parcelUuid.getUuid());
                 if (callback == null) {
                     return false;
                 }
-                return mSoundTriggerHelper.isRecognitionRequested(parcelUuid.getUuid());
             }
+            return mSoundTriggerHelper.isRecognitionRequested(parcelUuid.getUuid());
         }
     }
 
@@ -513,7 +527,7 @@
 
         private void removeCallback(boolean releaseWakeLock) {
             mCallbackIntent = null;
-            synchronized (mLock) {
+            synchronized (mCallbacksLock) {
                 mIntentCallbacks.remove(mUuid);
                 if (releaseWakeLock) {
                     mWakelock.release();
@@ -523,7 +537,7 @@
     }
 
     private void grabWakeLock() {
-        synchronized (mLock) {
+        synchronized (mCallbacksLock) {
             if (mWakelock == null) {
                 PowerManager pm = ((PowerManager) mContext.getSystemService(Context.POWER_SERVICE));
                 mWakelock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
@@ -537,7 +551,7 @@
         public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
                 String resultData, Bundle resultExtras) {
             // We're only ever invoked when the callback is done, so release the lock.
-            synchronized (mLock) {
+            synchronized (mCallbacksLock) {
                 mWakelock.release();
             }
         }
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 16150ba..2091101 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -416,8 +416,15 @@
          */
         public static final int PROPERTY_SELF_MANAGED = 0x00000100;
 
+        /**
+         * Indicates the call used Assisted Dialing.
+         * See also {@link Connection#PROPERTY_ASSISTED_DIALING_USED}
+         * @hide
+         */
+        public static final int PROPERTY_ASSISTED_DIALING_USED = 0x00000200;
+
         //******************************************************************************************
-        // Next PROPERTY value: 0x00000200
+        // Next PROPERTY value: 0x00000400
         //******************************************************************************************
 
         private final String mTelecomCallId;
@@ -577,6 +584,9 @@
             if(hasProperty(properties, PROPERTY_HAS_CDMA_VOICE_PRIVACY)) {
                 builder.append(" PROPERTY_HAS_CDMA_VOICE_PRIVACY");
             }
+            if(hasProperty(properties, PROPERTY_ASSISTED_DIALING_USED)) {
+                builder.append(" PROPERTY_ASSISTED_DIALING_USED");
+            }
             builder.append("]");
             return builder.toString();
         }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 2bb1c4e..aaef8d3 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -23,7 +23,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Notification;
 import android.bluetooth.BluetoothDevice;
 import android.content.Intent;
@@ -397,13 +396,17 @@
     /**
      * Set by the framework to indicate that a connection has an active RTT session associated with
      * it.
-     * @hide
      */
-    @TestApi
     public static final int PROPERTY_IS_RTT = 1 << 8;
 
+    /**
+     * Set by the framework to indicate that a connection is using assisted dialing.
+     * @hide
+     */
+    public static final int PROPERTY_ASSISTED_DIALING_USED = 1 << 9;
+
     //**********************************************************************************************
-    // Next PROPERTY value: 1<<9
+    // Next PROPERTY value: 1<<10
     //**********************************************************************************************
 
     /**
@@ -831,9 +834,7 @@
 
     /**
      * Provides methods to read and write RTT data to/from the in-call app.
-     * @hide
      */
-    @TestApi
     public static final class RttTextStream {
         private static final int READ_BUFFER_SIZE = 1000;
         private final InputStreamReader mPipeFromInCall;
@@ -2608,10 +2609,8 @@
     /**
      * Informs listeners that a previously requested RTT session via
      * {@link ConnectionRequest#isRequestingRtt()} or
-     * {@link #onStartRtt(ParcelFileDescriptor, ParcelFileDescriptor)} has succeeded.
-     * @hide
+     * {@link #onStartRtt(RttTextStream)} has succeeded.
      */
-    @TestApi
     public final void sendRttInitiationSuccess() {
         setRttProperty();
         mListeners.forEach((l) -> l.onRttInitiationSuccess(Connection.this));
@@ -2619,14 +2618,11 @@
 
     /**
      * Informs listeners that a previously requested RTT session via
-     * {@link ConnectionRequest#isRequestingRtt()} or
-     * {@link #onStartRtt(ParcelFileDescriptor, ParcelFileDescriptor)}
+     * {@link ConnectionRequest#isRequestingRtt()} or {@link #onStartRtt(RttTextStream)}
      * has failed.
      * @param reason One of the reason codes defined in {@link RttModifyStatus}, with the
      *               exception of {@link RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}.
-     * @hide
      */
-    @TestApi
     public final void sendRttInitiationFailure(int reason) {
         unsetRttProperty();
         mListeners.forEach((l) -> l.onRttInitiationFailure(Connection.this, reason));
@@ -2635,9 +2631,7 @@
     /**
      * Informs listeners that a currently active RTT session has been terminated by the remote
      * side of the coll.
-     * @hide
      */
-    @TestApi
     public final void sendRttSessionRemotelyTerminated() {
         mListeners.forEach((l) -> l.onRttSessionRemotelyTerminated(Connection.this));
     }
@@ -2645,9 +2639,7 @@
     /**
      * Informs listeners that the remote side of the call has requested an upgrade to include an
      * RTT session in the call.
-     * @hide
      */
-    @TestApi
     public final void sendRemoteRttRequest() {
         mListeners.forEach((l) -> l.onRemoteRttRequest(Connection.this));
     }
@@ -2864,17 +2856,13 @@
      * request, respectively.
      * @param rttTextStream The object that should be used to send text to or receive text from
      *                      the in-call app.
-     * @hide
      */
-    @TestApi
     public void onStartRtt(@NonNull RttTextStream rttTextStream) {}
 
     /**
      * Notifies this {@link Connection} that it should terminate any existing RTT communication
      * channel. No response to Telecom is needed for this method.
-     * @hide
      */
-    @TestApi
     public void onStopRtt() {}
 
     /**
@@ -2882,11 +2870,9 @@
      * request sent via {@link #sendRemoteRttRequest}. Acceptance of the request is
      * indicated by the supplied {@link RttTextStream} being non-null, and rejection is
      * indicated by {@code rttTextStream} being {@code null}
-     * @hide
      * @param rttTextStream The object that should be used to send text to or receive text from
      *                      the in-call app.
      */
-    @TestApi
     public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {}
 
     /**
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index e169e5f..658b473 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.TestApi;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -310,9 +309,7 @@
      * send and receive RTT text to/from the in-call app.
      * @return An instance of {@link android.telecom.Connection.RttTextStream}, or {@code null}
      * if this connection request is not requesting an RTT session upon connection establishment.
-     * @hide
      */
-    @TestApi
     public Connection.RttTextStream getRttTextStream() {
         if (isRequestingRtt()) {
             return new Connection.RttTextStream(mRttPipeToInCall, mRttPipeFromInCall);
@@ -324,9 +321,7 @@
     /**
      * Convenience method for determining whether the ConnectionRequest is requesting an RTT session
      * @return {@code true} if RTT is requested, {@code false} otherwise.
-     * @hide
      */
-    @TestApi
     public boolean isRequestingRtt() {
         return mRttPipeFromInCall != null && mRttPipeToInCall != null;
     }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 2834201..7001615 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -143,6 +143,8 @@
     private static final String SESSION_START_RTT = "CS.+RTT";
     private static final String SESSION_STOP_RTT = "CS.-RTT";
     private static final String SESSION_RTT_UPGRADE_RESPONSE = "CS.rTRUR";
+    private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL";
+    private static final String SESSION_CONNECTION_SERVICE_FOCUS_GAINED = "CS.cSFG";
 
     private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;
     private static final int MSG_CREATE_CONNECTION = 2;
@@ -172,6 +174,8 @@
     private static final int MSG_ON_STOP_RTT = 27;
     private static final int MSG_RTT_UPGRADE_RESPONSE = 28;
     private static final int MSG_CREATE_CONNECTION_COMPLETE = 29;
+    private static final int MSG_CONNECTION_SERVICE_FOCUS_LOST = 30;
+    private static final int MSG_CONNECTION_SERVICE_FOCUS_GAINED = 31;
 
     private static Connection sNullConnection;
 
@@ -591,6 +595,26 @@
                 Log.endSession();
             }
         }
+
+        @Override
+        public void connectionServiceFocusLost(Session.Info sessionInfo) throws RemoteException {
+            Log.startSession(sessionInfo, SESSION_CONNECTION_SERVICE_FOCUS_LOST);
+            try {
+                mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_LOST).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
+        public void connectionServiceFocusGained(Session.Info sessionInfo) throws RemoteException {
+            Log.startSession(sessionInfo, SESSION_CONNECTION_SERVICE_FOCUS_GAINED);
+            try {
+                mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_GAINED).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
     };
 
     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -1012,6 +1036,12 @@
                     }
                     break;
                 }
+                case MSG_CONNECTION_SERVICE_FOCUS_GAINED:
+                    onConnectionServiceFocusGained();
+                    break;
+                case MSG_CONNECTION_SERVICE_FOCUS_LOST:
+                    onConnectionServiceFocusLost();
+                    break;
                 default:
                     break;
             }
@@ -1873,6 +1903,16 @@
     }
 
     /**
+     * Call to inform Telecom that your {@link ConnectionService} has released call resources (e.g
+     * microphone, camera).
+     *
+     * @see ConnectionService#onConnectionServiceFocusLost()
+     */
+    public final void connectionServiceFocusReleased() {
+        mAdapter.onConnectionServiceFocusReleased();
+    }
+
+    /**
      * Adds a connection created by the {@link ConnectionService} and informs telecom of the new
      * connection.
      *
@@ -2146,6 +2186,20 @@
     public void onRemoteExistingConnectionAdded(RemoteConnection connection) {}
 
     /**
+     * Called when the {@link ConnectionService} has lost the call focus.
+     * The {@link ConnectionService} should release the call resources and invokes
+     * {@link ConnectionService#connectionServiceFocusReleased()} to inform telecom that it has
+     * released the call resources.
+     */
+    public void onConnectionServiceFocusLost() {}
+
+    /**
+     * Called when the {@link ConnectionService} has gained the call focus. The
+     * {@link ConnectionService} can acquire the call resources at this time.
+     */
+    public void onConnectionServiceFocusGained() {}
+
+    /**
      * @hide
      */
     public boolean containsConference(Conference conference) {
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 92a9dc2..0d319bb 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -628,4 +628,17 @@
             }
         }
     }
+
+    /**
+     * Notifies Telecom that the {@link ConnectionService} has released the call resource.
+     */
+    void onConnectionServiceFocusReleased() {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
+            try {
+                Log.d(this, "onConnectionServiceFocusReleased");
+                adapter.onConnectionServiceFocusReleased(Log.getExternalSession());
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
 }
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 3fbdeb1..3e1bf77 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -73,6 +73,7 @@
     private static final int MSG_ON_RTT_REMOTELY_TERMINATED = 32;
     private static final int MSG_ON_RTT_UPGRADE_REQUEST = 33;
     private static final int MSG_SET_PHONE_ACCOUNT_CHANGED = 34;
+    private static final int MSG_CONNECTION_SERVICE_FOCUS_RELEASED = 35;
 
     private final IConnectionServiceAdapter mDelegate;
 
@@ -329,6 +330,9 @@
                     }
                     break;
                 }
+                case MSG_CONNECTION_SERVICE_FOCUS_RELEASED:
+                    mDelegate.onConnectionServiceFocusReleased(null /*Session.Info*/);
+                    break;
             }
         }
     };
@@ -601,6 +605,11 @@
             args.arg2 = pHandle;
             mHandler.obtainMessage(MSG_SET_PHONE_ACCOUNT_CHANGED, args).sendToTarget();
         }
+
+        @Override
+        public void onConnectionServiceFocusReleased(Session.Info sessionInfo) {
+            mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_RELEASED).sendToTarget();
+        }
     };
 
     public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) {
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 85906ad..59ce590 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -213,6 +213,9 @@
         }
 
         @Override
+        public void onConnectionServiceFocusReleased(Session.Info sessionInfo) {}
+
+        @Override
         public void addConferenceCall(
                 final String callId, ParcelableConference parcel, Session.Info sessionInfo) {
             RemoteConference conference = new RemoteConference(callId,
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 92d458f..15355ac 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -582,13 +582,29 @@
             "android.telecom.extra.CALL_BACK_INTENT";
 
     /**
+     * The dialer activity responsible for placing emergency calls from, for example, a locked
+     * keyguard.
+     * @hide
+     */
+    public static final ComponentName EMERGENCY_DIALER_COMPONENT =
+            ComponentName.createRelative("com.android.phone", ".EmergencyDialer");
+
+    /**
+     * The boolean indicated by this extra controls whether or not a call is eligible to undergo
+     * assisted dialing. This extra is stored under {@link #EXTRA_OUTGOING_CALL_EXTRAS}.
+     * @hide
+     */
+    public static final String EXTRA_USE_ASSISTED_DIALING =
+            "android.telecom.extra.USE_ASSISTED_DIALING";
+
+    /**
      * The following 4 constants define how properties such as phone numbers and names are
      * displayed to the user.
      */
 
     /**
      * Indicates that the address or number of a call is allowed to be displayed for caller ID.
-    */
+     */
     public static final int PRESENTATION_ALLOWED = 1;
 
     /**
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index e428286..a740566 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -100,4 +100,8 @@
 
     void respondToRttUpgradeRequest(String callId, in ParcelFileDescriptor fromInCall,
     in ParcelFileDescriptor toInCall, in Session.Info sessionInfo);
+
+    void connectionServiceFocusLost(in Session.Info sessionInfo);
+
+    void connectionServiceFocusGained(in Session.Info sessionInfo);
 }
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index da2015f..be474bd 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -119,4 +119,6 @@
 
     void onPhoneAccountChanged(String callId, in PhoneAccountHandle pHandle,
     in Session.Info sessionInfo);
+
+    void onConnectionServiceFocusReleased(in Session.Info sessionInfo);
 }
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index c3a2ceb..56e1e64 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -280,6 +280,36 @@
      * {@hide}
      */
     public static final int NORMAL_UNSPECIFIED = 65;
+
+    /**
+     * Stk Call Control modified DIAL request to video DIAL request.
+     * {@hide}
+     */
+    public static final int DIAL_MODIFIED_TO_DIAL_VIDEO = 66;
+
+    /**
+     * Stk Call Control modified Video DIAL request to SS request.
+     * {@hide}
+     */
+    public static final int DIAL_VIDEO_MODIFIED_TO_SS = 67;
+
+    /**
+     * Stk Call Control modified Video DIAL request to USSD request.
+     * {@hide}
+     */
+    public static final int DIAL_VIDEO_MODIFIED_TO_USSD = 68;
+
+    /**
+     * Stk Call Control modified Video DIAL request to DIAL request.
+     * {@hide}
+     */
+    public static final int DIAL_VIDEO_MODIFIED_TO_DIAL = 69;
+
+    /**
+     * Stk Call Control modified Video DIAL request to Video DIAL request.
+     * {@hide}
+     */
+    public static final int DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 70;
     //*********************************************************************************************
     // When adding a disconnect type:
     // 1) Update toString() with the newly added disconnect type.
@@ -382,6 +412,16 @@
             return "DIAL_MODIFIED_TO_SS";
         case DIAL_MODIFIED_TO_DIAL:
             return "DIAL_MODIFIED_TO_DIAL";
+        case DIAL_MODIFIED_TO_DIAL_VIDEO:
+            return "DIAL_MODIFIED_TO_DIAL_VIDEO";
+        case DIAL_VIDEO_MODIFIED_TO_SS:
+            return "DIAL_VIDEO_MODIFIED_TO_SS";
+        case DIAL_VIDEO_MODIFIED_TO_USSD:
+            return "DIAL_VIDEO_MODIFIED_TO_USSD";
+        case DIAL_VIDEO_MODIFIED_TO_DIAL:
+            return "DIAL_VIDEO_MODIFIED_TO_DIAL";
+        case DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO:
+            return "DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO";
         case ERROR_UNSPECIFIED:
             return "ERROR_UNSPECIFIED";
         case OUTGOING_FAILURE:
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 9ccfa94..c7e5131 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -408,7 +408,7 @@
     /**
      * Callback invoked when device call state changes.
      * @param state call state
-     * @param incomingNumber incoming call phone number. If application does not have
+     * @param phoneNumber call phone number. If application does not have
      * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission, an empty
      * string will be passed as an argument.
      *
@@ -416,7 +416,7 @@
      * @see TelephonyManager#CALL_STATE_RINGING
      * @see TelephonyManager#CALL_STATE_OFFHOOK
      */
-    public void onCallStateChanged(int state, String incomingNumber) {
+    public void onCallStateChanged(int state, String phoneNumber) {
         // default implementation empty
     }
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 81806e5..99fc9b3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6641,6 +6641,7 @@
      * @return PRLVersion or null if error.
      * @hide
      */
+    @SystemApi
     public String getCdmaPrlVersion() {
         return getCdmaPrlVersion(getSubId());
     }
diff --git a/telephony/java/android/telephony/data/DataCallResponse.aidl b/telephony/java/android/telephony/data/DataCallResponse.aidl
new file mode 100644
index 0000000..e4cfd69
--- /dev/null
+++ b/telephony/java/android/telephony/data/DataCallResponse.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright 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.
+ */
+
+/** @hide */
+package android.telephony.data;
+
+parcelable DataCallResponse;
diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java
new file mode 100644
index 0000000..8cdad3f
--- /dev/null
+++ b/telephony/java/android/telephony/data/DataCallResponse.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2009 Qualcomm Innovation Center, Inc.  All Rights Reserved.
+ * 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.
+ */
+
+package android.telephony.data;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Description of the response of a setup data call connection request.
+ *
+ * @hide
+ */
+@SystemApi
+public final class DataCallResponse implements Parcelable {
+    private final int mStatus;
+    private final int mSuggestedRetryTime;
+    private final int mCid;
+    private final int mActive;
+    private final String mType;
+    private final String mIfname;
+    private final List<InterfaceAddress> mAddresses;
+    private final List<InetAddress> mDnses;
+    private final List<InetAddress> mGateways;
+    private final List<String> mPcscfs;
+    private final int mMtu;
+
+    /**
+     * @param status Data call fail cause. 0 indicates no error.
+     * @param suggestedRetryTime The suggested data retry time in milliseconds.
+     * @param cid The unique id of the data connection.
+     * @param active Data connection active status. 0 = inactive, 1 = active/physical link down,
+     *               2 = active/physical link up.
+     * @param type The connection protocol, should be one of the PDP_type values in TS 27.007
+     *             section 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP".
+     * @param ifname The network interface name.
+     * @param addresses A list of addresses with optional "/" prefix length, e.g.,
+     *                  "192.0.1.3" or "192.0.1.11/16 2001:db8::1/64". Typically 1 IPv4 or 1 IPv6 or
+     *                  one of each. If the prefix length is absent the addresses are assumed to be
+     *                  point to point with IPv4 having a prefix length of 32 and IPv6 128.
+     * @param dnses A list of DNS server addresses, e.g., "192.0.1.3" or
+     *              "192.0.1.11 2001:db8::1". Null if no dns server addresses returned.
+     * @param gateways A list of default gateway addresses, e.g., "192.0.1.3" or
+     *                 "192.0.1.11 2001:db8::1". When null, the addresses represent point to point
+     *                 connections.
+     * @param pcscfs A list of Proxy Call State Control Function address via PCO(Protocol
+     *               Configuration Option) for IMS client.
+     * @param mtu MTU (Maximum transmission unit) received from network Value <= 0 means network has
+     *            either not sent a value or sent an invalid value.
+     */
+    public DataCallResponse(int status, int suggestedRetryTime, int cid, int active,
+                            @Nullable String type, @Nullable String ifname,
+                            @Nullable List<InterfaceAddress> addresses,
+                            @Nullable List<InetAddress> dnses,
+                            @Nullable List<InetAddress> gateways,
+                            @Nullable List<String> pcscfs, int mtu) {
+        mStatus = status;
+        mSuggestedRetryTime = suggestedRetryTime;
+        mCid = cid;
+        mActive = active;
+        mType = (type == null) ? "" : type;
+        mIfname = (ifname == null) ? "" : ifname;
+        mAddresses = (addresses == null) ? new ArrayList<>() : addresses;
+        mDnses = (dnses == null) ? new ArrayList<>() : dnses;
+        mGateways = (gateways == null) ? new ArrayList<>() : gateways;
+        mPcscfs = (pcscfs == null) ? new ArrayList<>() : pcscfs;
+        mMtu = mtu;
+    }
+
+    public DataCallResponse(Parcel source) {
+        mStatus = source.readInt();
+        mSuggestedRetryTime = source.readInt();
+        mCid = source.readInt();
+        mActive = source.readInt();
+        mType = source.readString();
+        mIfname = source.readString();
+        mAddresses = new ArrayList<>();
+        source.readList(mAddresses, InterfaceAddress.class.getClassLoader());
+        mDnses = new ArrayList<>();
+        source.readList(mDnses, InetAddress.class.getClassLoader());
+        mGateways = new ArrayList<>();
+        source.readList(mGateways, InetAddress.class.getClassLoader());
+        mPcscfs = new ArrayList<>();
+        source.readList(mPcscfs, InetAddress.class.getClassLoader());
+        mMtu = source.readInt();
+    }
+
+    /**
+     * @return Data call fail cause. 0 indicates no error.
+     */
+    public int getStatus() { return mStatus; }
+
+    /**
+     * @return The suggested data retry time in milliseconds.
+     */
+    public int getSuggestedRetryTime() { return mSuggestedRetryTime; }
+
+
+    /**
+     * @return The unique id of the data connection.
+     */
+    public int getCallId() { return mCid; }
+
+    /**
+     * @return 0 = inactive, 1 = active/physical link down, 2 = active/physical link up.
+     */
+    public int getActive() { return mActive; }
+
+    /**
+     * @return The connection protocol, should be one of the PDP_type values in TS 27.007 section
+     * 10.1.1. For example, "IP", "IPV6", "IPV4V6", or "PPP".
+     */
+    @NonNull
+    public String getType() { return mType; }
+
+    /**
+     * @return The network interface name.
+     */
+    @NonNull
+    public String getIfname() { return mIfname; }
+
+    /**
+     * @return A list of {@link InterfaceAddress}
+     */
+    @NonNull
+    public List<InterfaceAddress> getAddresses() { return mAddresses; }
+
+    /**
+     * @return A list of DNS server addresses, e.g., "192.0.1.3" or
+     * "192.0.1.11 2001:db8::1". Empty list if no dns server addresses returned.
+     */
+    @NonNull
+    public List<InetAddress> getDnses() { return mDnses; }
+
+    /**
+     * @return A list of default gateway addresses, e.g., "192.0.1.3" or
+     * "192.0.1.11 2001:db8::1". Empty list if the addresses represent point to point connections.
+     */
+    @NonNull
+    public List<InetAddress> getGateways() { return mGateways; }
+
+    /**
+     * @return A list of Proxy Call State Control Function address via PCO(Protocol Configuration
+     * Option) for IMS client.
+     */
+    @NonNull
+    public List<String> getPcscfs() { return mPcscfs; }
+
+    /**
+     * @return MTU received from network Value <= 0 means network has either not sent a value or
+     * sent an invalid value
+     */
+    public int getMtu() { return mMtu; }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("DataCallResponse: {")
+           .append(" status=").append(mStatus)
+           .append(" retry=").append(mSuggestedRetryTime)
+           .append(" cid=").append(mCid)
+           .append(" active=").append(mActive)
+           .append(" type=").append(mType)
+           .append(" ifname=").append(mIfname)
+           .append(" mtu=").append(mMtu)
+           .append(" addresses=").append(mAddresses)
+           .append(" dnses=").append(mDnses)
+           .append(" gateways=").append(mGateways)
+           .append(" pcscf=").append(mPcscfs)
+           .append("}");
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mStatus);
+        dest.writeInt(mSuggestedRetryTime);
+        dest.writeInt(mCid);
+        dest.writeInt(mActive);
+        dest.writeString(mType);
+        dest.writeString(mIfname);
+        dest.writeList(mAddresses);
+        dest.writeList(mDnses);
+        dest.writeList(mGateways);
+        dest.writeList(mPcscfs);
+        dest.writeInt(mMtu);
+    }
+
+    public static final Parcelable.Creator<DataCallResponse> CREATOR =
+            new Parcelable.Creator<DataCallResponse>() {
+                @Override
+                public DataCallResponse createFromParcel(Parcel source) {
+                    return new DataCallResponse(source);
+                }
+
+                @Override
+                public DataCallResponse[] newArray(int size) {
+                    return new DataCallResponse[size];
+                }
+            };
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.aidl b/telephony/java/android/telephony/data/InterfaceAddress.aidl
new file mode 100644
index 0000000..d750363
--- /dev/null
+++ b/telephony/java/android/telephony/data/InterfaceAddress.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright 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.
+ */
+
+/** @hide */
+package android.telephony.data;
+
+parcelable InterfaceAddress;
diff --git a/telephony/java/android/telephony/data/InterfaceAddress.java b/telephony/java/android/telephony/data/InterfaceAddress.java
new file mode 100644
index 0000000..947d0ff
--- /dev/null
+++ b/telephony/java/android/telephony/data/InterfaceAddress.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 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.data;
+
+import android.annotation.SystemApi;
+import android.net.NetworkUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * This class represents a Network Interface address. In short it's an IP address, a subnet mask
+ * when the address is an IPv4 one. An IP address and a network prefix length in the case of IPv6
+ * address.
+ *
+ * @hide
+ */
+@SystemApi
+public final class InterfaceAddress implements Parcelable {
+
+    private final InetAddress mInetAddress;
+
+    private final int mPrefixLength;
+
+    /**
+     * @param inetAddress A {@link InetAddress} of the address
+     * @param prefixLength The network prefix length for this address.
+     */
+    public InterfaceAddress(InetAddress inetAddress, int prefixLength) {
+        mInetAddress = inetAddress;
+        mPrefixLength = prefixLength;
+    }
+
+    /**
+     * @param address The address in string format
+     * @param prefixLength The network prefix length for this address.
+     * @throws UnknownHostException
+     */
+    public InterfaceAddress(String address, int prefixLength) throws UnknownHostException {
+        InetAddress ia;
+        try {
+            ia = NetworkUtils.numericToInetAddress(address);
+        } catch (IllegalArgumentException e) {
+            throw new UnknownHostException("Non-numeric ip addr=" + address);
+        }
+        mInetAddress = ia;
+        mPrefixLength = prefixLength;
+    }
+
+    public InterfaceAddress(Parcel source) {
+        mInetAddress = (InetAddress) source.readSerializable();
+        mPrefixLength = source.readInt();
+    }
+
+    /**
+     * @return an InetAddress for this address.
+     */
+    public InetAddress getAddress() { return mInetAddress; }
+
+    /**
+     * @return The network prefix length for this address.
+     */
+    public int getNetworkPrefixLength() { return mPrefixLength; }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return mInetAddress + "/" + mPrefixLength;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeSerializable(mInetAddress);
+        dest.writeInt(mPrefixLength);
+    }
+
+    public static final Parcelable.Creator<InterfaceAddress> CREATOR =
+            new Parcelable.Creator<InterfaceAddress>() {
+        @Override
+        public InterfaceAddress createFromParcel(Parcel source) {
+            return new InterfaceAddress(source);
+        }
+
+        @Override
+        public InterfaceAddress[] newArray(int size) {
+            return new InterfaceAddress[size];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java
index b371efb..daa74c8 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java
@@ -21,6 +21,7 @@
 
 import com.android.ims.ImsCallForwardInfo;
 import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsSsData;
 import com.android.ims.ImsSsInfo;
 import com.android.ims.internal.IImsUt;
 import com.android.ims.internal.IImsUtListener;
@@ -85,4 +86,10 @@
     public void utConfigurationCallWaitingQueried(IImsUt ut, int id, ImsSsInfo[] cwInfo)
             throws RemoteException {
     }
+
+    /**
+     * Notifies client when Supplementary Service indication is received
+     */
+    @Override
+    public void onSupplementaryServiceIndication(ImsSsData ssData) {}
 }
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index cdfc1fd..6ad54c1 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -111,6 +111,16 @@
     // and this capability is not supported by the network.
     public static final int CODE_IMEI_NOT_ACCEPTED = 243;
 
+    //STK CC errors
+    public static final int CODE_DIAL_MODIFIED_TO_USSD = 244;
+    public static final int CODE_DIAL_MODIFIED_TO_SS = 245;
+    public static final int CODE_DIAL_MODIFIED_TO_DIAL = 246;
+    public static final int CODE_DIAL_MODIFIED_TO_DIAL_VIDEO = 247;
+    public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL = 248;
+    public static final int CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO = 249;
+    public static final int CODE_DIAL_VIDEO_MODIFIED_TO_SS = 250;
+    public static final int CODE_DIAL_VIDEO_MODIFIED_TO_USSD = 251;
+
     /**
      * STATUSCODE (SIP response code) (IMS -> Telephony)
      */
@@ -217,6 +227,11 @@
     public static final int CODE_UT_OPERATION_NOT_ALLOWED = 803;
     public static final int CODE_UT_NETWORK_ERROR = 804;
     public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821;
+    //STK CC errors
+    public static final int CODE_UT_SS_MODIFIED_TO_DIAL = 822;
+    public static final int CODE_UT_SS_MODIFIED_TO_USSD = 823;
+    public static final int CODE_UT_SS_MODIFIED_TO_SS = 824;
+    public static final int CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO = 825;
 
     /**
      * ECBM
diff --git a/telephony/java/com/android/ims/ImsSsData.aidl b/telephony/java/com/android/ims/ImsSsData.aidl
new file mode 100644
index 0000000..33f8306
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsSsData.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.ims;
+
+parcelable ImsSsData;
diff --git a/telephony/java/com/android/ims/ImsSsData.java b/telephony/java/com/android/ims/ImsSsData.java
new file mode 100644
index 0000000..7336c13
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsSsData.java
@@ -0,0 +1,189 @@
+/*
+ * 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.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+
+/**
+ * Provided STK Call Control Suplementary Service information
+ *
+ * {@hide}
+ */
+public class ImsSsData implements Parcelable {
+
+    //ServiceType
+    public static final int SS_CFU = 0;
+    public static final int SS_CF_BUSY = 1;
+    public static final int SS_CF_NO_REPLY = 2;
+    public static final int SS_CF_NOT_REACHABLE = 3;
+    public static final int SS_CF_ALL = 4;
+    public static final int SS_CF_ALL_CONDITIONAL = 5;
+    public static final int SS_CFUT = 6;
+    public static final int SS_CLIP = 7;
+    public static final int SS_CLIR = 8;
+    public static final int SS_COLP = 9;
+    public static final int SS_COLR = 10;
+    public static final int SS_CNAP = 11;
+    public static final int SS_WAIT = 12;
+    public static final int SS_BAOC = 13;
+    public static final int SS_BAOIC = 14;
+    public static final int SS_BAOIC_EXC_HOME = 15;
+    public static final int SS_BAIC = 16;
+    public static final int SS_BAIC_ROAMING = 17;
+    public static final int SS_ALL_BARRING = 18;
+    public static final int SS_OUTGOING_BARRING = 19;
+    public static final int SS_INCOMING_BARRING = 20;
+    public static final int SS_INCOMING_BARRING_DN = 21;
+    public static final int SS_INCOMING_BARRING_ANONYMOUS = 22;
+
+    //SSRequestType
+    public static final int SS_ACTIVATION = 0;
+    public static final int SS_DEACTIVATION = 1;
+    public static final int SS_INTERROGATION = 2;
+    public static final int SS_REGISTRATION = 3;
+    public static final int SS_ERASURE = 4;
+
+    //TeleserviceType
+    public static final int SS_ALL_TELE_AND_BEARER_SERVICES = 0;
+    public static final int SS_ALL_TELESEVICES = 1;
+    public static final int SS_TELEPHONY = 2;
+    public static final int SS_ALL_DATA_TELESERVICES = 3;
+    public static final int SS_SMS_SERVICES = 4;
+    public static final int SS_ALL_TELESERVICES_EXCEPT_SMS = 5;
+
+    // Refer to ServiceType
+    public int serviceType;
+    // Refere to SSRequestType
+    public int requestType;
+    // Refer to TeleserviceType
+    public int teleserviceType;
+    // Service Class
+    public int serviceClass;
+    // Error information
+    public int result;
+
+    public int[] ssInfo; /* Valid for all supplementary services.
+                             This field will be empty for RequestType SS_INTERROGATION
+                             and ServiceType SS_CF_*, SS_INCOMING_BARRING_DN,
+                             SS_INCOMING_BARRING_ANONYMOUS.*/
+
+    public ImsCallForwardInfo[] cfInfo; /* Valid only for supplementary services
+                                            ServiceType SS_CF_* and RequestType SS_INTERROGATION */
+
+    public ImsSsInfo[] imsSsInfo;   /* Valid only for ServiceType SS_INCOMING_BARRING_DN and
+                                        ServiceType SS_INCOMING_BARRING_ANONYMOUS */
+
+    public ImsSsData() {}
+
+    public ImsSsData(Parcel in) {
+        readFromParcel(in);
+    }
+
+    public static final Creator<ImsSsData> CREATOR = new Creator<ImsSsData>() {
+        @Override
+        public ImsSsData createFromParcel(Parcel in) {
+            return new ImsSsData(in);
+        }
+
+        @Override
+        public ImsSsData[] newArray(int size) {
+            return new ImsSsData[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(serviceType);
+        out.writeInt(requestType);
+        out.writeInt(teleserviceType);
+        out.writeInt(serviceClass);
+        out.writeInt(result);
+        out.writeIntArray(ssInfo);
+        out.writeParcelableArray(cfInfo, 0);
+    }
+
+    private void readFromParcel(Parcel in) {
+        serviceType = in.readInt();
+        requestType = in.readInt();
+        teleserviceType = in.readInt();
+        serviceClass = in.readInt();
+        result = in.readInt();
+        ssInfo = in.createIntArray();
+        cfInfo = (ImsCallForwardInfo[])in.readParcelableArray(this.getClass().getClassLoader());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public boolean isTypeCF() {
+        return (serviceType == SS_CFU || serviceType == SS_CF_BUSY ||
+              serviceType == SS_CF_NO_REPLY || serviceType == SS_CF_NOT_REACHABLE ||
+              serviceType == SS_CF_ALL || serviceType == SS_CF_ALL_CONDITIONAL);
+    }
+
+    public boolean isTypeUnConditional() {
+        return (serviceType == SS_CFU || serviceType == SS_CF_ALL);
+    }
+
+    public boolean isTypeCW() {
+        return (serviceType == SS_WAIT);
+    }
+
+    public boolean isTypeClip() {
+        return (serviceType == SS_CLIP);
+    }
+
+    public boolean isTypeColr() {
+        return (serviceType == SS_COLR);
+    }
+
+    public boolean isTypeColp() {
+        return (serviceType == SS_COLP);
+    }
+
+    public boolean isTypeClir() {
+        return (serviceType == SS_CLIR);
+    }
+
+    public boolean isTypeIcb() {
+        return (serviceType == SS_INCOMING_BARRING_DN ||
+                serviceType == SS_INCOMING_BARRING_ANONYMOUS);
+    }
+
+    public boolean isTypeBarring() {
+        return (serviceType == SS_BAOC || serviceType == SS_BAOIC ||
+              serviceType == SS_BAOIC_EXC_HOME || serviceType == SS_BAIC ||
+              serviceType == SS_BAIC_ROAMING || serviceType == SS_ALL_BARRING ||
+              serviceType == SS_OUTGOING_BARRING || serviceType == SS_INCOMING_BARRING);
+    }
+
+    public boolean isTypeInterrogation() {
+        return (requestType == SS_INTERROGATION);
+    }
+
+    public String toString() {
+        return "[ImsSsData] " + "ServiceType: " + serviceType
+            + " RequestType: " + requestType
+            + " TeleserviceType: " + teleserviceType
+            + " ServiceClass: " + serviceClass
+            + " Result: " + result;
+    }
+}
diff --git a/telephony/java/com/android/ims/ImsUtInterface.java b/telephony/java/com/android/ims/ImsUtInterface.java
index 250371f..14c184a 100644
--- a/telephony/java/com/android/ims/ImsUtInterface.java
+++ b/telephony/java/com/android/ims/ImsUtInterface.java
@@ -16,6 +16,7 @@
 
 package com.android.ims;
 
+import android.os.Handler;
 import android.os.Message;
 
 /**
@@ -188,4 +189,18 @@
      * Updates the configuration of the COLP supplementary service.
      */
     public void updateCOLP(boolean enable, Message result);
+
+    /**
+     * Register for UNSOL_ON_SS indications.
+     * @param handler the {@link Handler} that is notified when there is an ss indication.
+     * @param event  Supplimentary service indication event.
+     * @param Object user object.
+     */
+    public void registerForSuppServiceIndication(Handler handler, int event, Object object);
+
+    /**
+     * Deregister for UNSOL_ON_SS indications.
+     * @param handler the {@link Handler} that is notified when there is an ss indication.
+     */
+    public void unregisterForSuppServiceIndication(Handler handler);
 }
diff --git a/telephony/java/com/android/ims/internal/IImsUtListener.aidl b/telephony/java/com/android/ims/internal/IImsUtListener.aidl
index 300273a..1bc0369 100644
--- a/telephony/java/com/android/ims/internal/IImsUtListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsUtListener.aidl
@@ -19,6 +19,7 @@
 import android.os.Bundle;
 
 import com.android.ims.ImsCallForwardInfo;
+import com.android.ims.ImsSsData;
 import com.android.ims.ImsSsInfo;
 import com.android.ims.internal.IImsUt;
 import com.android.ims.ImsReasonInfo;
@@ -56,4 +57,11 @@
      */
     void utConfigurationCallWaitingQueried(in IImsUt ut,
             int id, in ImsSsInfo[] cwInfo);
+
+    /**
+     * Notifies client when Supplementary Service indication is received
+     *
+     * @param ssData Details of SS request and response information
+     */
+    void onSupplementaryServiceIndication(in ImsSsData ssData);
 }
diff --git a/test-base/Android.mk b/test-base/Android.mk
index 6a1ac9e..03bdcf23 100644
--- a/test-base/Android.mk
+++ b/test-base/Android.mk
@@ -60,6 +60,9 @@
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
+# For unbundled build we'll use the prebuilt jar from prebuilts/sdk.
+ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
+
 # Generate the stub source files for android.test.base.stubs
 # ==========================================================
 include $(CLEAR_VARS)
@@ -151,6 +154,8 @@
 	@echo Copying removed.txt
 	$(hide) $(ACP) $(ANDROID_TEST_BASE_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_BASE_REMOVED_API_FILE)
 
+endif  # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
+
 # Build the legacy-android-test library
 # =====================================
 # This contains the android.test classes that were in Android API level 25,
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 0c562e6..ce8019f 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -46,6 +46,7 @@
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.pm.VersionedPackage;
+import android.content.pm.dex.ArtManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Rect;
@@ -1174,4 +1175,12 @@
             @Nullable DexModuleRegisterCallback callback) {
         throw new UnsupportedOperationException();
     }
+
+    /**
+     * @hide
+     */
+    @Override
+    public ArtManager getArtManager() {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/tests/AppLaunch/Android.mk b/tests/AppLaunch/Android.mk
index d01b1f9..09739e5 100644
--- a/tests/AppLaunch/Android.mk
+++ b/tests/AppLaunch/Android.mk
@@ -9,7 +9,7 @@
 LOCAL_PACKAGE_NAME := AppLaunch
 
 LOCAL_CERTIFICATE := platform
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.base android.test.runner
 
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
index 527d1bbf..9e7f618 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
@@ -24,7 +24,7 @@
 
 LOCAL_SRC_FILES += $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.base
 LOCAL_STATIC_JAVA_LIBRARIES := guava junit
 
 LOCAL_PROGUARD_ENABLED := disabled
diff --git a/tests/ServiceCrashTest/Android.mk b/tests/ServiceCrashTest/Android.mk
index d1f8456..f7b3452 100644
--- a/tests/ServiceCrashTest/Android.mk
+++ b/tests/ServiceCrashTest/Android.mk
@@ -9,7 +9,7 @@
 LOCAL_PACKAGE_NAME := ServiceCrashTest
 
 LOCAL_CERTIFICATE := platform
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test
 
diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
index 6b9bb31..9174014 100644
--- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
+++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
@@ -24,6 +24,7 @@
 import android.util.Log;
 import android.util.MergedConfiguration;
 import android.view.Display;
+import android.view.DisplayCutout;
 import android.view.IWindowSession;
 import android.view.Surface;
 import android.view.View;
@@ -103,7 +104,8 @@
                         WindowManagerGlobal.getWindowSession().relayout(window,
                                 window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, mTmpRect,
                                 mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect,
-                                new MergedConfiguration(), new Surface());
+                                new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(),
+                                new Surface());
                     } catch (RemoteException e) {
                         e.printStackTrace();
                     }
diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk
index 88794c2..202a699 100644
--- a/tests/backup/Android.mk
+++ b/tests/backup/Android.mk
@@ -20,7 +20,8 @@
 
 LOCAL_SRC_FILES := \
     backup_helper_test.cpp
- 
+
+LOCAL_CFLAGS := -Wall -Werror
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := backup_helper_test
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
@@ -43,4 +44,3 @@
 LOCAL_PROGUARD_ENABLED := disabled
 
 include $(BUILD_PACKAGE)
-    
diff --git a/tests/backup/backup_helper_test.cpp b/tests/backup/backup_helper_test.cpp
index b5f6ff5..457dcc4 100644
--- a/tests/backup/backup_helper_test.cpp
+++ b/tests/backup/backup_helper_test.cpp
@@ -118,7 +118,7 @@
 
 #else
 int
-main(int argc, char** argv)
+main(int, char**)
 {
     printf ("test_backup_helper built without the tests\n");
     return 0;
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 677585c..1bd1af5 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -21,7 +21,9 @@
     services.net
 
 LOCAL_JAVA_LIBRARIES := \
-    android.test.runner
+    android.test.runner \
+    android.test.base \
+    android.test.mock
 
 LOCAL_PACKAGE_NAME := FrameworksNetTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
index 772ff8d..558dbb6 100644
--- a/tests/net/java/android/net/MacAddressTest.java
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -161,6 +161,9 @@
 
             assertEquals(mac, MacAddress.fromString(stringRepr));
             assertEquals(mac, MacAddress.fromBytes(bytesRepr));
+
+            assertEquals(mac, MacAddress.fromString(MacAddress.stringAddrFromByteAddr(bytesRepr)));
+            assertEquals(mac, MacAddress.fromBytes(MacAddress.byteAddrFromStringAddr(stringRepr)));
         }
     }
 
diff --git a/tests/net/java/android/net/ip/IpManagerTest.java b/tests/net/java/android/net/ip/IpManagerTest.java
index 22d88fb..ebf121a 100644
--- a/tests/net/java/android/net/ip/IpManagerTest.java
+++ b/tests/net/java/android/net/ip/IpManagerTest.java
@@ -69,6 +69,8 @@
 
 /**
  * Tests for IpManager.
+ *
+ * TODO: Rename to IpClientTest.
  */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -111,6 +113,10 @@
         verify(mNMService, times(1)).registerObserver(arg.capture());
         mObserver = arg.getValue();
         reset(mNMService);
+        final LinkProperties emptyLp = new LinkProperties();
+        emptyLp.setInterfaceName(ifname);
+        verify(mCb, timeout(100)).onLinkPropertiesChange(eq(emptyLp));
+        reset(mCb);
         return ipm;
     }
 
diff --git a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
index f849689..54776db 100644
--- a/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
+++ b/tests/net/java/android/net/ip/IpReachabilityMonitorTest.java
@@ -18,10 +18,12 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.when;
 
 import android.net.util.SharedLog;
+import android.os.Handler;
+import android.os.Looper;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -42,14 +44,18 @@
     @Mock IpReachabilityMonitor.Callback mCallback;
     @Mock IpReachabilityMonitor.Dependencies mDependencies;
     @Mock SharedLog mLog;
+    Handler mHandler;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        when(mLog.forSubComponent(anyString())).thenReturn(mLog);
+        mHandler = new Handler(Looper.getMainLooper());
     }
 
     IpReachabilityMonitor makeMonitor() {
-        return new IpReachabilityMonitor("fake0", 1, mLog, mCallback, null, mDependencies);
+        return new IpReachabilityMonitor(
+                "fake0", 1, mHandler, mLog, mCallback, null, mDependencies);
     }
 
     @Test
diff --git a/tests/net/java/android/net/netlink/NetlinkSocketTest.java b/tests/net/java/android/net/netlink/NetlinkSocketTest.java
index bd36bac8..11be40b 100644
--- a/tests/net/java/android/net/netlink/NetlinkSocketTest.java
+++ b/tests/net/java/android/net/netlink/NetlinkSocketTest.java
@@ -16,6 +16,8 @@
 
 package android.net.netlink;
 
+import static android.net.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE;
+import static android.system.OsConstants.NETLINK_ROUTE;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -28,10 +30,12 @@
 import android.support.test.filters.SmallTest;
 import android.system.ErrnoException;
 import android.system.NetlinkSocketAddress;
-import android.system.OsConstants;
+import android.system.Os;
 import android.util.Log;
+import libcore.io.IoUtils;
 
 import java.io.InterruptedIOException;
+import java.io.FileDescriptor;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
@@ -46,29 +50,28 @@
 
     @Test
     public void testBasicWorkingGetNeighborsQuery() throws Exception {
-        NetlinkSocket s = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
-        assertNotNull(s);
+        final FileDescriptor fd = NetlinkSocket.forProto(NETLINK_ROUTE);
+        assertNotNull(fd);
 
-        s.connectToKernel();
+        NetlinkSocket.connectToKernel(fd);
 
-        NetlinkSocketAddress localAddr = s.getLocalAddress();
+        final NetlinkSocketAddress localAddr = (NetlinkSocketAddress) Os.getsockname(fd);
         assertNotNull(localAddr);
         assertEquals(0, localAddr.getGroupsMask());
         assertTrue(0 != localAddr.getPortId());
 
         final int TEST_SEQNO = 5;
-        final byte[] request = RtNetlinkNeighborMessage.newGetNeighborsRequest(TEST_SEQNO);
-        assertNotNull(request);
+        final byte[] req = RtNetlinkNeighborMessage.newGetNeighborsRequest(TEST_SEQNO);
+        assertNotNull(req);
 
         final long TIMEOUT = 500;
-        assertTrue(s.sendMessage(request, 0, request.length, TIMEOUT));
+        assertEquals(req.length, NetlinkSocket.sendMessage(fd, req, 0, req.length, TIMEOUT));
 
         int neighMessageCount = 0;
         int doneMessageCount = 0;
 
         while (doneMessageCount == 0) {
-            ByteBuffer response = null;
-            response = s.recvMessage(TIMEOUT);
+            ByteBuffer response = NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, TIMEOUT);
             assertNotNull(response);
             assertTrue(StructNlMsgHdr.STRUCT_SIZE <= response.limit());
             assertEquals(0, response.position());
@@ -100,30 +103,6 @@
         // TODO: make sure this test passes sanely in airplane mode.
         assertTrue(neighMessageCount > 0);
 
-        s.close();
-    }
-
-    @Test
-    public void testRepeatedCloseCallsAreQuiet() throws Exception {
-        // Create a working NetlinkSocket.
-        NetlinkSocket s = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
-        assertNotNull(s);
-        s.connectToKernel();
-        NetlinkSocketAddress localAddr = s.getLocalAddress();
-        assertNotNull(localAddr);
-        assertEquals(0, localAddr.getGroupsMask());
-        assertTrue(0 != localAddr.getPortId());
-        // Close once.
-        s.close();
-        // Test that it is closed.
-        boolean expectedErrorSeen = false;
-        try {
-            localAddr = s.getLocalAddress();
-        } catch (ErrnoException e) {
-            expectedErrorSeen = true;
-        }
-        assertTrue(expectedErrorSeen);
-        // Close once more.
-        s.close();
+        IoUtils.closeQuietly(fd);
     }
 }
diff --git a/tests/net/java/android/net/util/BlockingSocketReaderTest.java b/tests/net/java/android/net/util/PacketReaderTest.java
similarity index 91%
rename from tests/net/java/android/net/util/BlockingSocketReaderTest.java
rename to tests/net/java/android/net/util/PacketReaderTest.java
index 29dfa4c..dced743 100644
--- a/tests/net/java/android/net/util/BlockingSocketReaderTest.java
+++ b/tests/net/java/android/net/util/PacketReaderTest.java
@@ -16,7 +16,7 @@
 
 package android.net.util;
 
-import static android.net.util.BlockingSocketReader.DEFAULT_RECV_BUF_SIZE;
+import static android.net.util.PacketReader.DEFAULT_RECV_BUF_SIZE;
 import static android.system.OsConstants.*;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -53,13 +53,13 @@
 import libcore.io.IoBridge;
 
 /**
- * Tests for BlockingSocketReader.
+ * Tests for PacketReader.
  *
  * @hide
  */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-public class BlockingSocketReaderTest {
+public class PacketReaderTest {
     static final InetAddress LOOPBACK6 = Inet6Address.getLoopbackAddress();
     static final StructTimeval TIMEO = StructTimeval.fromMillis(500);
 
@@ -69,9 +69,9 @@
     protected byte[] mLastRecvBuf;
     protected boolean mStopped;
     protected HandlerThread mHandlerThread;
-    protected BlockingSocketReader mReceiver;
+    protected PacketReader mReceiver;
 
-    class UdpLoopbackReader extends BlockingSocketReader {
+    class UdpLoopbackReader extends PacketReader {
         public UdpLoopbackReader(Handler h) {
             super(h);
         }
@@ -121,7 +121,7 @@
         mLastRecvBuf = null;
         mStopped = false;
 
-        mHandlerThread = new HandlerThread(BlockingSocketReaderTest.class.getSimpleName());
+        mHandlerThread = new HandlerThread(PacketReaderTest.class.getSimpleName());
         mHandlerThread.start();
     }
 
@@ -188,8 +188,8 @@
         mReceiver = null;
     }
 
-    class NullBlockingSocketReader extends BlockingSocketReader {
-        public NullBlockingSocketReader(Handler h, int recvbufsize) {
+    class NullPacketReader extends PacketReader {
+        public NullPacketReader(Handler h, int recvbufsize) {
             super(h, recvbufsize);
         }
 
@@ -202,7 +202,7 @@
         final Handler h = mHandlerThread.getThreadHandler();
 
         for (int i : new int[]{-1, 0, 1, DEFAULT_RECV_BUF_SIZE-1}) {
-            final BlockingSocketReader b = new NullBlockingSocketReader(h, i);
+            final PacketReader b = new NullPacketReader(h, i);
             assertEquals(DEFAULT_RECV_BUF_SIZE, b.recvBufSize());
         }
     }
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 8e579aa..0720886f 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -270,8 +270,8 @@
     }
 
     /**
-     * This function checks if the number of encap UDP socket that one UID can reserve
-     * has a reasonable limit.
+     * This function checks if the number of encap UDP socket that one UID can reserve has a
+     * reasonable limit.
      */
     @Test
     public void testSocketResourceTrackerLimitation() throws Exception {
@@ -287,9 +287,10 @@
             openUdpEncapSockets.add(newUdpEncapSocket);
         }
         // Assert that the total sockets quota has a reasonable limit.
+        assertTrue("No UDP encap socket was open", !openUdpEncapSockets.isEmpty());
         assertTrue(
-                openUdpEncapSockets.size() > 0
-                        && openUdpEncapSockets.size() < MAX_NUM_ENCAP_SOCKETS);
+                "Number of open UDP encap sockets is out of bound",
+                openUdpEncapSockets.size() < MAX_NUM_ENCAP_SOCKETS);
 
         // Try to reserve one more UDP encapsulation socket, and should fail.
         IpSecUdpEncapResponse extraUdpEncapSocket =
@@ -297,7 +298,7 @@
         assertNotNull(extraUdpEncapSocket);
         assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraUdpEncapSocket.status);
 
-        // Close one of the open UDP encapsulation scokets.
+        // Close one of the open UDP encapsulation sockets.
         mIpSecService.closeUdpEncapsulationSocket(openUdpEncapSockets.get(0).resourceId);
         openUdpEncapSockets.get(0).fileDescriptor.close();
         openUdpEncapSockets.remove(0);
@@ -316,10 +317,9 @@
     }
 
     /**
-     * This function checks if the number of SPI that one UID can reserve
-     * has a reasonable limit.
-     * This test does not test for both address families or duplicate SPIs because resource
-     * tracking code does not depend on them.
+     * This function checks if the number of SPI that one UID can reserve has a reasonable limit.
+     * This test does not test for both address families or duplicate SPIs because resource tracking
+     * code does not depend on them.
      */
     @Test
     public void testSpiResourceTrackerLimitation() throws Exception {
diff --git a/tests/testables/Android.mk b/tests/testables/Android.mk
index 0e36981..7fcfc6e 100644
--- a/tests/testables/Android.mk
+++ b/tests/testables/Android.mk
@@ -25,10 +25,9 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
-    mockito-target-minus-junit4 \
-    legacy-android-test
+    mockito-target-minus-junit4
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.mock
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
diff --git a/tests/utils/testutils/Android.mk b/tests/utils/testutils/Android.mk
index 43d1e37..543c652 100644
--- a/tests/utils/testutils/Android.mk
+++ b/tests/utils/testutils/Android.mk
@@ -25,9 +25,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
-    legacy-android-test \
     mockito-target-minus-junit4
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index a93ee2e..deb9cc0 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -68,6 +68,7 @@
           mSingleCrunchInputFile(NULL), mSingleCrunchOutputFile(NULL),
           mBuildSharedLibrary(false),
           mBuildAppAsSharedLibrary(false),
+          mCompileSdkVersion(0),
           mArgc(0), mArgv(NULL)
         {}
     ~Bundle(void) {}
@@ -123,6 +124,10 @@
     void setErrorOnFailedInsert(bool val) { mErrorOnFailedInsert = val; }
     bool getErrorOnMissingConfigEntry() { return mErrorOnMissingConfigEntry; }
     void setErrorOnMissingConfigEntry(bool val) { mErrorOnMissingConfigEntry = val; }
+    const android::String8& getCompileSdkVersionCodename() { return mCompileSdkVersionCodename; }
+    void setCompileSdkVersionCodename(const android::String8& codename) { mCompileSdkVersionCodename = codename; }
+    int getCompileSdkVersion() { return mCompileSdkVersion; }
+    void setCompileSdkVersion(int version) { mCompileSdkVersion = version; }
     const android::String8& getPlatformBuildVersionCode() { return mPlatformVersionCode; }
     void setPlatformBuildVersionCode(const android::String8& code) { mPlatformVersionCode = code; }
     const android::String8& getPlatformBuildVersionName() { return mPlatformVersionName; }
@@ -344,6 +349,8 @@
     const char* mSingleCrunchOutputFile;
     bool        mBuildSharedLibrary;
     bool        mBuildAppAsSharedLibrary;
+    int         mCompileSdkVersion;
+    android::String8 mCompileSdkVersionCodename;
     android::String8 mPlatformVersionCode;
     android::String8 mPlatformVersionName;
     android::String8 mPrivateSymbolsPackage;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index cb87737..05375b0 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -293,6 +293,8 @@
     ISGAME_ATTR = 0x10103f4,
     REQUIRED_FEATURE_ATTR = 0x1010557,
     REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
+    COMPILE_SDK_VERSION_ATTR = 0x01010572, // NOT FINALIZED
+    COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573, // NOT FINALIZED
 };
 
 String8 getComponentName(String8 &pkgName, String8 &componentName) {
@@ -1247,9 +1249,37 @@
                                     splitName.string()).string());
                     }
 
-                    String8 platformVersionName = AaptXml::getAttribute(tree, NULL,
+                    String8 platformBuildVersionName = AaptXml::getAttribute(tree, NULL,
                             "platformBuildVersionName");
-                    printf(" platformBuildVersionName='%s'", platformVersionName.string());
+                    if (platformBuildVersionName != "") {
+                        printf(" platformBuildVersionName='%s'", platformBuildVersionName.string());
+                    }
+
+                    String8 platformBuildVersionCode = AaptXml::getAttribute(tree, NULL,
+                            "platformBuildVersionCode");
+                    if (platformBuildVersionCode != "") {
+                        printf(" platformBuildVersionCode='%s'", platformBuildVersionCode.string());
+                    }
+
+                    int32_t compileSdkVersion = AaptXml::getIntegerAttribute(tree,
+                            COMPILE_SDK_VERSION_ATTR, &error);
+                    if (error != "") {
+                        SourcePos(manifestFile, tree.getLineNumber()).error(
+                                "ERROR getting 'android:compileSdkVersion' attribute: %s",
+                                error.string());
+                        goto bail;
+                    }
+                    if (compileSdkVersion > 0) {
+                        printf(" compileSdkVersion='%d'", compileSdkVersion);
+                    }
+
+                    String8 compileSdkVersionCodename = AaptXml::getResolvedAttribute(res, tree,
+                            COMPILE_SDK_VERSION_CODENAME_ATTR, &error);
+                    if (compileSdkVersionCodename != "") {
+                        printf(" compileSdkVersionCodename='%s'", ResTable::normalizeForOutput(
+                                compileSdkVersionCodename.string()).string());
+                    }
+
                     printf("\n");
 
                     int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree,
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index bd2b2a36..ab6dced 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -918,6 +918,22 @@
         }
     }
 
+
+    if (bundle->getCompileSdkVersion() != 0) {
+        if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "compileSdkVersion",
+                    String8::format("%d", bundle->getCompileSdkVersion()),
+                    errorOnFailedInsert, true)) {
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    if (bundle->getCompileSdkVersionCodename() != "") {
+        if (!addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "compileSdkVersionCodename",
+                    bundle->getCompileSdkVersionCodename(), errorOnFailedInsert, true)) {
+            return UNKNOWN_ERROR;
+        }
+    }
+
     if (bundle->getPlatformBuildVersionCode() != "") {
         if (!addTagAttribute(root, "", "platformBuildVersionCode",
                     bundle->getPlatformBuildVersionCode(), errorOnFailedInsert, true)) {
@@ -1052,7 +1068,12 @@
     VERSION_NAME_ATTR = 0x0101021c,
 };
 
-static ssize_t extractPlatformBuildVersion(ResXMLTree& tree, Bundle* bundle) {
+static ssize_t extractPlatformBuildVersion(const ResTable& table, ResXMLTree& tree, Bundle* bundle) {
+    // First check if we should be recording the compileSdkVersion* attributes.
+    static const String16 compileSdkVersionName("android:attr/compileSdkVersion");
+    const bool useCompileSdkVersion = table.identifierForName(compileSdkVersionName.string(),
+                                                              compileSdkVersionName.size()) != 0u;
+
     size_t len;
     ResXMLTree::event_code_t code;
     while ((code = tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
@@ -1082,6 +1103,10 @@
             bundle->setPlatformBuildVersionCode(String8::format("%d", versionCode));
         }
 
+        if (useCompileSdkVersion && versionCode >= 0 && bundle->getCompileSdkVersion() == 0) {
+            bundle->setCompileSdkVersion(versionCode);
+        }
+
         String8 versionName = AaptXml::getAttribute(tree, VERSION_NAME_ATTR, &error);
         if (error != "") {
             fprintf(stderr, "ERROR: failed to get platform version name\n");
@@ -1091,6 +1116,11 @@
         if (versionName != "" && bundle->getPlatformBuildVersionName() == "") {
             bundle->setPlatformBuildVersionName(versionName);
         }
+
+        if (useCompileSdkVersion && versionName != ""
+                && bundle->getCompileSdkVersionCodename() == "") {
+            bundle->setCompileSdkVersionCodename(versionName);
+        }
         return NO_ERROR;
     }
 
@@ -1121,7 +1151,7 @@
             fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n");
             result = UNKNOWN_ERROR;
         } else {
-            result = extractPlatformBuildVersion(tree, bundle);
+            result = extractPlatformBuildVersion(assets.getResources(true), tree, bundle);
         }
     }
 
@@ -1707,7 +1737,9 @@
     // extract them from the platform APK.
     if (packageType != ResourceTable::System &&
             (bundle->getPlatformBuildVersionCode() == "" ||
-            bundle->getPlatformBuildVersionName() == "")) {
+            bundle->getPlatformBuildVersionName() == "" ||
+            bundle->getCompileSdkVersion() == 0 ||
+            bundle->getCompileSdkVersionCodename() == "")) {
         err = extractPlatformBuildVersion(assets->getAssetManager(), bundle);
         if (err != NO_ERROR) {
             return UNKNOWN_ERROR;
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index d2aebfd..13dd93e 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <sys/stat.h>
+#include <cinttypes>
 
 #include <queue>
 #include <unordered_map>
@@ -725,6 +726,30 @@
   return true;
 }
 
+static int32_t FindFrameworkAssetManagerCookie(const android::AssetManager& assets) {
+  using namespace android;
+
+  // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so
+  // we're looking for the first attribute resource in the system package.
+  const ResTable& table = assets.getResources(true);
+  Res_value val;
+  ssize_t idx = table.getResource(0x01010000, &val, true);
+  if (idx != NO_ERROR) {
+    // Try as a bag.
+    const ResTable::bag_entry* entry;
+    ssize_t cnt = table.lockBag(0x01010000, &entry);
+    if (cnt >= 0) {
+      idx = entry->stringBlock;
+    }
+    table.unlockBag(entry);
+  }
+
+  if (idx < 0) {
+    return 0;
+  }
+  return table.getTableCookie(idx);
+}
+
 class LinkCommand {
  public:
   LinkCommand(LinkContext* context, const LinkOptions& options)
@@ -734,7 +759,65 @@
         file_collection_(util::make_unique<io::FileCollection>()) {
   }
 
+  void ExtractCompileSdkVersions(android::AssetManager* assets) {
+    using namespace android;
+
+    int32_t cookie = FindFrameworkAssetManagerCookie(*assets);
+    if (cookie == 0) {
+      // No Framework assets loaded. Not a failure.
+      return;
+    }
+
+    std::unique_ptr<Asset> manifest(
+        assets->openNonAsset(cookie, kAndroidManifestPath, Asset::AccessMode::ACCESS_BUFFER));
+    if (manifest == nullptr) {
+      // No errors.
+      return;
+    }
+
+    std::string error;
+    std::unique_ptr<xml::XmlResource> manifest_xml =
+        xml::Inflate(manifest->getBuffer(true /*wordAligned*/), manifest->getLength(), &error);
+    if (manifest_xml == nullptr) {
+      // No errors.
+      return;
+    }
+
+    xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionCode");
+    if (attr != nullptr) {
+      Maybe<std::string>& compile_sdk_version = options_.manifest_fixer_options.compile_sdk_version;
+      if (BinaryPrimitive* prim = ValueCast<BinaryPrimitive>(attr->compiled_value.get())) {
+        switch (prim->value.dataType) {
+          case Res_value::TYPE_INT_DEC:
+            compile_sdk_version = StringPrintf("%" PRId32, static_cast<int32_t>(prim->value.data));
+            break;
+          case Res_value::TYPE_INT_HEX:
+            compile_sdk_version = StringPrintf("%" PRIx32, prim->value.data);
+            break;
+          default:
+            break;
+        }
+      } else if (String* str = ValueCast<String>(attr->compiled_value.get())) {
+        compile_sdk_version = *str->value;
+      } else {
+        compile_sdk_version = attr->value;
+      }
+    }
+
+    attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionName");
+    if (attr != nullptr) {
+      Maybe<std::string>& compile_sdk_version_codename =
+          options_.manifest_fixer_options.compile_sdk_version_codename;
+      if (String* str = ValueCast<String>(attr->compiled_value.get())) {
+        compile_sdk_version_codename = *str->value;
+      } else {
+        compile_sdk_version_codename = attr->value;
+      }
+    }
+  }
+
   // Creates a SymbolTable that loads symbols from the various APKs.
+  // Pre-condition: context_->GetCompilationPackage() needs to be set.
   bool LoadSymbolsFromIncludePaths() {
     auto asset_source = util::make_unique<AssetManagerSymbolSource>();
     for (const std::string& path : options_.include_paths) {
@@ -802,6 +885,17 @@
       } else if (entry.first == kAppPackageId) {
         // Capture the included base feature package.
         included_feature_base_ = entry.second;
+      } else if (entry.first == kFrameworkPackageId) {
+        // Try to embed which version of the framework we're compiling against.
+        // First check if we should use compileSdkVersion at all. Otherwise compilation may fail
+        // when linking our synthesized 'android:compileSdkVersion' attribute.
+        std::unique_ptr<SymbolTable::Symbol> symbol = asset_source->FindByName(
+            ResourceName("android", ResourceType::kAttr, "compileSdkVersion"));
+        if (symbol != nullptr && symbol->is_public) {
+          // The symbol is present and public, extract the android:versionName and
+          // android:versionCode from the framework AndroidManifest.xml.
+          ExtractCompileSdkVersions(asset_source->GetAssetManager());
+        }
       }
     }
 
@@ -1535,6 +1629,12 @@
       context_->SetCompilationPackage(app_info.package);
     }
 
+    // Now that the compilation package is set, load the dependencies. This will also extract
+    // the Android framework's versionCode and versionName, if they exist.
+    if (!LoadSymbolsFromIncludePaths()) {
+      return 1;
+    }
+
     ManifestFixer manifest_fixer(options_.manifest_fixer_options);
     if (!manifest_fixer.Consume(context_, manifest_xml.get())) {
       return 1;
@@ -1563,10 +1663,6 @@
       }
     }
 
-    if (!LoadSymbolsFromIncludePaths()) {
-      return 1;
-    }
-
     TableMergerOptions table_merger_options;
     table_merger_options.auto_add_overlay = options_.auto_add_overlay;
     table_merger_ = util::make_unique<TableMerger>(context_, &final_table_, table_merger_options);
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 3b90637..1bdb762 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -75,6 +75,10 @@
   TableFlattenerOptions table_flattener_options;
 
   Maybe<PostProcessingConfiguration> configuration;
+
+  // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts
+  // are kept and will be written as output.
+  std::unordered_set<std::string> kept_artifacts;
 };
 
 class OptimizeContext : public IAaptContext {
@@ -197,9 +201,12 @@
 
     if (options_.configuration && options_.output_dir) {
       MultiApkGenerator generator{apk.get(), context_};
-      MultiApkGeneratorOptions generator_options = {options_.output_dir.value(),
-                                                    options_.configuration.value(),
-                                                    options_.table_flattener_options};
+      MultiApkGeneratorOptions generator_options = {
+          options_.output_dir.value(),
+          options_.configuration.value(),
+          options_.table_flattener_options,
+          options_.kept_artifacts,
+      };
       if (!generator.FromBaseApk(generator_options)) {
         return 1;
       }
@@ -323,6 +330,7 @@
   Maybe<std::string> target_abis;
   std::vector<std::string> configs;
   std::vector<std::string> split_args;
+  std::unordered_set<std::string> kept_artifacts;
   bool verbose = false;
   bool print_only = false;
   Flags flags =
@@ -356,6 +364,10 @@
                             "Split APK.\nSyntax: path/to/output.apk;<config>[,<config>[...]].\n"
                             "On Windows, use a semicolon ';' separator instead.",
                             &split_args)
+          .OptionalFlagList("--keep-artifacts",
+                            "Comma separated list of artifacts to keep. If none are specified,\n"
+                            "all artifacts will be kept.",
+                            &kept_artifacts)
           .OptionalSwitch("--enable-sparse-encoding",
                           "Enables encoding sparse entries using a binary search tree.\n"
                           "This decreases APK size at the cost of resource retrieval performance.",
@@ -438,6 +450,14 @@
       return 0;
     }
 
+    if (!kept_artifacts.empty()) {
+      for (const auto& artifact_str : kept_artifacts) {
+        for (const auto& artifact : util::Tokenize(artifact_str, ',')) {
+          options.kept_artifacts.insert(artifact.to_string());
+        }
+      }
+    }
+
     // Since we know that we are going to process the APK (not just print targets), make sure we
     // have somewhere to write them to.
     if (!options.output_dir) {
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index eaaefd5..a68df1d 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -406,6 +406,25 @@
     root->InsertChild(0, std::move(uses_sdk));
   }
 
+  if (options_.compile_sdk_version) {
+    xml::Attribute* attr = root->FindOrCreateAttribute(xml::kSchemaAndroid, "compileSdkVersion");
+
+    // Make sure we un-compile the value if it was set to something else.
+    attr->compiled_value = {};
+
+    attr->value = options_.compile_sdk_version.value();
+  }
+
+  if (options_.compile_sdk_version_codename) {
+    xml::Attribute* attr =
+        root->FindOrCreateAttribute(xml::kSchemaAndroid, "compileSdkVersionCodename");
+
+    // Make sure we un-compile the value if it was set to something else.
+    attr->compiled_value = {};
+
+    attr->value = options_.compile_sdk_version_codename.value();
+  }
+
   xml::XmlActionExecutor executor;
   if (!BuildRules(&executor, context->GetDiagnostics())) {
     return false;
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index 470f65e..f5715f6 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -29,22 +29,42 @@
 namespace aapt {
 
 struct ManifestFixerOptions {
+  // The minimum SDK version to set if no 'android:minSdkVersion' is defined in a <uses-sdk> tag.
   Maybe<std::string> min_sdk_version_default;
+
+  // The target SDK version to set if no 'android:targetSdkVersion' is defined in a <uses-sdk> tag.
   Maybe<std::string> target_sdk_version_default;
+
+  // The Android package to use instead of the one defined in 'package' in <manifest>.
+  // This also renames all relative package/class names in the manifest to fully qualified
+  // Java names.
   Maybe<std::string> rename_manifest_package;
+
+  // The Android package to use instead of the one defined in 'android:targetPackage' in
+  // <instrumentation>.
   Maybe<std::string> rename_instrumentation_target_package;
+
+  // The version name to set if 'android:versionName' is not defined in <manifest>.
   Maybe<std::string> version_name_default;
+
+  // The version code to set if 'android:versionCode' is not defined in <manifest>.
   Maybe<std::string> version_code_default;
+
+  // The version of the framework being compiled against to set for 'android:compileSdkVersion' in
+  // the <manifest> tag.
+  Maybe<std::string> compile_sdk_version;
+
+  // The version codename of the framework being compiled against to set for
+  // 'android:compileSdkVersionCodename' in the <manifest> tag.
+  Maybe<std::string> compile_sdk_version_codename;
 };
 
-/**
- * Verifies that the manifest is correctly formed and inserts defaults
- * where specified with ManifestFixerOptions.
- */
+// Verifies that the manifest is correctly formed and inserts defaults where specified with
+// ManifestFixerOptions.
 class ManifestFixer : public IXmlResourceConsumer {
  public:
-  explicit ManifestFixer(const ManifestFixerOptions& options)
-      : options_(options) {}
+  explicit ManifestFixer(const ManifestFixerOptions& options) : options_(options) {
+  }
 
   bool Consume(IAaptContext* context, xml::XmlResource* doc) override;
 
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 40085ea..1320dcd 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -19,7 +19,12 @@
 #include "test/Test.h"
 
 using ::android::StringPiece;
+using ::testing::Eq;
+using ::testing::Gt;
+using ::testing::IsNull;
+using ::testing::Ne;
 using ::testing::NotNull;
+using ::testing::StrEq;
 
 namespace aapt {
 
@@ -72,22 +77,20 @@
 };
 
 TEST_F(ManifestFixerTest, EnsureManifestIsRootTag) {
-  EXPECT_EQ(nullptr, Verify("<other-tag />"));
-  EXPECT_EQ(nullptr, Verify("<ns:manifest xmlns:ns=\"com\" />"));
-  EXPECT_NE(nullptr, Verify("<manifest package=\"android\"></manifest>"));
+  EXPECT_THAT(Verify("<other-tag />"), IsNull());
+  EXPECT_THAT(Verify("<ns:manifest xmlns:ns=\"com\" />"), IsNull());
+  EXPECT_THAT(Verify("<manifest package=\"android\"></manifest>"), NotNull());
 }
 
 TEST_F(ManifestFixerTest, EnsureManifestHasPackage) {
-  EXPECT_NE(nullptr, Verify("<manifest package=\"android\" />"));
-  EXPECT_NE(nullptr, Verify("<manifest package=\"com.android\" />"));
-  EXPECT_NE(nullptr, Verify("<manifest package=\"com.android.google\" />"));
-  EXPECT_EQ(nullptr,
-            Verify("<manifest package=\"com.android.google.Class$1\" />"));
-  EXPECT_EQ(nullptr, Verify("<manifest "
-                            "xmlns:android=\"http://schemas.android.com/apk/"
-                            "res/android\" "
-                            "android:package=\"com.android\" />"));
-  EXPECT_EQ(nullptr, Verify("<manifest package=\"@string/str\" />"));
+  EXPECT_THAT(Verify("<manifest package=\"android\" />"), NotNull());
+  EXPECT_THAT(Verify("<manifest package=\"com.android\" />"), NotNull());
+  EXPECT_THAT(Verify("<manifest package=\"com.android.google\" />"), NotNull());
+  EXPECT_THAT(Verify("<manifest package=\"com.android.google.Class$1\" />"), IsNull());
+  EXPECT_THAT(Verify("<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" "
+                     "android:package=\"com.android\" />"),
+              IsNull());
+  EXPECT_THAT(Verify("<manifest package=\"@string/str\" />"), IsNull());
 }
 
 TEST_F(ManifestFixerTest, AllowMetaData) {
@@ -105,7 +108,7 @@
           </application>
           <instrumentation android:name=".Go"><meta-data /></instrumentation>
         </manifest>)EOF");
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 }
 
 TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
@@ -117,21 +120,21 @@
         <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" />
       </manifest>)EOF",
                                                             options);
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   xml::Element* el;
   xml::Attribute* attr;
 
   el = doc->root.get();
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
   el = el->FindChild({}, "uses-sdk");
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
   attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ("7", attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("7"));
   attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ("21", attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("21"));
 
   doc = VerifyWithOptions(R"EOF(
       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
@@ -139,18 +142,18 @@
         <uses-sdk android:targetSdkVersion="21" />
       </manifest>)EOF",
                           options);
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   el = doc->root.get();
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
   el = el->FindChild({}, "uses-sdk");
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
   attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ("8", attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("8"));
   attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ("21", attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("21"));
 
   doc = VerifyWithOptions(R"EOF(
       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
@@ -158,35 +161,35 @@
         <uses-sdk />
       </manifest>)EOF",
                           options);
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   el = doc->root.get();
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
   el = el->FindChild({}, "uses-sdk");
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
   attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ("8", attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("8"));
   attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ("22", attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("22"));
 
   doc = VerifyWithOptions(R"EOF(
       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                 package="android" />)EOF",
                           options);
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   el = doc->root.get();
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
   el = el->FindChild({}, "uses-sdk");
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
   attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ("8", attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("8"));
   attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ("22", attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("22"));
 }
 
 TEST_F(ManifestFixerTest, UsesSdkMustComeBeforeApplication) {
@@ -197,17 +200,17 @@
             <application android:name=".MainApplication" />
           </manifest>)EOF",
                                                             options);
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   xml::Element* manifest_el = doc->root.get();
-  ASSERT_NE(nullptr, manifest_el);
+  ASSERT_THAT(manifest_el, NotNull());
   ASSERT_EQ("manifest", manifest_el->name);
 
   xml::Element* application_el = manifest_el->FindChild("", "application");
-  ASSERT_NE(nullptr, application_el);
+  ASSERT_THAT(application_el, NotNull());
 
   xml::Element* uses_sdk_el = manifest_el->FindChild("", "uses-sdk");
-  ASSERT_NE(nullptr, uses_sdk_el);
+  ASSERT_THAT(uses_sdk_el, NotNull());
 
   // Check that the uses_sdk_el comes before application_el in the children
   // vector.
@@ -225,12 +228,12 @@
                      return child.get() == application_el;
                    });
 
-  ASSERT_NE(manifest_el->children.end(), uses_sdk_iter);
-  ASSERT_NE(manifest_el->children.end(), application_iter);
+  ASSERT_THAT(uses_sdk_iter, Ne(manifest_el->children.end()));
+  ASSERT_THAT(application_iter, Ne(manifest_el->children.end()));
 
   // The distance should be positive, meaning uses_sdk_iter comes before
   // application_iter.
-  EXPECT_GT(std::distance(uses_sdk_iter, application_iter), 0);
+  EXPECT_THAT(std::distance(uses_sdk_iter, application_iter), Gt(0));
 }
 
 TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) {
@@ -247,48 +250,49 @@
         </application>
       </manifest>)EOF",
                                                             options);
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   xml::Element* manifest_el = doc->root.get();
-  ASSERT_NE(nullptr, manifest_el);
+  ASSERT_THAT(manifest_el, NotNull());
 
   xml::Attribute* attr = nullptr;
 
   attr = manifest_el->FindAttribute({}, "package");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ(std::string("com.android"), attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("com.android"));
 
   xml::Element* uses_split_el = manifest_el->FindChild({}, "uses-split");
-  ASSERT_NE(nullptr, uses_split_el);
+  ASSERT_THAT(uses_split_el, NotNull());
   attr = uses_split_el->FindAttribute(xml::kSchemaAndroid, "name");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ(std::string("feature_a"), attr->value);
+  ASSERT_THAT(attr, NotNull());
+  // This should NOT have been affected.
+  EXPECT_THAT(attr->value, StrEq("feature_a"));
 
   xml::Element* application_el = manifest_el->FindChild({}, "application");
-  ASSERT_NE(nullptr, application_el);
+  ASSERT_THAT(application_el, NotNull());
 
   attr = application_el->FindAttribute(xml::kSchemaAndroid, "name");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ(std::string("android.MainApplication"), attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("android.MainApplication"));
 
   attr = application_el->FindAttribute({}, "text");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ(std::string("hello"), attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("hello"));
 
   xml::Element* el;
   el = application_el->FindChild({}, "activity");
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
 
   attr = el->FindAttribute(xml::kSchemaAndroid, "name");
-  ASSERT_NE(nullptr, el);
-  EXPECT_EQ(std::string("android.activity.Start"), attr->value);
+  ASSERT_THAT(el, NotNull());
+  EXPECT_THAT(attr->value, StrEq("android.activity.Start"));
 
   el = application_el->FindChild({}, "receiver");
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
 
   attr = el->FindAttribute(xml::kSchemaAndroid, "name");
-  ASSERT_NE(nullptr, el);
-  EXPECT_EQ(std::string("com.google.android.Receiver"), attr->value);
+  ASSERT_THAT(el, NotNull());
+  EXPECT_THAT(attr->value, StrEq("com.google.android.Receiver"));
 }
 
 TEST_F(ManifestFixerTest,
@@ -302,19 +306,19 @@
         <instrumentation android:name=".TestRunner" android:targetPackage="android" />
       </manifest>)EOF",
                                                             options);
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   xml::Element* manifest_el = doc->root.get();
-  ASSERT_NE(nullptr, manifest_el);
+  ASSERT_THAT(manifest_el, NotNull());
 
   xml::Element* instrumentation_el =
       manifest_el->FindChild({}, "instrumentation");
-  ASSERT_NE(nullptr, instrumentation_el);
+  ASSERT_THAT(instrumentation_el, NotNull());
 
   xml::Attribute* attr =
       instrumentation_el->FindAttribute(xml::kSchemaAndroid, "targetPackage");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ(std::string("com.android"), attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("com.android"));
 }
 
 TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) {
@@ -326,41 +330,39 @@
       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                 package="android" />)EOF",
                                                             options);
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   xml::Element* manifest_el = doc->root.get();
-  ASSERT_NE(nullptr, manifest_el);
+  ASSERT_THAT(manifest_el, NotNull());
 
   xml::Attribute* attr =
       manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ(std::string("Beta"), attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("Beta"));
 
   attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
-  ASSERT_NE(nullptr, attr);
-  EXPECT_EQ(std::string("0x10000000"), attr->value);
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("0x10000000"));
 }
 
 TEST_F(ManifestFixerTest, EnsureManifestAttributesAreTyped) {
-  EXPECT_EQ(nullptr,
-            Verify("<manifest package=\"android\" coreApp=\"hello\" />"));
-  EXPECT_EQ(nullptr,
-            Verify("<manifest package=\"android\" coreApp=\"1dp\" />"));
+  EXPECT_THAT(Verify("<manifest package=\"android\" coreApp=\"hello\" />"), IsNull());
+  EXPECT_THAT(Verify("<manifest package=\"android\" coreApp=\"1dp\" />"), IsNull());
 
   std::unique_ptr<xml::XmlResource> doc =
       Verify("<manifest package=\"android\" coreApp=\"true\" />");
-  ASSERT_NE(nullptr, doc);
+  ASSERT_THAT(doc, NotNull());
 
   xml::Element* el = doc->root.get();
-  ASSERT_NE(nullptr, el);
+  ASSERT_THAT(el, NotNull());
 
-  EXPECT_EQ("manifest", el->name);
+  EXPECT_THAT(el->name, StrEq("manifest"));
 
   xml::Attribute* attr = el->FindAttribute("", "coreApp");
-  ASSERT_NE(nullptr, attr);
+  ASSERT_THAT(attr, NotNull());
 
-  EXPECT_NE(nullptr, attr->compiled_value);
-  EXPECT_NE(nullptr, ValueCast<BinaryPrimitive>(attr->compiled_value.get()));
+  EXPECT_THAT(attr->compiled_value, NotNull());
+  EXPECT_THAT(ValueCast<BinaryPrimitive>(attr->compiled_value.get()), NotNull());
 }
 
 TEST_F(ManifestFixerTest, UsesFeatureMustHaveNameOrGlEsVersion) {
@@ -375,21 +377,21 @@
             <uses-feature android:glEsVersion="2" />
           </feature-group>
         </manifest>)EOF";
-  EXPECT_NE(nullptr, Verify(input));
+  EXPECT_THAT(Verify(input), NotNull());
 
   input = R"EOF(
         <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                   package="android">
           <uses-feature android:name="feature" android:glEsVersion="1" />
         </manifest>)EOF";
-  EXPECT_EQ(nullptr, Verify(input));
+  EXPECT_THAT(Verify(input), IsNull());
 
   input = R"EOF(
         <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                   package="android">
           <uses-feature />
         </manifest>)EOF";
-  EXPECT_EQ(nullptr, Verify(input));
+  EXPECT_THAT(Verify(input), IsNull());
 
   input = R"EOF(
         <manifest xmlns:android="http://schemas.android.com/apk/res/android"
@@ -398,7 +400,7 @@
             <uses-feature android:name="feature" android:glEsVersion="1" />
           </feature-group>
         </manifest>)EOF";
-  EXPECT_EQ(nullptr, Verify(input));
+  EXPECT_THAT(Verify(input), IsNull());
 
   input = R"EOF(
         <manifest xmlns:android="http://schemas.android.com/apk/res/android"
@@ -407,7 +409,7 @@
             <uses-feature />
           </feature-group>
         </manifest>)EOF";
-  EXPECT_EQ(nullptr, Verify(input));
+  EXPECT_THAT(Verify(input), IsNull());
 }
 
 TEST_F(ManifestFixerTest, IgnoreNamespacedElements) {
@@ -416,7 +418,7 @@
                 package="android">
         <special:tag whoo="true" xmlns:special="http://google.com" />
       </manifest>)EOF";
-  EXPECT_NE(nullptr, Verify(input));
+  EXPECT_THAT(Verify(input), NotNull());
 }
 
 TEST_F(ManifestFixerTest, DoNotIgnoreNonNamespacedElements) {
@@ -425,7 +427,7 @@
                 package="android">
         <tag whoo="true" />
       </manifest>)EOF";
-  EXPECT_EQ(nullptr, Verify(input));
+  EXPECT_THAT(Verify(input), IsNull());
 }
 
 TEST_F(ManifestFixerTest, SupportKeySets) {
@@ -446,4 +448,23 @@
   EXPECT_THAT(Verify(input), NotNull());
 }
 
+TEST_F(ManifestFixerTest, InsertCompileSdkVersions) {
+  std::string input = R"(
+      <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" />)";
+  ManifestFixerOptions options;
+  options.compile_sdk_version = {"28"};
+  options.compile_sdk_version_codename = {"P"};
+
+  std::unique_ptr<xml::XmlResource> manifest = VerifyWithOptions(input, options);
+  ASSERT_THAT(manifest, NotNull());
+
+  xml::Attribute* attr = manifest->root->FindAttribute(xml::kSchemaAndroid, "compileSdkVersion");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("28"));
+
+  attr = manifest->root->FindAttribute(xml::kSchemaAndroid, "compileSdkVersionCodename");
+  ASSERT_THAT(attr, NotNull());
+  EXPECT_THAT(attr->value, StrEq("P"));
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
index 3c96344..da3b879 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -137,6 +137,10 @@
   const StringPiece ext = file::GetExtension(apk_name);
   const std::string base_name = apk_name.substr(0, apk_name.rfind(ext.to_string()));
 
+  std::unordered_set<std::string> artifacts_to_keep = options.kept_artifacts;
+  std::unordered_set<std::string> filtered_artifacts;
+  std::unordered_set<std::string> kept_artifacts;
+
   // For now, just write out the stripped APK since ABI splitting doesn't modify anything else.
   for (const Artifact& artifact : config.artifacts) {
     SourcePathDiagnostics diag{{apk_name}, context_->GetDiagnostics()};
@@ -163,6 +167,20 @@
     ContextWrapper wrapped_context{context_};
     wrapped_context.SetSource({artifact_name});
 
+    if (!options.kept_artifacts.empty()) {
+      const auto& it = artifacts_to_keep.find(artifact_name);
+      if (it == artifacts_to_keep.end()) {
+        filtered_artifacts.insert(artifact_name);
+        if (context_->IsVerbose()) {
+          context_->GetDiagnostics()->Note(DiagMessage(artifact_name) << "skipping artifact");
+        }
+        continue;
+      } else {
+        artifacts_to_keep.erase(it);
+        kept_artifacts.insert(artifact_name);
+      }
+    }
+
     std::unique_ptr<ResourceTable> table =
         FilterTable(artifact, config, *apk_->GetResourceTable(), &wrapped_context, &filters);
     if (!table) {
@@ -197,6 +215,30 @@
     }
   }
 
+  // Make sure all of the requested artifacts were valid. If there are any kept artifacts left,
+  // either the config or the command line was wrong.
+  if (!artifacts_to_keep.empty()) {
+    context_->GetDiagnostics()->Error(
+        DiagMessage() << "The configuration and command line to filter artifacts do not match");
+
+    context_->GetDiagnostics()->Error(DiagMessage() << kept_artifacts.size() << " kept:");
+    for (const auto& artifact : kept_artifacts) {
+      context_->GetDiagnostics()->Error(DiagMessage() << "  " << artifact);
+    }
+
+    context_->GetDiagnostics()->Error(DiagMessage() << filtered_artifacts.size() << " filtered:");
+    for (const auto& artifact : filtered_artifacts) {
+      context_->GetDiagnostics()->Error(DiagMessage() << "  " << artifact);
+    }
+
+    context_->GetDiagnostics()->Error(DiagMessage() << artifacts_to_keep.size() << " missing:");
+    for (const auto& artifact : artifacts_to_keep) {
+      context_->GetDiagnostics()->Error(DiagMessage() << "  " << artifact);
+    }
+
+    return false;
+  }
+
   return true;
 }
 
diff --git a/tools/aapt2/optimize/MultiApkGenerator.h b/tools/aapt2/optimize/MultiApkGenerator.h
index c9b4be8..dedb610 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.h
+++ b/tools/aapt2/optimize/MultiApkGenerator.h
@@ -17,6 +17,10 @@
 #ifndef AAPT2_APKSPLITTER_H
 #define AAPT2_APKSPLITTER_H
 
+#include <memory>
+#include <string>
+#include <unordered_set>
+
 #include "Diagnostics.h"
 #include "LoadedApk.h"
 #include "configuration/ConfigurationParser.h"
@@ -27,6 +31,7 @@
   std::string out_dir;
   configuration::PostProcessingConfiguration config;
   TableFlattenerOptions table_flattener_options;
+  std::unordered_set<std::string> kept_artifacts;
 };
 
 /**
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 4a2181e..b676efb 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -199,6 +199,10 @@
   std::unique_ptr<SymbolTable::Symbol> FindByReference(
       const Reference& ref) override;
 
+  android::AssetManager* GetAssetManager() {
+    return &assets_;
+  }
+
  private:
   android::AssetManager assets_;
 
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index b0cf44a..fddb6b8 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -418,6 +418,15 @@
   return nullptr;
 }
 
+Attribute* Element::FindOrCreateAttribute(const StringPiece& ns, const StringPiece& name) {
+  Attribute* attr = FindAttribute(ns, name);
+  if (attr == nullptr) {
+    attributes.push_back(Attribute{ns.to_string(), name.to_string()});
+    attr = &attributes.back();
+  }
+  return attr;
+}
+
 Element* Element::FindChild(const StringPiece& ns, const StringPiece& name) {
   return FindChildWithAttribute(ns, name, {}, {}, {});
 }
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index cf06ba5..8f382961 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -100,6 +100,8 @@
   Attribute* FindAttribute(const android::StringPiece& ns, const android::StringPiece& name);
   const Attribute* FindAttribute(const android::StringPiece& ns,
                                  const android::StringPiece& name) const;
+  Attribute* FindOrCreateAttribute(const android::StringPiece& ns,
+                                   const android::StringPiece& name);
 
   Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name);
   const Element* FindChild(const android::StringPiece& ns, const android::StringPiece& name) const;
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index fd42033..421e545 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -940,11 +940,14 @@
 
     for f in found.values():
         takes_handler = False
+        takes_exec = False
         for m in by_name[f.name]:
             if "android.os.Handler" in m.args:
                 takes_handler = True
-        if not takes_handler:
-            warn(clazz, f, "L1", "Registration methods should have overload that accepts delivery Handler")
+            if "java.util.concurrent.Executor" in m.args:
+                takes_exec = True
+        if not takes_exec:
+            warn(clazz, f, "L1", "Registration methods should have overload that accepts delivery Executor")
 
 
 def verify_context_first(clazz):
@@ -968,7 +971,7 @@
         for a in m.args:
             if a.endswith("Callback") or a.endswith("Callbacks") or a.endswith("Listener"):
                 found = True
-            elif found and a != "android.os.Handler":
+            elif found and a != "android.os.Handler" and a != "java.util.concurrent.Executor":
                 warn(clazz, m, "M3", "Listeners should always be at end of argument list")
 
 
@@ -1218,6 +1221,18 @@
             unique_binary_op(m, m.name[:-6])  # Remove 'Assign' suffix
 
 
+def verify_collections_over_arrays(clazz):
+    """Warn that [] should be Collections."""
+
+    safe = ["java.lang.String[]","byte[]","short[]","int[]","long[]","float[]","double[]","boolean[]","char[]"]
+    for m in clazz.methods:
+        if m.typ.endswith("[]") and m.typ not in safe:
+            warn(clazz, m, None, "Method should return Collection<> (or subclass) instead of raw array")
+        for arg in m.args:
+            if arg.endswith("[]") and arg not in safe:
+                warn(clazz, m, None, "Method argument should be Collection<> (or subclass) instead of raw array")
+
+
 def examine_clazz(clazz):
     """Find all style issues in the given class."""
 
@@ -1260,7 +1275,7 @@
     verify_manager(clazz)
     verify_boxed(clazz)
     verify_static_utils(clazz)
-    verify_overload_args(clazz)
+    # verify_overload_args(clazz)
     verify_callback_handlers(clazz)
     verify_context_first(clazz)
     verify_listener_last(clazz)
@@ -1274,6 +1289,7 @@
     verify_closable(clazz)
     verify_member_name_not_kotlin_keyword(clazz)
     verify_method_name_not_kotlin_operator(clazz)
+    verify_collections_over_arrays(clazz)
 
 
 def examine_stream(stream):
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index dc5c14e..4e9391d 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -24,6 +24,10 @@
         "stream_proto_utils.cpp",
         "string_utils.cpp",
     ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 
     shared_libs: ["libprotoc"],
 }
diff --git a/tools/streaming_proto/stream_proto_utils.cpp b/tools/streaming_proto/stream_proto_utils.cpp
index e8f86bc..fb2d98d 100644
--- a/tools/streaming_proto/stream_proto_utils.cpp
+++ b/tools/streaming_proto/stream_proto_utils.cpp
@@ -13,8 +13,6 @@
 // TODO: packed is not supported yet.
 //
 const uint64_t FIELD_COUNT_SHIFT = 40;
-const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
-const uint64_t FIELD_COUNT_UNKNOWN = 0;
 const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
 const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
 const uint64_t FIELD_COUNT_PACKED = 5ULL << FIELD_COUNT_SHIFT;
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index abbb82a..0dd964c 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -164,12 +164,6 @@
     void enableAggressiveHandover(int enabled);
     int getAggressiveHandover();
 
-    void setAllowScansWithTraffic(int enabled);
-    int getAllowScansWithTraffic();
-
-    boolean setEnableAutoJoinWhenAssociated(boolean enabled);
-    boolean getEnableAutoJoinWhenAssociated();
-
     void enableWifiConnectivityManager(boolean enabled);
 
     WifiConnectionStatistics getConnectionStatistics();
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 558004c..a158d94 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -3488,27 +3488,23 @@
     }
 
     /**
-     * Set setting for allowing Scans when traffic is ongoing.
+     * Deprecated
+     * Does nothing
      * @hide
+     * @deprecated
      */
     public void setAllowScansWithTraffic(int enabled) {
-        try {
-            mService.setAllowScansWithTraffic(enabled);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return;
     }
 
     /**
-     * Get setting for allowing Scans when traffic is ongoing.
+     * Deprecated
+     * returns value for 'disabled'
      * @hide
+     * @deprecated
      */
     public int getAllowScansWithTraffic() {
-        try {
-            return mService.getAllowScansWithTraffic();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return 0;
     }
 
     /**
@@ -3538,29 +3534,23 @@
     }
 
     /**
-     * Framework layer autojoin enable/disable when device is associated
-     * this will enable/disable autojoin scan and switch network when connected
-     * @return true -- if set successful false -- if set failed
+     * Deprecated
+     * returns false
      * @hide
+     * @deprecated
      */
     public boolean setEnableAutoJoinWhenAssociated(boolean enabled) {
-        try {
-            return mService.setEnableAutoJoinWhenAssociated(enabled);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return false;
     }
 
     /**
-     * Get setting for Framework layer autojoin enable status
+     * Deprecated
+     * returns false
      * @hide
+     * @deprecated
      */
     public boolean getEnableAutoJoinWhenAssociated() {
-        try {
-            return mService.getEnableAutoJoinWhenAssociated();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return false;
     }
 
     /**
diff --git a/wifi/java/android/net/wifi/rtt/RangingRequest.java b/wifi/java/android/net/wifi/rtt/RangingRequest.java
index 7d74a72..b4e3097 100644
--- a/wifi/java/android/net/wifi/rtt/RangingRequest.java
+++ b/wifi/java/android/net/wifi/rtt/RangingRequest.java
@@ -17,6 +17,7 @@
 package android.net.wifi.rtt;
 
 import android.annotation.NonNull;
+import android.net.MacAddress;
 import android.net.wifi.ScanResult;
 import android.net.wifi.aware.AttachCallback;
 import android.net.wifi.aware.DiscoverySessionCallback;
@@ -168,7 +169,7 @@
          * @param peerMacAddress The MAC address of the Wi-Fi Aware peer.
          * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
          */
-        public Builder addWifiAwarePeer(@NonNull byte[] peerMacAddress) {
+        public Builder addWifiAwarePeer(@NonNull MacAddress peerMacAddress) {
             if (peerMacAddress == null) {
                 throw new IllegalArgumentException("Null peer MAC address");
             }
diff --git a/wifi/java/android/net/wifi/rtt/RangingResult.java b/wifi/java/android/net/wifi/rtt/RangingResult.java
index 93e52ae..a380fae 100644
--- a/wifi/java/android/net/wifi/rtt/RangingResult.java
+++ b/wifi/java/android/net/wifi/rtt/RangingResult.java
@@ -17,16 +17,15 @@
 package android.net.wifi.rtt;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.net.MacAddress;
 import android.net.wifi.aware.PeerHandle;
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import libcore.util.HexEncoding;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 
@@ -62,7 +61,7 @@
     public static final int STATUS_FAIL = 1;
 
     private final int mStatus;
-    private final byte[] mMac;
+    private final MacAddress mMac;
     private final PeerHandle mPeerHandle;
     private final int mDistanceMm;
     private final int mDistanceStdDevMm;
@@ -70,7 +69,7 @@
     private final long mTimestamp;
 
     /** @hide */
-    public RangingResult(@RangeResultStatus int status, byte[] mac, int distanceMm,
+    public RangingResult(@RangeResultStatus int status, @NonNull MacAddress mac, int distanceMm,
             int distanceStdDevMm, int rssi, long timestamp) {
         mStatus = status;
         mMac = mac;
@@ -109,7 +108,7 @@
      * Will return a {@code null} for results corresponding to requests issued using a {@code
      * PeerHandle}, i.e. using the {@link RangingRequest.Builder#addWifiAwarePeer(PeerHandle)} API.
      */
-    public byte[] getMacAddress() {
+    public MacAddress getMacAddress() {
         return mMac;
     }
 
@@ -193,7 +192,12 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mStatus);
-        dest.writeByteArray(mMac);
+        if (mMac == null) {
+            dest.writeBoolean(false);
+        } else {
+            dest.writeBoolean(true);
+            mMac.writeToParcel(dest, flags);
+        }
         if (mPeerHandle == null) {
             dest.writeBoolean(false);
         } else {
@@ -216,7 +220,11 @@
         @Override
         public RangingResult createFromParcel(Parcel in) {
             int status = in.readInt();
-            byte[] mac = in.createByteArray();
+            boolean macAddressPresent = in.readBoolean();
+            MacAddress mac = null;
+            if (macAddressPresent) {
+                mac = MacAddress.CREATOR.createFromParcel(in);
+            }
             boolean peerHandlePresent = in.readBoolean();
             PeerHandle peerHandle = null;
             if (peerHandlePresent) {
@@ -240,11 +248,11 @@
     @Override
     public String toString() {
         return new StringBuilder("RangingResult: [status=").append(mStatus).append(", mac=").append(
-                mMac == null ? "<null>" : new String(HexEncoding.encodeToString(mMac))).append(
-                ", peerHandle=").append(mPeerHandle == null ? "<null>" : mPeerHandle.peerId).append(
-                ", distanceMm=").append(mDistanceMm).append(", distanceStdDevMm=").append(
-                mDistanceStdDevMm).append(", rssi=").append(mRssi).append(", timestamp=").append(
-                mTimestamp).append("]").toString();
+                mMac).append(", peerHandle=").append(
+                mPeerHandle == null ? "<null>" : mPeerHandle.peerId).append(", distanceMm=").append(
+                mDistanceMm).append(", distanceStdDevMm=").append(mDistanceStdDevMm).append(
+                ", rssi=").append(mRssi).append(", timestamp=").append(mTimestamp).append(
+                "]").toString();
     }
 
     @Override
@@ -259,7 +267,7 @@
 
         RangingResult lhs = (RangingResult) o;
 
-        return mStatus == lhs.mStatus && Arrays.equals(mMac, lhs.mMac) && Objects.equals(
+        return mStatus == lhs.mStatus && Objects.equals(mMac, lhs.mMac) && Objects.equals(
                 mPeerHandle, lhs.mPeerHandle) && mDistanceMm == lhs.mDistanceMm
                 && mDistanceStdDevMm == lhs.mDistanceStdDevMm && mRssi == lhs.mRssi
                 && mTimestamp == lhs.mTimestamp;
diff --git a/wifi/java/android/net/wifi/rtt/ResponderConfig.java b/wifi/java/android/net/wifi/rtt/ResponderConfig.java
index 1090bfa..8be7782 100644
--- a/wifi/java/android/net/wifi/rtt/ResponderConfig.java
+++ b/wifi/java/android/net/wifi/rtt/ResponderConfig.java
@@ -24,11 +24,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import libcore.util.HexEncoding;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
 import java.util.Objects;
 
 /**
@@ -127,13 +124,13 @@
      * peerHandle field) ise used to identify the Responder.
      * TODO: convert to MacAddress
      */
-    public byte[] macAddress;
+    public MacAddress macAddress;
 
     /**
      * The peer identifier of a Wi-Fi Aware Responder. Will be null if a MAC Address (the macAddress
      * field) is used to identify the Responder.
      */
-    public final PeerHandle peerHandle;
+    public PeerHandle peerHandle;
 
     /**
      * The device type of the Responder.
@@ -194,9 +191,13 @@
      * @param preamble        The preamble used by the Responder, specified using
      *                        {@link PreambleType}.
      */
-    public ResponderConfig(@NonNull byte[] macAddress, @ResponderType int responderType,
+    public ResponderConfig(@NonNull MacAddress macAddress, @ResponderType int responderType,
             boolean supports80211mc, @ChannelWidth int channelWidth, int frequency, int centerFreq0,
             int centerFreq1, @PreambleType int preamble) {
+        if (macAddress == null) {
+            throw new IllegalArgumentException(
+                    "Invalid ResponderConfig - must specify a MAC address");
+        }
         this.macAddress = macAddress;
         this.peerHandle = null;
         this.responderType = responderType;
@@ -248,10 +249,7 @@
      * Point (AP), which can be obtained from {@link android.net.wifi.WifiManager#getScanResults()}.
      */
     public static ResponderConfig fromScanResult(ScanResult scanResult) {
-        byte[] macAddress = null;
-        if (scanResult.BSSID != null) {
-            macAddress = MacAddress.byteAddrFromStringAddr(scanResult.BSSID);
-        }
+        MacAddress macAddress = MacAddress.fromString(scanResult.BSSID);
         int responderType = RESPONDER_AP;
         boolean supports80211mc = scanResult.is80211mcResponder();
         int channelWidth = translcateScanResultChannelWidth(scanResult.channelWidth);
@@ -275,7 +273,7 @@
      * Creates a Responder configuration from a MAC address corresponding to a Wi-Fi Aware
      * Responder. The Responder parameters are set to defaults.
      */
-    public static ResponderConfig fromWifiAwarePeerMacAddressWithDefaults(byte[] macAddress) {
+    public static ResponderConfig fromWifiAwarePeerMacAddressWithDefaults(MacAddress macAddress) {
         /* Note: the parameters are those of the Aware discovery channel (channel 6). A Responder
          * is expected to be brought up and available to negotiate a maximum accuracy channel
          * (i.e. Band 5 @ 80MHz). A Responder is brought up on the peer by starting an Aware
@@ -323,11 +321,16 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeByteArray(macAddress);
-        if (peerHandle == null) {
-            dest.writeInt(0);
+        if (macAddress == null) {
+            dest.writeBoolean(false);
         } else {
-            dest.writeInt(1);
+            dest.writeBoolean(true);
+            macAddress.writeToParcel(dest, flags);
+        }
+        if (peerHandle == null) {
+            dest.writeBoolean(false);
+        } else {
+            dest.writeBoolean(true);
             dest.writeInt(peerHandle.peerId);
         }
         dest.writeInt(responderType);
@@ -347,10 +350,14 @@
 
         @Override
         public ResponderConfig createFromParcel(Parcel in) {
-            byte[] macAddress = in.createByteArray();
-            int peerHandleFlag = in.readInt();
+            boolean macAddressPresent = in.readBoolean();
+            MacAddress macAddress = null;
+            if (macAddressPresent) {
+                macAddress = MacAddress.CREATOR.createFromParcel(in);
+            }
+            boolean peerHandlePresent = in.readBoolean();
             PeerHandle peerHandle = null;
-            if (peerHandleFlag == 1) {
+            if (peerHandlePresent) {
                 peerHandle = new PeerHandle(in.readInt());
             }
             int responderType = in.readInt();
@@ -383,7 +390,7 @@
 
         ResponderConfig lhs = (ResponderConfig) o;
 
-        return Arrays.equals(macAddress, lhs.macAddress) && Objects.equals(peerHandle,
+        return Objects.equals(macAddress, lhs.macAddress) && Objects.equals(peerHandle,
                 lhs.peerHandle) && responderType == lhs.responderType
                 && supports80211mc == lhs.supports80211mc && channelWidth == lhs.channelWidth
                 && frequency == lhs.frequency && centerFreq0 == lhs.centerFreq0
@@ -399,8 +406,7 @@
     /** @hide */
     @Override
     public String toString() {
-        return new StringBuffer("ResponderConfig: macAddress=").append(
-                macAddress == null ? "<null>" : new String(HexEncoding.encode(macAddress))).append(
+        return new StringBuffer("ResponderConfig: macAddress=").append(macAddress).append(
                 ", peerHandle=").append(peerHandle == null ? "<null>" : peerHandle.peerId).append(
                 ", responderType=").append(responderType).append(", supports80211mc=").append(
                 supports80211mc).append(", channelWidth=").append(channelWidth).append(
diff --git a/wifi/tests/Android.mk b/wifi/tests/Android.mk
index c98e40a..d9f332f 100644
--- a/wifi/tests/Android.mk
+++ b/wifi/tests/Android.mk
@@ -58,6 +58,7 @@
 
 LOCAL_JAVA_LIBRARIES := \
     android.test.runner \
+    android.test.base \
 
 LOCAL_PACKAGE_NAME := FrameworksWifiApiTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index 300d425..72e95b9 100644
--- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.net.MacAddress;
 import android.net.wifi.ScanResult;
 import android.net.wifi.aware.PeerHandle;
 import android.os.Handler;
@@ -33,8 +34,6 @@
 import android.os.test.TestLooper;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import libcore.util.HexEncoding;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
@@ -73,13 +72,15 @@
     }
 
     /**
-     * Validate ranging call flow with succesful results.
+     * Validate ranging call flow with successful results.
      */
     @Test
     public void testRangeSuccess() throws Exception {
         RangingRequest request = new RangingRequest.Builder().build();
         List<RangingResult> results = new ArrayList<>();
-        results.add(new RangingResult(RangingResult.STATUS_SUCCESS, (byte[]) null, 15, 5, 10, 666));
+        results.add(
+                new RangingResult(RangingResult.STATUS_SUCCESS, MacAddress.BROADCAST_ADDRESS, 15, 5,
+                        10, 666));
         RangingResultCallback callbackMock = mock(RangingResultCallback.class);
         ArgumentCaptor<IRttCallback> callbackCaptor = ArgumentCaptor.forClass(IRttCallback.class);
 
@@ -135,7 +136,7 @@
         List<ScanResult> scanResults2and3 = new ArrayList<>(2);
         scanResults2and3.add(scanResult2);
         scanResults2and3.add(scanResult3);
-        final byte[] mac1 = HexEncoding.decode("000102030405".toCharArray(), false);
+        MacAddress mac1 = MacAddress.fromString("00:01:02:03:04:05");
         PeerHandle peerHandle1 = new PeerHandle(12);
 
         RangingRequest.Builder builder = new RangingRequest.Builder();
@@ -169,7 +170,7 @@
         for (int i = 0; i < RangingRequest.getMaxPeers() - 3; ++i) {
             scanResultList.add(scanResult);
         }
-        final byte[] mac1 = HexEncoding.decode("000102030405".toCharArray(), false);
+        MacAddress mac1 = MacAddress.fromString("00:01:02:03:04:05");
 
         // create request
         RangingRequest.Builder builder = new RangingRequest.Builder();
@@ -189,11 +190,12 @@
     @Test(expected = IllegalArgumentException.class)
     public void testRangingRequestPastLimit() {
         ScanResult scanResult = new ScanResult();
+        scanResult.BSSID = "00:01:02:03:04:05";
         List<ScanResult> scanResultList = new ArrayList<>();
         for (int i = 0; i < RangingRequest.getMaxPeers() - 2; ++i) {
             scanResultList.add(scanResult);
         }
-        final byte[] mac1 = HexEncoding.decode("000102030405".toCharArray(), false);
+        MacAddress mac1 = MacAddress.fromString("00:01:02:03:04:05");
 
         // create request
         RangingRequest.Builder builder = new RangingRequest.Builder();
@@ -228,7 +230,7 @@
     public void testRangingResultsParcel() {
         // Note: not validating parcel code of ScanResult (assumed to work)
         int status = RangingResult.STATUS_SUCCESS;
-        final byte[] mac = HexEncoding.decode("000102030405".toCharArray(), false);
+        final MacAddress mac = MacAddress.fromString("00:01:02:03:04:05");
         PeerHandle peerHandle = new PeerHandle(10);
         int distanceCm = 105;
         int distanceStdDevCm = 10;