Merge "Add system service for slices"
diff --git a/Android.bp b/Android.bp
index 4ef3af0..70b1fa0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -135,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/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index 57a61ec8..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 d230aea..f16ff14 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
@@ -6388,6 +6390,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);
@@ -6430,6 +6433,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);
@@ -6767,6 +6771,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
@@ -10590,6 +10595,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 +10626,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 +11141,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 +11238,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;
   }
@@ -15494,6 +15504,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 +15780,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 +25988,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();
@@ -39468,6 +39498,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 +39515,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 +39526,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 +39583,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 +39603,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 +39649,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;
   }
@@ -49081,6 +49127,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 +49142,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 +49207,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 {
diff --git a/api/system-current.txt b/api/system-current.txt
index 16404e4..b964d8a 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,15 @@
 
   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);
     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 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 +848,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 +956,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 +4130,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();
diff --git a/api/test-current.txt b/api/test-current.txt
index ef898a4..7e0731a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -600,32 +600,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 {
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index bc63f59..c291647 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -86,7 +86,9 @@
     // pass the event to metrics managers.
     for (auto& pair : mMetricsManagers) {
         pair.second->onLogEvent(msg);
-        flushIfNecessary(msg.GetTimestampNs(), pair.first, pair.second);
+        // TODO: THIS CHECK FAILS BECAUSE ONCE UIDMAP SIZE EXCEEDS LIMIT, DROPPING METRICS DATA
+        // DOESN'T HELP. FIX THIS.
+        // flushIfNecessary(msg.GetTimestampNs(), pair.first, pair.second);
     }
 
     // Hard-coded logic to update the isolated uid's in the uid-map.
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 0e9cd3b..fa7fe0c 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -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..bdae1ef 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -54,9 +54,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/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/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/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 4b82e68..6a76c55 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -422,57 +422,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/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..fd484c2 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -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.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/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..1fc4d42 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -70,14 +70,14 @@
         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;
@@ -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;
 
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..60d9a3d 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;
 }
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 6837be0..ca63b03 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;
     }
 }
@@ -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/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/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/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/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 5d47437..1adcc11 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|";
@@ -202,8 +209,8 @@
     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);
+    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..fa7b9a7 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|";
@@ -261,8 +270,8 @@
     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);
+    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|";
@@ -321,8 +332,8 @@
     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);
+    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/src/com/android/statsd/loadtest/ConfigFactory.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
index e7bb539..8d20f97 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
@@ -21,7 +21,7 @@
 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;
@@ -31,7 +31,7 @@
 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;
@@ -113,9 +113,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()) {
@@ -254,30 +254,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/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/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/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index f0117f2..acdad1c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8475,6 +8475,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..4925f34 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -366,4 +366,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/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index 6692e13..bfee2797 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -136,9 +136,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
@@ -349,7 +348,7 @@
      * 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)}.
+     * {@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.
@@ -382,11 +381,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());
             }
@@ -407,13 +404,9 @@
      * 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 +414,7 @@
         final IBluetoothHidDevice service = mService;
         if (service != null) {
             try {
-                result = service.unregisterApp(config);
+                result = service.unregisterApp();
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
             }
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/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
index 5ccda0d..dc6f9fa 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
@@ -37,21 +37,17 @@
      * {@link BluetoothHidDevice#registerApp
      * (String, String, String, byte, byte[], BluetoothHidDeviceCallback)}
      * or
-     * {@link BluetoothHidDevice#unregisterApp(BluetoothHidDeviceAppConfiguration)}
+     * {@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)}.
      * @param registered <code>true</code> if application is registered, <code>false</code>
      * otherwise.
      */
-    public void onAppStatusChanged(BluetoothDevice pluggedDevice,
-            BluetoothHidDeviceAppConfiguration config, boolean registered) {
+    public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
         Log.d(TAG, "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered="
                 + registered);
     }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d1e12aa..55ad5c5 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4462,11 +4462,19 @@
 
     /**
      * 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_LONG_VERSION_CODE = "android.intent.extra.LONG_VERSION_CODE";
+
+    /**
      * The app that triggered the ephemeral installation.
      * @hide
      */
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..fb3094c 100644
--- a/core/java/android/content/pm/InstantAppResolveInfo.java
+++ b/core/java/android/content/pm/InstantAppResolveInfo.java
@@ -44,10 +44,15 @@
     /** 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;
 
     public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
             @Nullable List<InstantAppIntentFilter> filters, int versionCode) {
+        this(digest, packageName, filters, (long)versionCode);
+    }
+
+    public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
+            @Nullable List<InstantAppIntentFilter> filters, long versionCode) {
         // validate arguments
         if ((packageName == null && (filters != null && filters.size() != 0))
                 || (packageName != null && (filters == null || filters.size() == 0))) {
@@ -74,7 +79,7 @@
         mPackageName = in.readString();
         mFilters = new ArrayList<InstantAppIntentFilter>();
         in.readList(mFilters, null /*loader*/);
-        mVersionCode = in.readInt();
+        mVersionCode = in.readLong();
     }
 
     public byte[] getDigestBytes() {
@@ -93,7 +98,15 @@
         return mFilters;
     }
 
+    /**
+     * @deprecated Use {@link #getLongVersionCode} instead.
+     */
+    @Deprecated
     public int getVersionCode() {
+        return (int) (mVersionCode & 0xffffffff);
+    }
+
+    public long getLongVersionCode() {
         return mVersionCode;
     }
 
@@ -107,7 +120,7 @@
         out.writeParcelable(mDigest, flags);
         out.writeString(mPackageName);
         out.writeList(mFilters);
-        out.writeInt(mVersionCode);
+        out.writeLong(mVersionCode);
     }
 
     public static final Parcelable.Creator<InstantAppResolveInfo> CREATOR
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/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/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 4b57018..a7a3df7 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 AF regions.</p>
+     * @see CaptureResult#CONTROL_AF_SCENE_CHANGE
+     */
+    public static final int CONTROL_AF_SCENE_CHANGE_NOT_DETECTED = 0;
+
+    /**
+     * <p>Scene change is detected within AF regions.</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..b7d7f7d 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2185,6 +2185,24 @@
             new Key<Boolean>("android.control.enableZsl", boolean.class);
 
     /**
+     * <p>Whether scene change is detected within AF regions.</p>
+     * <p>When AF detects a scene change within current AF regions, it will be set to DETECTED. Otherwise,
+     * it will be set to NOT_DETECTED. This value will remain NOT_DETECTED if afMode is AF_MODE_OFF or
+     * AF_MODE_EDOF.</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/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/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 5620a62..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 {
 
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/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/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/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/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..512f2df 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -48,6 +48,7 @@
 import java.util.GregorianCalendar;
 import java.util.Locale;
 import java.util.Objects;
+import java.util.TimeZone;
 import java.util.UUID;
 
 /**
@@ -692,6 +693,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 +808,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 +819,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 +1102,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/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/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/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/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/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/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/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/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/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 22bfcc3..9336ddd 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -425,14 +425,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 +445,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/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/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_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_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/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/attrs.xml b/core/res/res/values/attrs.xml
index 3b49d9d..faf3c2f 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>
 
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..d79df35 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2854,6 +2854,10 @@
       <public name="compileSdkVersionCodename" />
       <public name="screenReaderFocusable" />
       <public name="buttonCornerRadius" />
+      <public name="versionCodeMajor" />
+      <public name="versionMajor" />
+      <!-- @hide @SystemApi -->
+      <public name="isVrOnly"/>
     </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..8958af4 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] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index feb7800..cbd34f9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3163,4 +3163,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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 4f1f4d7..968e08b 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5023,6 +5023,11 @@
     // OS: P
     CONNECTION_DEVICE_ADVANCED = 1264;
 
+    // OPEN: Settings > Security > Screen lock gear icon
+    // CATEGORY: SETTINGS
+    // OS: P
+    SCREEN_LOCK_SETTINGS = 1265;
+
     // ---- End P Constants, all P constants go above this line ----
 
     // Add new aosp constants above this line.
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/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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5e67396..399760d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4522,9 +4522,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 +4594,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 +4630,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 +4653,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 +4709,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 +4727,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 +4773,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 +4951,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;
@@ -14542,7 +14598,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(")");
                         }
@@ -23824,7 +23880,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..2405890 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3899,12 +3899,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;
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..b12351b 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;
@@ -186,6 +185,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 +199,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 +299,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 +370,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 +652,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);
     }
 
     /**
@@ -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/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/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/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/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/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index e5e9a37..88fc65e 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -159,7 +159,7 @@
                     long startTime) {
                 final String packageName;
                 final String splitName;
-                final int versionCode;
+                final long versionCode;
                 final Intent failureIntent;
                 if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
                     final AuxiliaryResolveInfo instantAppIntentInfo =
@@ -241,7 +241,7 @@
             @NonNull String instantAppPackageName,
             @Nullable String instantAppSplitName,
             @Nullable ComponentName installFailureActivity,
-            int versionCode,
+            long versionCode,
             @Nullable String token,
             boolean needsPhaseTwo) {
         // Construct the intent that launches the instant installer
@@ -308,7 +308,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..4e1ed9d 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);
             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
@@ -9835,7 +9830,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(),
@@ -10403,20 +10398,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 +10419,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 +10510,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 +10528,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 +10568,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 +10610,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 +10656,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 +14651,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 +16584,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 +16892,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 +17009,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 +17034,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 +17052,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 +17063,7 @@
             } else if (highestVersion == null) {
                 highestVersion = libEntry;
             } else if (libVersionCode  > highestVersion.info
-                    .getDeclaringPackage().getVersionCode()) {
+                    .getDeclaringPackage().getLongVersionCode()) {
                 highestVersion = libEntry;
             }
         }
@@ -17192,7 +17192,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 +17243,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 +20527,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 +20548,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 +20917,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 +22274,11 @@
         return mInstallerService;
     }
 
+    @Override
+    public IArtManager getArtManager() {
+        return mArtManagerService;
+    }
+
     private boolean userNeedsBadging(int userId) {
         int index = mUserNeedsBadging.indexOfKey(userId);
         if (index < 0) {
@@ -22421,11 +22427,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 +22618,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/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index f0ce3c9..3884916 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -60,10 +60,8 @@
     // to synchronize access during policy load and access attempts.
     private static List<Policy> sPolicies = new ArrayList<>();
 
-    /** Path to MAC permissions on system image */
-    private static final File[] MAC_PERMISSIONS =
-    { new File(Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml"),
-      new File(Environment.getVendorDirectory(), "/etc/selinux/nonplat_mac_permissions.xml") };
+    // Required MAC permissions files.
+    private static List<File> sMacPermissions = new ArrayList<>();
 
     // Append privapp to existing seinfo label
     private static final String PRIVILEGED_APP_STR = ":privapp";
@@ -76,11 +74,11 @@
 
     /**
      * Load the mac_permissions.xml file containing all seinfo assignments used to
-     * label apps. The loaded mac_permissions.xml file is determined by the
-     * MAC_PERMISSIONS class variable which is set at class load time which itself
-     * is based on the USE_OVERRIDE_POLICY class variable. For further guidance on
+     * label apps. The loaded mac_permissions.xml files are plat_mac_permissions.xml and
+     * vendor_mac_permissions.xml, on /system and /vendor partitions, respectively.
+     * odm_mac_permissions.xml on /odm partition is optional. For further guidance on
      * the proper structure of a mac_permissions.xml file consult the source code
-     * located at system/sepolicy/mac_permissions.xml.
+     * located at system/sepolicy/private/mac_permissions.xml.
      *
      * @return boolean indicating if policy was correctly loaded. A value of false
      *         typically indicates a structural problem with the xml or incorrectly
@@ -93,10 +91,42 @@
 
         FileReader policyFile = null;
         XmlPullParser parser = Xml.newPullParser();
-        for (int i = 0; i < MAC_PERMISSIONS.length; i++) {
+
+        synchronized (sMacPermissions) {
+            // Only initialize it once.
+            if (sMacPermissions.isEmpty()) {
+                // Platform mac permissions.
+                sMacPermissions.add(new File(
+                    Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml"));
+
+                // Vendor mac permissions.
+                // The filename has been renamed from nonplat_mac_permissions to
+                // vendor_mac_permissions. Either of them should exist.
+                File vendorMacPermission = new File(
+                    Environment.getVendorDirectory(), "/etc/selinux/vendor_mac_permissions.xml");
+                if (vendorMacPermission.exists()) {
+                    sMacPermissions.add(vendorMacPermission);
+                } else {
+                    // For backward compatibility.
+                    sMacPermissions.add(new File(Environment.getVendorDirectory(),
+                                                 "/etc/selinux/nonplat_mac_permissions.xml"));
+                }
+
+                // ODM mac permissions (optional).
+                File odmMacPermission = new File(
+                    Environment.getOdmDirectory(), "/etc/selinux/odm_mac_permissions.xml");
+                if (odmMacPermission.exists()) {
+                    sMacPermissions.add(odmMacPermission);
+                }
+            }
+        }
+
+        final int count = sMacPermissions.size();
+        for (int i = 0; i < count; ++i) {
+            File macPermission = sMacPermissions.get(i);
             try {
-                policyFile = new FileReader(MAC_PERMISSIONS[i]);
-                Slog.d(TAG, "Using policy file " + MAC_PERMISSIONS[i]);
+                policyFile = new FileReader(macPermission);
+                Slog.d(TAG, "Using policy file " + macPermission);
 
                 parser.setInput(policyFile);
                 parser.nextTag();
@@ -120,13 +150,13 @@
                 StringBuilder sb = new StringBuilder("Exception @");
                 sb.append(parser.getPositionDescription());
                 sb.append(" while parsing ");
-                sb.append(MAC_PERMISSIONS[i]);
+                sb.append(macPermission);
                 sb.append(":");
                 sb.append(ex);
                 Slog.w(TAG, sb.toString());
                 return false;
             } catch (IOException ioe) {
-                Slog.w(TAG, "Exception parsing " + MAC_PERMISSIONS[i], ioe);
+                Slog.w(TAG, "Exception parsing " + macPermission, ioe);
                 return false;
             } finally {
                 IoUtils.closeQuietly(policyFile);
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..5a1f840
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -0,0 +1,155 @@
+/*
+ * 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.Handler;
+import android.os.RemoteException;
+import android.content.pm.IPackageManager;
+import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
+import android.os.SystemProperties;
+import android.util.Slog;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.Preconditions;
+
+/**
+ * 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 Handler mHandler;
+
+    public ArtManagerService(IPackageManager pm) {
+        mPackageManager = pm;
+        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, move forward and get the profile.
+        postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+    }
+
+    @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) {
+        mHandler.post(() -> {
+            try {
+                callback.onError(errCode);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to callback 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/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 3810192..8a40b4d 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";
 
@@ -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/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/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/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..57f754f 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -349,8 +349,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;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 663083c..ddc0c23 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -736,6 +736,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 +786,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 +1104,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 +1282,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);
@@ -11433,4 +11443,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/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/notification/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/notification/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/notification/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/notification/src/com/android/server/notification/ScheduleConditionProviderTest.java
index ddf46a0..ba5ad81 100644
--- a/services/tests/notification/src/com/android/server/notification/ScheduleConditionProviderTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ScheduleConditionProviderTest.java
@@ -6,6 +6,7 @@
 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;
@@ -34,7 +35,9 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        Looper.prepare();
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
 
         Intent startIntent =
                 new Intent("com.android.server.notification.ScheduleConditionProvider");
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/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/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/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/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 2bb1c4e..3c32614 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,9 +396,7 @@
     /**
      * 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;
 
     //**********************************************************************************************
@@ -831,9 +828,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 +2603,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 +2612,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 +2625,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 +2633,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 +2850,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 +2864,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/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/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/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/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/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..1bdb7625 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/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;
     }
 
     /**