Merge "Update VectorDrawables to use Skia's drawArc implementation."
diff --git a/Android.bp b/Android.bp
index 487bf7a..70b1fa0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -98,6 +98,7 @@
         "core/java/android/app/backup/IRestoreObserver.aidl",
         "core/java/android/app/backup/IRestoreSession.aidl",
         "core/java/android/app/backup/ISelectBackupTransportCallback.aidl",
+        "core/java/android/app/slice/ISliceManager.aidl",
         "core/java/android/app/timezone/ICallback.aidl",
         "core/java/android/app/timezone/IRulesManager.aidl",
         "core/java/android/app/usage/ICacheQuotaService.aidl",
diff --git a/Android.mk b/Android.mk
index ce504a5..d4d2baa 100644
--- a/Android.mk
+++ b/Android.mk
@@ -95,6 +95,7 @@
 	frameworks/base/core/java/android/app/admin/NetworkEvent.aidl \
 	frameworks/base/core/java/android/app/admin/SystemUpdatePolicy.aidl \
 	frameworks/base/core/java/android/app/admin/PasswordMetrics.aidl \
+	frameworks/base/core/java/android/app/slice/ISliceManager.aidl \
 	frameworks/base/core/java/android/print/PrintDocumentInfo.aidl \
 	frameworks/base/core/java/android/print/PageRange.aidl \
 	frameworks/base/core/java/android/print/PrintAttributes.aidl \
diff --git a/api/current.txt b/api/current.txt
index 38b5090..a30d560 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1505,6 +1505,7 @@
     field public static final deprecated int weekSeparatorLineColor = 16843590; // 0x1010346
     field public static final int weightSum = 16843048; // 0x1010128
     field public static final int widgetCategory = 16843716; // 0x10103c4
+    field public static final int widgetFeatures = 16844153; // 0x1010579
     field public static final int widgetLayout = 16843243; // 0x10101eb
     field public static final int width = 16843097; // 0x1010159
     field public static final int windowActionBar = 16843469; // 0x10102cd
@@ -4994,7 +4995,7 @@
   public class KeyguardManager {
     method public android.content.Intent createConfirmDeviceCredentialIntent(java.lang.CharSequence, java.lang.CharSequence);
     method public deprecated void exitKeyguardSecurely(android.app.KeyguardManager.OnKeyguardExitResult);
-    method public boolean inKeyguardRestrictedInputMode();
+    method public deprecated boolean inKeyguardRestrictedInputMode();
     method public boolean isDeviceLocked();
     method public boolean isDeviceSecure();
     method public boolean isKeyguardLocked();
@@ -6322,6 +6323,7 @@
     method public android.os.UserHandle createAndManageUser(android.content.ComponentName, java.lang.String, android.content.ComponentName, android.os.PersistableBundle, int);
     method public void enableSystemApp(android.content.ComponentName, java.lang.String);
     method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
+    method public android.security.AttestedKeyPair generateKeyPair(android.content.ComponentName, java.lang.String, android.security.keystore.KeyGenParameterSpec);
     method public java.lang.String[] getAccountTypesWithManagementDisabled();
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
     method public java.util.Set<java.lang.String> getAffiliationIds(android.content.ComponentName);
@@ -6370,6 +6372,7 @@
     method public java.util.List<java.lang.String> getPermittedInputMethods(android.content.ComponentName);
     method public long getRequiredStrongAuthTimeout(android.content.ComponentName);
     method public boolean getScreenCaptureDisabled(android.content.ComponentName);
+    method public java.util.List<android.os.UserHandle> getSecondaryUsers(android.content.ComponentName);
     method public java.lang.CharSequence getShortSupportMessage(android.content.ComponentName);
     method public boolean getStorageEncryption(android.content.ComponentName);
     method public int getStorageEncryptionStatus();
@@ -6402,6 +6405,7 @@
     method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String);
     method public void lockNow();
     method public void lockNow(int);
+    method public boolean logoutUser(android.content.ComponentName);
     method public void reboot(android.content.ComponentName);
     method public void removeActiveAdmin(android.content.ComponentName);
     method public boolean removeCrossProfileWidgetProvider(android.content.ComponentName, java.lang.String);
@@ -6476,6 +6480,7 @@
     method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
     method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
     method public void setUserIcon(android.content.ComponentName, android.graphics.Bitmap);
+    method public boolean stopUser(android.content.ComponentName, android.os.UserHandle);
     method public boolean switchUser(android.content.ComponentName, android.os.UserHandle);
     method public void uninstallAllUserCaCerts(android.content.ComponentName);
     method public void uninstallCaCert(android.content.ComponentName, byte[]);
@@ -7348,6 +7353,8 @@
     field public static final int WIDGET_CATEGORY_HOME_SCREEN = 1; // 0x1
     field public static final int WIDGET_CATEGORY_KEYGUARD = 2; // 0x2
     field public static final int WIDGET_CATEGORY_SEARCHBOX = 4; // 0x4
+    field public static final int WIDGET_FEATURE_HIDE_FROM_PICKER = 2; // 0x2
+    field public static final int WIDGET_FEATURE_RECONFIGURABLE = 1; // 0x1
     field public int autoAdvanceViewId;
     field public android.content.ComponentName configure;
     field public int icon;
@@ -7363,6 +7370,7 @@
     field public int resizeMode;
     field public int updatePeriodMillis;
     field public int widgetCategory;
+    field public int widgetFeatures;
   }
 
 }
@@ -10077,6 +10085,7 @@
 
   public abstract interface ServiceConnection {
     method public default void onBindingDied(android.content.ComponentName);
+    method public default void onNullBinding(android.content.ComponentName);
     method public abstract void onServiceConnected(android.content.ComponentName, android.os.IBinder);
     method public abstract void onServiceDisconnected(android.content.ComponentName);
   }
@@ -11252,6 +11261,8 @@
 package android.content.pm.crossprofile {
 
   public class CrossProfileApps {
+    method public android.graphics.drawable.Drawable getProfileSwitchingIcon(android.os.UserHandle);
+    method public java.lang.CharSequence getProfileSwitchingLabel(android.os.UserHandle);
     method public java.util.List<android.os.UserHandle> getTargetUserProfiles();
     method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
   }
@@ -15504,6 +15515,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
@@ -15778,6 +15791,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;
@@ -25985,6 +25999,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();
@@ -37119,6 +37150,11 @@
 
 package android.security {
 
+  public final class AttestedKeyPair {
+    method public java.util.List<java.security.cert.Certificate> getAttestationRecord();
+    method public java.security.KeyPair getKeyPair();
+  }
+
   public final class KeyChain {
     ctor public KeyChain();
     method public static void choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, java.lang.String[], java.security.Principal[], java.lang.String, int, java.lang.String);
@@ -39641,12 +39677,15 @@
     method public final void addConference(android.telecom.Conference);
     method public final void addExistingConnection(android.telecom.PhoneAccountHandle, android.telecom.Connection);
     method public final void conferenceRemoteConnections(android.telecom.RemoteConnection, android.telecom.RemoteConnection);
+    method public final void connectionServiceFocusReleased();
     method public final android.telecom.RemoteConnection createRemoteIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public final android.telecom.RemoteConnection createRemoteOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public final java.util.Collection<android.telecom.Conference> getAllConferences();
     method public final java.util.Collection<android.telecom.Connection> getAllConnections();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConference(android.telecom.Connection, android.telecom.Connection);
+    method public void onConnectionServiceFocusGained();
+    method public void onConnectionServiceFocusLost();
     method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
     method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
@@ -47950,7 +47989,6 @@
     method public void interrupt();
     method public static boolean isAccessibilityButtonSupported();
     method public boolean isEnabled();
-    method public boolean isObservedEventType(int);
     method public boolean isTouchExplorationEnabled();
     method public void removeAccessibilityRequestPreparer(android.view.accessibility.AccessibilityRequestPreparer);
     method public boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
diff --git a/api/system-current.txt b/api/system-current.txt
index 1ce043c..1d206c17 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -184,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
@@ -782,11 +783,12 @@
 
   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(android.content.pm.InstantAppResolveInfo.InstantAppDigest, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>, long, android.os.Bundle);
     ctor public InstantAppResolveInfo(java.lang.String, java.lang.String, java.util.List<android.content.pm.InstantAppIntentFilter>);
     method public int describeContents();
     method public byte[] getDigestBytes();
     method public int getDigestPrefix();
+    method public android.os.Bundle getExtras();
     method public java.util.List<android.content.pm.InstantAppIntentFilter> getIntentFilters();
     method public long getLongVersionCode();
     method public java.lang.String getPackageName();
diff --git a/api/test-current.txt b/api/test-current.txt
index 7e0731a..3c3521f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -425,7 +425,7 @@
 
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
     field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
-    field public static final java.lang.String AUTOFILL_FEATURE_FIELD_DETECTION = "autofill_field_detection";
+    field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
     field public static final java.lang.String AUTOFILL_SERVICE = "autofill_service";
     field public static final java.lang.String DISABLED_PRINT_SERVICES = "disabled_print_services";
     field public static final deprecated java.lang.String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
@@ -457,8 +457,31 @@
     method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
   }
 
+  public final class EditDistanceScorer extends android.service.autofill.InternalScorer implements android.os.Parcelable android.service.autofill.Scorer {
+    method public int describeContents();
+    method public static android.service.autofill.EditDistanceScorer getInstance();
+    method public float getScore(android.view.autofill.AutofillValue, java.lang.String);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.EditDistanceScorer> CREATOR;
+  }
+
+  public final class FieldClassification implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.util.List<android.service.autofill.FieldClassification.Match> getMatches();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.FieldClassification> CREATOR;
+  }
+
+  public static final class FieldClassification.Match implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.lang.String getRemoteId();
+    method public float getScore();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.FieldClassification.Match> CREATOR;
+  }
+
   public static final class FillEventHistory.Event {
-    method public java.util.Map<java.lang.String, java.lang.Integer> getFieldsClassification();
+    method public java.util.Map<android.view.autofill.AutofillId, android.service.autofill.FieldClassification> getFieldsClassification();
   }
 
   public static final class FillResponse.Builder {
@@ -473,6 +496,11 @@
     ctor public InternalSanitizer();
   }
 
+  public abstract class InternalScorer implements android.os.Parcelable android.service.autofill.Scorer {
+    ctor public InternalScorer();
+    method public abstract float getScore(android.view.autofill.AutofillValue, java.lang.String);
+  }
+
   public abstract class InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
     ctor public InternalTransformation();
   }
@@ -490,6 +518,9 @@
     method public boolean isValid(android.service.autofill.ValueFinder);
   }
 
+  public abstract interface Scorer {
+  }
+
   public final class TextValueSanitizer extends android.service.autofill.InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
     method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
   }
@@ -505,7 +536,7 @@
   }
 
   public static final class UserData.Builder {
-    ctor public UserData.Builder(java.lang.String, java.lang.String);
+    ctor public UserData.Builder(android.service.autofill.Scorer, java.lang.String, java.lang.String);
     method public android.service.autofill.UserData.Builder add(java.lang.String, java.lang.String);
     method public android.service.autofill.UserData build();
   }
@@ -941,15 +972,6 @@
 
 package android.view.accessibility {
 
-  public final class AccessibilityManager {
-    method public void addAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener, android.os.Handler);
-    method public void removeAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
-  }
-
-  public static abstract interface AccessibilityManager.AccessibilityServicesStateChangeListener {
-    method public abstract void onAccessibilityServicesStateChanged(android.view.accessibility.AccessibilityManager);
-  }
-
   public class AccessibilityNodeInfo implements android.os.Parcelable {
     method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
   }
@@ -968,6 +990,7 @@
 
   public final class AutofillManager {
     method public android.service.autofill.UserData getUserData();
+    method public boolean isFieldClassificationEnabled();
     method public void setUserData(android.service.autofill.UserData);
   }
 
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index c6b8418..c291647 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -84,12 +84,12 @@
 void StatsLogProcessor::OnLogEvent(const LogEvent& msg) {
     StatsdStats::getInstance().noteAtomLogged(msg.GetTagId(), msg.GetTimestampNs() / NS_PER_SEC);
     // pass the event to metrics managers.
-    // TODO: THIS CHECK FAILS BECAUSE ONCE UIDMAP SIZE EXCEEDS LIMIT, DROPPING METRICS DATA
-    // DOESN'T HELP. FIX THIS.
-    //for (auto& pair : mMetricsManagers) {
-    //    pair.second->onLogEvent(msg);
-    //    flushIfNecessary(msg.GetTimestampNs(), pair.first, pair.second);
-    //}
+    for (auto& pair : mMetricsManagers) {
+        pair.second->onLogEvent(msg);
+        // 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.
     // The field numbers need to be currently updated by hand with atoms.proto
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index dd84cf4..02aca1a 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -39,11 +39,11 @@
     VLOG("~CombinationConditionTracker() %s", mName.c_str());
 }
 
-bool CombinationConditionTracker::init(const vector<Condition>& allConditionConfig,
+bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConfig,
                                        const vector<sp<ConditionTracker>>& allConditionTrackers,
                                        const unordered_map<string, int>& conditionNameIndexMap,
                                        vector<bool>& stack) {
-    VLOG("Combiniation condition init() %s", mName.c_str());
+    VLOG("Combination predicate init() %s", mName.c_str());
     if (mInitialized) {
         return true;
     }
@@ -51,22 +51,22 @@
     // mark this node as visited in the recursion stack.
     stack[mIndex] = true;
 
-    Condition_Combination combinationCondition = allConditionConfig[mIndex].combination();
+    Predicate_Combination combinationCondition = allConditionConfig[mIndex].combination();
 
     if (!combinationCondition.has_operation()) {
         return false;
     }
     mLogicalOperation = combinationCondition.operation();
 
-    if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.condition_size() != 1) {
+    if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.predicate_size() != 1) {
         return false;
     }
 
-    for (string child : combinationCondition.condition()) {
+    for (string child : combinationCondition.predicate()) {
         auto it = conditionNameIndexMap.find(child);
 
         if (it == conditionNameIndexMap.end()) {
-            ALOGW("Condition %s not found in the config", child.c_str());
+            ALOGW("Predicate %s not found in the config", child.c_str());
             return false;
         }
 
@@ -154,7 +154,7 @@
             }
         }
         nonSlicedConditionCache[mIndex] = ConditionState::kUnknown;
-        ALOGD("CombinationCondition %s sliced may changed? %d", mName.c_str(),
+        ALOGD("CombinationPredicate %s sliced may changed? %d", mName.c_str(),
               conditionChangedCache[mIndex] == true);
     }
 }
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index 00dc6b7..9336914 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -30,7 +30,7 @@
 
     ~CombinationConditionTracker();
 
-    bool init(const std::vector<Condition>& allConditionConfig,
+    bool init(const std::vector<Predicate>& allConditionConfig,
               const std::vector<sp<ConditionTracker>>& allConditionTrackers,
               const std::unordered_map<std::string, int>& conditionNameIndexMap,
               std::vector<bool>& stack) override;
@@ -47,7 +47,7 @@
 
 private:
     LogicalOperation mLogicalOperation;
-    // Store index of the children Conditions.
+    // Store index of the children Predicates.
     // We don't store string name of the Children, because we want to get rid of the hash map to
     // map the name to object. We don't want to store smart pointers to children, because it
     // increases the risk of circular dependency and memory leak.
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index b85d8c1..6f66ad6 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -45,12 +45,12 @@
     // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also
     // be done in the constructor, but we do it separately because (1) easy to return a bool to
     // indicate whether the initialization is successful. (2) makes unit test easier.
-    // allConditionConfig: the list of all Condition config from statsd_config.
+    // allConditionConfig: the list of all Predicate config from statsd_config.
     // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
     //                       need to call init() on children conditions)
     // conditionNameIndexMap: the mapping from condition name to its index.
     // stack: a bit map to keep track which nodes have been visited on the stack in the recursion.
-    virtual bool init(const std::vector<Condition>& allConditionConfig,
+    virtual bool init(const std::vector<Predicate>& allConditionConfig,
                       const std::vector<sp<ConditionTracker>>& allConditionTrackers,
                       const std::unordered_map<std::string, int>& conditionNameIndexMap,
                       std::vector<bool>& stack) = 0;
@@ -118,4 +118,3 @@
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
-
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index 1a01afa..30a3684 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -24,7 +24,7 @@
 namespace os {
 namespace statsd {
 
-// Held by MetricProducer, to query a condition state with input defined in EventConditionLink.
+// Held by MetricProducer, to query a condition state with input defined in MetricConditionLink.
 class ConditionWizard : public virtual android::RefBase {
 public:
     ConditionWizard(){};  // for testing
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 50cd130..18b93ee 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -34,16 +34,16 @@
 
 SimpleConditionTracker::SimpleConditionTracker(
         const ConfigKey& key, const string& name, const int index,
-        const SimpleCondition& simpleCondition,
+        const SimplePredicate& simplePredicate,
         const unordered_map<string, int>& trackerNameIndexMap)
     : ConditionTracker(name, index), mConfigKey(key) {
     VLOG("creating SimpleConditionTracker %s", mName.c_str());
-    mCountNesting = simpleCondition.count_nesting();
+    mCountNesting = simplePredicate.count_nesting();
 
-    if (simpleCondition.has_start()) {
-        auto pair = trackerNameIndexMap.find(simpleCondition.start());
+    if (simplePredicate.has_start()) {
+        auto pair = trackerNameIndexMap.find(simplePredicate.start());
         if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Start matcher %s not found in the config", simpleCondition.start().c_str());
+            ALOGW("Start matcher %s not found in the config", simplePredicate.start().c_str());
             return;
         }
         mStartLogMatcherIndex = pair->second;
@@ -52,10 +52,10 @@
         mStartLogMatcherIndex = -1;
     }
 
-    if (simpleCondition.has_stop()) {
-        auto pair = trackerNameIndexMap.find(simpleCondition.stop());
+    if (simplePredicate.has_stop()) {
+        auto pair = trackerNameIndexMap.find(simplePredicate.stop());
         if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Stop matcher %s not found in the config", simpleCondition.stop().c_str());
+            ALOGW("Stop matcher %s not found in the config", simplePredicate.stop().c_str());
             return;
         }
         mStopLogMatcherIndex = pair->second;
@@ -64,10 +64,10 @@
         mStopLogMatcherIndex = -1;
     }
 
-    if (simpleCondition.has_stop_all()) {
-        auto pair = trackerNameIndexMap.find(simpleCondition.stop_all());
+    if (simplePredicate.has_stop_all()) {
+        auto pair = trackerNameIndexMap.find(simplePredicate.stop_all());
         if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Stop all matcher %s not found in the config", simpleCondition.stop().c_str());
+            ALOGW("Stop all matcher %s not found in the config", simplePredicate.stop().c_str());
             return;
         }
         mStopAllLogMatcherIndex = pair->second;
@@ -76,14 +76,14 @@
         mStopAllLogMatcherIndex = -1;
     }
 
-    mOutputDimension.insert(mOutputDimension.begin(), simpleCondition.dimension().begin(),
-                            simpleCondition.dimension().end());
+    mOutputDimension.insert(mOutputDimension.begin(), simplePredicate.dimension().begin(),
+                            simplePredicate.dimension().end());
 
     if (mOutputDimension.size() > 0) {
         mSliced = true;
     }
 
-    if (simpleCondition.initial_value() == SimpleCondition_InitialValue_FALSE) {
+    if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) {
         mInitialValue = ConditionState::kFalse;
     } else {
         mInitialValue = ConditionState::kUnknown;
@@ -98,7 +98,7 @@
     VLOG("~SimpleConditionTracker()");
 }
 
-bool SimpleConditionTracker::init(const vector<Condition>& allConditionConfig,
+bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig,
                                   const vector<sp<ConditionTracker>>& allConditionTrackers,
                                   const unordered_map<string, int>& conditionNameIndexMap,
                                   vector<bool>& stack) {
@@ -139,7 +139,7 @@
         StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mName, newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
-            ALOGE("Condition %s dropping data for dimension key %s", mName.c_str(), newKey.c_str());
+            ALOGE("Predicate %s dropping data for dimension key %s", mName.c_str(), newKey.c_str());
             return true;
         }
     }
@@ -221,7 +221,7 @@
     conditionChangedCache[mIndex] = changed;
     conditionCache[mIndex] = newCondition;
 
-    VLOG("SimpleCondition %s nonSlicedChange? %d", mName.c_str(),
+    VLOG("SimplePredicate %s nonSlicedChange? %d", mName.c_str(),
          conditionChangedCache[mIndex] == true);
 }
 
@@ -292,13 +292,13 @@
             (pair == conditionParameters.end()) ? DEFAULT_DIMENSION_KEY : pair->second;
 
     if (pair == conditionParameters.end() && mOutputDimension.size() > 0) {
-        ALOGE("Condition %s output has dimension, but it's not specified in the query!",
+        ALOGE("Predicate %s output has dimension, but it's not specified in the query!",
               mName.c_str());
         conditionCache[mIndex] = mInitialValue;
         return;
     }
 
-    VLOG("simpleCondition %s query key: %s", mName.c_str(), key.c_str());
+    VLOG("simplePredicate %s query key: %s", mName.c_str(), key.c_str());
 
     auto startedCountIt = mSlicedConditionState.find(key);
     if (startedCountIt == mSlicedConditionState.end()) {
@@ -308,7 +308,7 @@
                 startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
     }
 
-    VLOG("Condition %s return %d", mName.c_str(), conditionCache[mIndex]);
+    VLOG("Predicate %s return %d", mName.c_str(), conditionCache[mIndex]);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index d21afd1..644d84c 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -30,12 +30,12 @@
 class SimpleConditionTracker : public virtual ConditionTracker {
 public:
     SimpleConditionTracker(const ConfigKey& key, const std::string& name, const int index,
-                           const SimpleCondition& simpleCondition,
+                           const SimplePredicate& simplePredicate,
                            const std::unordered_map<std::string, int>& trackerNameIndexMap);
 
     ~SimpleConditionTracker();
 
-    bool init(const std::vector<Condition>& allConditionConfig,
+    bool init(const std::vector<Predicate>& allConditionConfig,
               const std::vector<sp<ConditionTracker>>& allConditionTrackers,
               const std::unordered_map<std::string, int>& conditionNameIndexMap,
               std::vector<bool>& stack) override;
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index 669a4b7..ff0e3bc 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -94,17 +94,17 @@
 }
 
 HashableDimensionKey getDimensionKeyForCondition(const LogEvent& event,
-                                                 const EventConditionLink& link) {
+                                                 const MetricConditionLink& link) {
     vector<KeyMatcher> eventKey;
-    eventKey.reserve(link.key_in_main().size());
+    eventKey.reserve(link.key_in_what().size());
 
-    for (const auto& key : link.key_in_main()) {
+    for (const auto& key : link.key_in_what()) {
         eventKey.push_back(key);
     }
 
     vector<KeyValuePair> dimensionKey = getDimensionKey(event, eventKey);
 
-    for (int i = 0; i < link.key_in_main_size(); i++) {
+    for (int i = 0; i < link.key_in_what_size(); i++) {
         auto& kv = dimensionKey[i];
         kv.set_key(link.key_in_condition(i).key());
     }
diff --git a/cmds/statsd/src/condition/condition_util.h b/cmds/statsd/src/condition/condition_util.h
index 4167bf9..934c207 100644
--- a/cmds/statsd/src/condition/condition_util.h
+++ b/cmds/statsd/src/condition/condition_util.h
@@ -37,7 +37,7 @@
                                             const std::vector<ConditionState>& conditionCache);
 
 HashableDimensionKey getDimensionKeyForCondition(const LogEvent& event,
-                                                 const EventConditionLink& link);
+                                                 const MetricConditionLink& link);
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 4b82e68..540199d 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -214,7 +214,8 @@
     int UID_PROCESS_STATE_UID_KEY = 1;
 
     int KERNEL_WAKELOCK_TAG_ID = 1004;
-    int KERNEL_WAKELOCK_NAME_KEY = 4;
+    int KERNEL_WAKELOCK_COUNT_KEY = 2;
+    int KERNEL_WAKELOCK_NAME_KEY = 1;
 
     int DEVICE_TEMPERATURE_TAG_ID = 33;
     int DEVICE_TEMPERATURE_KEY = 1;
@@ -272,9 +273,9 @@
     keyMatcher = metric->add_dimension();
     keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
     metric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
-    EventConditionLink* link = metric->add_links();
+    MetricConditionLink* link = metric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
-    link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
+    link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID);
     link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
 
     // Duration of an app holding any wl, while screen on and app in background, slice by uid
@@ -288,7 +289,7 @@
     durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
-    link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
+    link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID);
     link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
 
     // max Duration of an app holding any wl, while screen on and app in background, slice by uid
@@ -302,7 +303,7 @@
     durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
-    link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
+    link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID);
     link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
 
     // Duration of an app holding any wl, while screen on and app in background
@@ -314,7 +315,7 @@
     durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
-    link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID);
+    link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID);
     link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
 
     // Duration of screen on time.
@@ -338,7 +339,7 @@
     ValueMetric* valueMetric = config.add_value_metric();
     valueMetric->set_name("METRIC_6");
     valueMetric->set_what("KERNEL_WAKELOCK");
-    valueMetric->set_value_field(1);
+    valueMetric->set_value_field(KERNEL_WAKELOCK_COUNT_KEY);
     valueMetric->set_condition("SCREEN_IS_ON");
     keyMatcher = valueMetric->add_dimension();
     keyMatcher->set_key(KERNEL_WAKELOCK_NAME_KEY);
@@ -422,57 +423,57 @@
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
     simpleAtomMatcher->set_tag(KERNEL_WAKELOCK_TAG_ID);
 
-    // Conditions.............
-    Condition* condition = config.add_condition();
-    condition->set_name("SCREEN_IS_ON");
-    SimpleCondition* simpleCondition = condition->mutable_simple_condition();
-    simpleCondition->set_start("SCREEN_TURNED_ON");
-    simpleCondition->set_stop("SCREEN_TURNED_OFF");
-    simpleCondition->set_count_nesting(false);
+    // Predicates.............
+    Predicate* predicate = config.add_predicate();
+    predicate->set_name("SCREEN_IS_ON");
+    SimplePredicate* simplePredicate = predicate->mutable_simple_predicate();
+    simplePredicate->set_start("SCREEN_TURNED_ON");
+    simplePredicate->set_stop("SCREEN_TURNED_OFF");
+    simplePredicate->set_count_nesting(false);
 
-    condition = config.add_condition();
-    condition->set_name("SCREEN_IS_OFF");
-    simpleCondition = condition->mutable_simple_condition();
-    simpleCondition->set_start("SCREEN_TURNED_OFF");
-    simpleCondition->set_stop("SCREEN_TURNED_ON");
-    simpleCondition->set_count_nesting(false);
+    predicate = config.add_predicate();
+    predicate->set_name("SCREEN_IS_OFF");
+    simplePredicate = predicate->mutable_simple_predicate();
+    simplePredicate->set_start("SCREEN_TURNED_OFF");
+    simplePredicate->set_stop("SCREEN_TURNED_ON");
+    simplePredicate->set_count_nesting(false);
 
-    condition = config.add_condition();
-    condition->set_name("APP_IS_BACKGROUND");
-    simpleCondition = condition->mutable_simple_condition();
-    simpleCondition->set_start("APP_GOES_BACKGROUND");
-    simpleCondition->set_stop("APP_GOES_FOREGROUND");
-    KeyMatcher* condition_dimension1 = simpleCondition->add_dimension();
-    condition_dimension1->set_key(APP_USAGE_UID_KEY_ID);
-    simpleCondition->set_count_nesting(false);
+    predicate = config.add_predicate();
+    predicate->set_name("APP_IS_BACKGROUND");
+    simplePredicate = predicate->mutable_simple_predicate();
+    simplePredicate->set_start("APP_GOES_BACKGROUND");
+    simplePredicate->set_stop("APP_GOES_FOREGROUND");
+    KeyMatcher* predicate_dimension1 = simplePredicate->add_dimension();
+    predicate_dimension1->set_key(APP_USAGE_UID_KEY_ID);
+    simplePredicate->set_count_nesting(false);
 
-    condition = config.add_condition();
-    condition->set_name("APP_IS_BACKGROUND_AND_SCREEN_ON");
-    Condition_Combination* combination_condition = condition->mutable_combination();
-    combination_condition->set_operation(LogicalOperation::AND);
-    combination_condition->add_condition("APP_IS_BACKGROUND");
-    combination_condition->add_condition("SCREEN_IS_ON");
+    predicate = config.add_predicate();
+    predicate->set_name("APP_IS_BACKGROUND_AND_SCREEN_ON");
+    Predicate_Combination* combination_predicate = predicate->mutable_combination();
+    combination_predicate->set_operation(LogicalOperation::AND);
+    combination_predicate->add_predicate("APP_IS_BACKGROUND");
+    combination_predicate->add_predicate("SCREEN_IS_ON");
 
-    condition = config.add_condition();
-    condition->set_name("WL_HELD_PER_APP_PER_NAME");
-    simpleCondition = condition->mutable_simple_condition();
-    simpleCondition->set_start("APP_GET_WL");
-    simpleCondition->set_stop("APP_RELEASE_WL");
-    KeyMatcher* condition_dimension = simpleCondition->add_dimension();
-    condition_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
-    condition_dimension = simpleCondition->add_dimension();
-    condition_dimension->set_key(WAKE_LOCK_NAME_KEY);
-    simpleCondition->set_count_nesting(true);
+    predicate = config.add_predicate();
+    predicate->set_name("WL_HELD_PER_APP_PER_NAME");
+    simplePredicate = predicate->mutable_simple_predicate();
+    simplePredicate->set_start("APP_GET_WL");
+    simplePredicate->set_stop("APP_RELEASE_WL");
+    KeyMatcher* predicate_dimension = simplePredicate->add_dimension();
+    predicate_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
+    predicate_dimension = simplePredicate->add_dimension();
+    predicate_dimension->set_key(WAKE_LOCK_NAME_KEY);
+    simplePredicate->set_count_nesting(true);
 
-    condition = config.add_condition();
-    condition->set_name("WL_HELD_PER_APP");
-    simpleCondition = condition->mutable_simple_condition();
-    simpleCondition->set_start("APP_GET_WL");
-    simpleCondition->set_stop("APP_RELEASE_WL");
-    simpleCondition->set_initial_value(SimpleCondition_InitialValue_FALSE);
-    condition_dimension = simpleCondition->add_dimension();
-    condition_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
-    simpleCondition->set_count_nesting(true);
+    predicate = config.add_predicate();
+    predicate->set_name("WL_HELD_PER_APP");
+    simplePredicate = predicate->mutable_simple_predicate();
+    simplePredicate->set_start("APP_GET_WL");
+    simplePredicate->set_stop("APP_RELEASE_WL");
+    simplePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
+    predicate_dimension = simplePredicate->add_dimension();
+    predicate_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
+    simplePredicate->set_count_nesting(true);
 
     return config;
 }
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index e1c02d7..ffe1be9 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -49,15 +49,15 @@
             return false;
         }
         data->clear();
-        long timestamp = time(nullptr);
+        int timestamp = time(nullptr);
         for (const StatsLogEventWrapper& it : returned_value) {
             log_msg tmp;
             tmp.entry_v1.len = it.bytes.size();
             // Manually set the header size to 28 bytes to match the pushed log events.
             tmp.entry.hdr_size = kLogMsgHeaderSize;
+            tmp.entry_v1.sec = timestamp;
             // And set the received bytes starting after the 28 bytes reserved for header.
             std::copy(it.bytes.begin(), it.bytes.end(), tmp.buf + kLogMsgHeaderSize);
-            tmp.entry_v1.sec = timestamp;
             data->push_back(make_shared<LogEvent>(tmp));
         }
         ALOGD("StatsCompanionServicePuller::pull succeeded for %d", tagId);
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 534d54e..9e88e5d0 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -117,6 +117,9 @@
                     allMatched = false;
                     break;
                 }
+            } else {
+                allMatched = false;
+                break;
             }
         } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqInt ||
                    matcherCase == KeyValueMatcher::ValueMatcherCase::kLtInt ||
@@ -153,6 +156,9 @@
                         break;
                     }
                 }
+            } else {
+                allMatched = false;
+                break;
             }
         } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqBool) {
             // Boolean fields
@@ -163,6 +169,9 @@
                     allMatched = false;
                     break;
                 }
+            } else {
+                allMatched = false;
+                break;
             }
         } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtFloat ||
                    matcherCase == KeyValueMatcher::ValueMatcherCase::kGtFloat) {
@@ -181,6 +190,9 @@
                         break;
                     }
                 }
+            } else {
+                allMatched = false;
+                break;
             }
         } else {
             // If value matcher is not present, assume that we match.
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 50cc8d4..1f6bd58b 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -203,7 +203,7 @@
         return;
     }
     for (const auto& data : allData) {
-        onMatchedLogEvent(0, *data, false /*scheduledPull*/);
+        onMatchedLogEventLocked(0, *data, false /*scheduledPull*/);
     }
     flushIfNeededLocked(eventTime);
 }
@@ -227,7 +227,7 @@
     std::lock_guard<std::mutex> lock(mMutex);
 
     for (const auto& data : allData) {
-        onMatchedLogEvent(0, *data, true /*scheduledPull*/);
+        onMatchedLogEventLocked(0, *data, true /*scheduledPull*/);
     }
 }
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 819df77..e7e84ab 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -137,7 +137,7 @@
     // that StatsLogReport wants.
     std::unordered_map<HashableDimensionKey, std::vector<KeyValuePair>> mDimensionKeyMap;
 
-    std::vector<EventConditionLink> mConditionLinks;
+    std::vector<MetricConditionLink> mConditionLinks;
 
     std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
 
@@ -149,7 +149,7 @@
      * [eventKey]: the extracted dimension key for the final output. if the metric doesn't have
      *             dimensions, it will be DEFAULT_DIMENSION_KEY
      * [conditionKey]: the keys of conditions which should be used to query the condition for this
-     *                 target event (from EventConditionLink). This is passed to individual metrics
+     *                 target event (from MetricConditionLink). This is passed to individual metrics
      *                 because DurationMetric needs it to be cached.
      * [condition]: whether condition is met. If condition is sliced, this is the result coming from
      *              query with ConditionWizard; If condition is not sliced, this is the
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 00048f5..943becb 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -64,20 +64,20 @@
 bool handleMetricWithConditions(
         const string condition, const int metricIndex,
         const unordered_map<string, int>& conditionTrackerMap,
-        const ::google::protobuf::RepeatedPtrField<::android::os::statsd::EventConditionLink>&
+        const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>&
                 links,
         vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
         unordered_map<int, std::vector<int>>& conditionToMetricMap) {
     auto condition_it = conditionTrackerMap.find(condition);
     if (condition_it == conditionTrackerMap.end()) {
-        ALOGW("cannot find Condition \"%s\" in the config", condition.c_str());
+        ALOGW("cannot find Predicate \"%s\" in the config", condition.c_str());
         return false;
     }
 
     for (const auto& link : links) {
         auto it = conditionTrackerMap.find(link.condition());
         if (it == conditionTrackerMap.end()) {
-            ALOGW("cannot find Condition \"%s\" in the config", link.condition().c_str());
+            ALOGW("cannot find Predicate \"%s\" in the config", link.condition().c_str());
             return false;
         }
         allConditionTrackers[condition_it->second]->setSliced(true);
@@ -142,31 +142,31 @@
                     unordered_map<string, int>& conditionTrackerMap,
                     vector<sp<ConditionTracker>>& allConditionTrackers,
                     unordered_map<int, std::vector<int>>& trackerToConditionMap) {
-    vector<Condition> conditionConfigs;
-    const int conditionTrackerCount = config.condition_size();
+    vector<Predicate> conditionConfigs;
+    const int conditionTrackerCount = config.predicate_size();
     conditionConfigs.reserve(conditionTrackerCount);
     allConditionTrackers.reserve(conditionTrackerCount);
 
     for (int i = 0; i < conditionTrackerCount; i++) {
-        const Condition& condition = config.condition(i);
+        const Predicate& condition = config.predicate(i);
         int index = allConditionTrackers.size();
         switch (condition.contents_case()) {
-            case Condition::ContentsCase::kSimpleCondition: {
+            case Predicate::ContentsCase::kSimplePredicate: {
                 allConditionTrackers.push_back(new SimpleConditionTracker(
-                        key, condition.name(), index, condition.simple_condition(), logTrackerMap));
+                        key, condition.name(), index, condition.simple_predicate(), logTrackerMap));
                 break;
             }
-            case Condition::ContentsCase::kCombination: {
+            case Predicate::ContentsCase::kCombination: {
                 allConditionTrackers.push_back(
                         new CombinationConditionTracker(condition.name(), index));
                 break;
             }
             default:
-                ALOGE("Condition \"%s\" malformed", condition.name().c_str());
+                ALOGE("Predicate \"%s\" malformed", condition.name().c_str());
                 return false;
         }
         if (conditionTrackerMap.find(condition.name()) != conditionTrackerMap.end()) {
-            ALOGE("Duplicate Condition found!");
+            ALOGE("Duplicate Predicate found!");
             return false;
         }
         conditionTrackerMap[condition.name()] = index;
@@ -232,7 +232,7 @@
             }
         } else {
             if (metric.links_size() > 0) {
-                ALOGW("metrics has a EventConditionLink but doesn't have a condition");
+                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
                 return false;
             }
         }
@@ -254,43 +254,43 @@
             return false;
         }
 
-        const Condition& durationWhat = config.condition(what_it->second);
+        const Predicate& durationWhat = config.predicate(what_it->second);
 
-        if (durationWhat.contents_case() != Condition::ContentsCase::kSimpleCondition) {
+        if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
             ALOGE("DurationMetric's \"what\" must be a simple condition");
             return false;
         }
 
-        const auto& simpleCondition = durationWhat.simple_condition();
+        const auto& simplePredicate = durationWhat.simple_predicate();
 
-        bool nesting = simpleCondition.count_nesting();
+        bool nesting = simplePredicate.count_nesting();
 
         int trackerIndices[3] = {-1, -1, -1};
-        if (!simpleCondition.has_start() ||
-            !handleMetricWithLogTrackers(simpleCondition.start(), metricIndex,
+        if (!simplePredicate.has_start() ||
+            !handleMetricWithLogTrackers(simplePredicate.start(), metricIndex,
                                          metric.dimension_size() > 0, allAtomMatchers,
                                          logTrackerMap, trackerToMetricMap, trackerIndices[0])) {
             ALOGE("Duration metrics must specify a valid the start event matcher");
             return false;
         }
 
-        if (simpleCondition.has_stop() &&
-            !handleMetricWithLogTrackers(simpleCondition.stop(), metricIndex,
+        if (simplePredicate.has_stop() &&
+            !handleMetricWithLogTrackers(simplePredicate.stop(), metricIndex,
                                          metric.dimension_size() > 0, allAtomMatchers,
                                          logTrackerMap, trackerToMetricMap, trackerIndices[1])) {
             return false;
         }
 
-        if (simpleCondition.has_stop_all() &&
-            !handleMetricWithLogTrackers(simpleCondition.stop_all(), metricIndex,
+        if (simplePredicate.has_stop_all() &&
+            !handleMetricWithLogTrackers(simplePredicate.stop_all(), metricIndex,
                                          metric.dimension_size() > 0, allAtomMatchers,
                                          logTrackerMap, trackerToMetricMap, trackerIndices[2])) {
             return false;
         }
 
         vector<KeyMatcher> internalDimension;
-        internalDimension.insert(internalDimension.begin(), simpleCondition.dimension().begin(),
-                                 simpleCondition.dimension().end());
+        internalDimension.insert(internalDimension.begin(), simplePredicate.dimension().begin(),
+                                 simplePredicate.dimension().end());
 
         int conditionIndex = -1;
 
@@ -303,7 +303,7 @@
             }
         } else {
             if (metric.links_size() > 0) {
-                ALOGW("metrics has a EventConditionLink but doesn't have a condition");
+                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
                 return false;
             }
         }
@@ -340,7 +340,7 @@
             }
         } else {
             if (metric.links_size() > 0) {
-                ALOGW("metrics has a EventConditionLink but doesn't have a condition");
+                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
                 return false;
             }
         }
@@ -390,7 +390,7 @@
             }
         } else {
             if (metric.links_size() > 0) {
-                ALOGW("metrics has a EventConditionLink but doesn't have a condition");
+                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
                 return false;
             }
         }
@@ -439,7 +439,7 @@
             }
         } else {
             if (metric.links_size() > 0) {
-                ALOGW("metrics has a EventConditionLink but doesn't have a condition");
+                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
                 return false;
             }
         }
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 751d8df..8e9c8e3 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -62,7 +62,7 @@
                     std::unordered_map<std::string, int>& conditionTrackerMap,
                     std::vector<sp<ConditionTracker>>& allConditionTrackers,
                     std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
-                    std::unordered_map<int, std::vector<EventConditionLink>>& eventConditionLinks);
+                    std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks);
 
 // Initialize MetricProducers.
 // input:
@@ -79,7 +79,7 @@
         const ConfigKey& key, const StatsdConfig& config,
         const std::unordered_map<std::string, int>& logTrackerMap,
         const std::unordered_map<std::string, int>& conditionTrackerMap,
-        const std::unordered_map<int, std::vector<EventConditionLink>>& eventConditionLinks,
+        const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
         const vector<sp<LogMatchingTracker>>& allAtomMatchers,
         vector<sp<ConditionTracker>>& allConditionTrackers,
         std::vector<sp<MetricProducer>>& allMetricProducers,
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 6837be0..c9654af 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -75,7 +75,7 @@
     }
 }
 
-message SimpleCondition {
+message SimplePredicate {
     optional string start = 1;
 
     optional string stop = 2;
@@ -93,17 +93,17 @@
     repeated KeyMatcher dimension = 6;
 }
 
-message Condition {
+message Predicate {
     optional string name = 1;
 
     message Combination {
         optional LogicalOperation operation = 1;
 
-        repeated string condition = 2;
+        repeated string predicate = 2;
     }
 
     oneof contents {
-        SimpleCondition simple_condition = 2;
+        SimplePredicate simple_predicate = 2;
         Combination combination = 3;
     }
 }
@@ -112,10 +112,10 @@
     optional int64 bucket_size_millis = 1;
 }
 
-message EventConditionLink {
+message MetricConditionLink {
     optional string condition = 1;
 
-    repeated KeyMatcher key_in_main = 2;
+    repeated KeyMatcher key_in_what = 2;
 
     repeated KeyMatcher key_in_condition = 3;
 }
@@ -127,7 +127,7 @@
 
     optional string condition = 3;
 
-    repeated EventConditionLink links = 4;
+    repeated MetricConditionLink links = 4;
 }
 
 message CountMetric {
@@ -141,7 +141,7 @@
 
     optional Bucket bucket = 5;
 
-    repeated EventConditionLink links = 6;
+    repeated MetricConditionLink links = 6;
 }
 
 message DurationMetric {
@@ -151,7 +151,7 @@
 
     optional string condition = 3;
 
-    repeated EventConditionLink links = 4;
+    repeated MetricConditionLink links = 4;
 
     enum AggregationType {
         SUM = 1;
@@ -178,7 +178,7 @@
 
     optional Bucket bucket = 6;
 
-    repeated EventConditionLink links = 7;
+    repeated MetricConditionLink links = 7;
 }
 
 message ValueMetric {
@@ -194,7 +194,7 @@
 
     optional Bucket bucket = 6;
 
-    repeated EventConditionLink links = 7;
+    repeated MetricConditionLink links = 7;
 
     enum AggregationType { SUM = 1; }
     optional AggregationType aggregation_type = 8 [default = SUM];
@@ -232,7 +232,7 @@
 
     repeated AtomMatcher atom_matcher = 7;
 
-    repeated Condition condition = 8;
+    repeated Predicate predicate = 8;
 
     repeated Alert alert = 9;
 }
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index e4750e9..c6a5310 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -34,7 +34,7 @@
 using std::set;
 using std::unordered_map;
 using std::vector;
-using android::os::statsd::Condition;
+using android::os::statsd::Predicate;
 
 #ifdef __ANDROID__
 
@@ -165,7 +165,7 @@
     return config;
 }
 
-StatsdConfig buildMissingCondition() {
+StatsdConfig buildMissingPredicate() {
     StatsdConfig config;
     config.set_name("12345");
 
@@ -223,7 +223,7 @@
     return config;
 }
 
-StatsdConfig buildCircleConditions() {
+StatsdConfig buildCirclePredicates() {
     StatsdConfig config;
     config.set_name("12345");
 
@@ -247,19 +247,19 @@
     simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
 
-    auto condition = config.add_condition();
+    auto condition = config.add_predicate();
     condition->set_name("SCREEN_IS_ON");
-    SimpleCondition* simpleCondition = condition->mutable_simple_condition();
-    simpleCondition->set_start("SCREEN_IS_ON");
-    simpleCondition->set_stop("SCREEN_IS_OFF");
+    SimplePredicate* simplePredicate = condition->mutable_simple_predicate();
+    simplePredicate->set_start("SCREEN_IS_ON");
+    simplePredicate->set_stop("SCREEN_IS_OFF");
 
-    condition = config.add_condition();
+    condition = config.add_predicate();
     condition->set_name("SCREEN_IS_EITHER_ON_OFF");
 
-    Condition_Combination* combination = condition->mutable_combination();
+    Predicate_Combination* combination = condition->mutable_combination();
     combination->set_operation(LogicalOperation::OR);
-    combination->add_condition("SCREEN_IS_ON");
-    combination->add_condition("SCREEN_IS_EITHER_ON_OFF");
+    combination->add_predicate("SCREEN_IS_ON");
+    combination->add_predicate("SCREEN_IS_EITHER_ON_OFF");
 
     return config;
 }
@@ -329,8 +329,8 @@
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
-TEST(MetricsManagerTest, TestMissingCondition) {
-    StatsdConfig config = buildMissingCondition();
+TEST(MetricsManagerTest, TestMissingPredicate) {
+    StatsdConfig config = buildMissingPredicate();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
@@ -344,8 +344,8 @@
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
-TEST(MetricsManagerTest, TestCircleConditionDependency) {
-    StatsdConfig config = buildCircleConditions();
+TEST(MetricsManagerTest, TestCirclePredicateDependency) {
+    StatsdConfig config = buildCirclePredicates();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
     vector<sp<ConditionTracker>> allConditionTrackers;
diff --git a/cmds/statsd/tests/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/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index dc42943..9b94099 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -141,9 +141,9 @@
     metric.set_name("1");
     metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
     metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
-    EventConditionLink* link = metric.add_links();
+    MetricConditionLink* link = metric.add_links();
     link->set_condition("APP_IN_BACKGROUND_PER_UID");
-    link->add_key_in_main()->set_key(1);
+    link->add_key_in_what()->set_key(1);
     link->add_key_in_condition()->set_key(2);
 
     LogEvent event1(1, bucketStartTimeNs + 1);
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index 724ad59..f3302fd 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -91,9 +91,9 @@
     EventMetric metric;
     metric.set_name("1");
     metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
-    EventConditionLink* link = metric.add_links();
+    MetricConditionLink* link = metric.add_links();
     link->set_condition("APP_IN_BACKGROUND_PER_UID");
-    link->add_key_in_main()->set_key(1);
+    link->add_key_in_what()->set_key(1);
     link->add_key_in_condition()->set_key(2);
 
     LogEvent event1(1, bucketStartTimeNs + 1);
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
index e7bb539..071e1f9 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,17 +21,17 @@
 import android.util.StatsLog;
 
 import com.android.internal.os.StatsdConfigProto.Bucket;
-import com.android.internal.os.StatsdConfigProto.Condition;
+import com.android.internal.os.StatsdConfigProto.Predicate;
 import com.android.internal.os.StatsdConfigProto.CountMetric;
 import com.android.internal.os.StatsdConfigProto.DurationMetric;
-import com.android.internal.os.StatsdConfigProto.EventConditionLink;
+import com.android.internal.os.StatsdConfigProto.MetricConditionLink;
 import com.android.internal.os.StatsdConfigProto.EventMetric;
 import com.android.internal.os.StatsdConfigProto.GaugeMetric;
 import com.android.internal.os.StatsdConfigProto.ValueMetric;
 import com.android.internal.os.StatsdConfigProto.KeyMatcher;
 import com.android.internal.os.StatsdConfigProto.KeyValueMatcher;
 import com.android.internal.os.StatsdConfigProto.AtomMatcher;
-import com.android.internal.os.StatsdConfigProto.SimpleCondition;
+import com.android.internal.os.StatsdConfigProto.SimplePredicate;
 import com.android.internal.os.StatsdConfigProto.StatsdConfig;
 
 import java.io.InputStream;
@@ -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()) {
@@ -130,13 +130,13 @@
     }
 
     /**
-     * Creates {@link EventConditionLink}s that are identical to the one passed to this method,
+     * Creates {@link MetricConditionLink}s that are identical to the one passed to this method,
      * except that the names are appended with the provided suffix.
      */
-    private List<EventConditionLink> getLinks(
-        List<EventConditionLink> links, int suffix) {
-        List<EventConditionLink> newLinks = new ArrayList();
-        for (EventConditionLink link : links) {
+    private List<MetricConditionLink> getLinks(
+        List<MetricConditionLink> links, int suffix) {
+        List<MetricConditionLink> newLinks = new ArrayList();
+        for (MetricConditionLink link : links) {
             newLinks.add(link.toBuilder()
                 .setCondition(link.getCondition() + suffix)
                 .build());
@@ -156,7 +156,7 @@
             metric.setCondition(template.getCondition() + suffix);
         }
         if (template.getLinksCount() > 0) {
-            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
             metric.clearLinks();
             metric.addAllLinks(links);
         }
@@ -182,7 +182,7 @@
             metric.setCondition(template.getCondition() + suffix);
         }
         if (template.getLinksCount() > 0) {
-            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
             metric.clearLinks();
             metric.addAllLinks(links);
         }
@@ -203,7 +203,7 @@
             metric.setCondition(template.getCondition() + suffix);
         }
         if (template.getLinksCount() > 0) {
-            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
             metric.clearLinks();
             metric.addAllLinks(links);
         }
@@ -224,7 +224,7 @@
             metric.setCondition(template.getCondition() + suffix);
         }
         if (template.getLinksCount() > 0) {
-            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
             metric.clearLinks();
             metric.addAllLinks(links);
         }
@@ -245,7 +245,7 @@
             metric.setCondition(template.getCondition() + suffix);
         }
         if (template.getLinksCount() > 0) {
-            List<EventConditionLink> links = getLinks(template.getLinksList(), suffix);
+            List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
             metric.clearLinks();
             metric.addAllLinks(links);
         }
@@ -254,30 +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/cmds/uiautomator/instrumentation/Android.mk b/cmds/uiautomator/instrumentation/Android.mk
index e6cbdb4..008bb93 100644
--- a/cmds/uiautomator/instrumentation/Android.mk
+++ b/cmds/uiautomator/instrumentation/Android.mk
@@ -22,7 +22,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, testrunner-src) \
     $(call all-java-files-under, ../library/core-src)
 LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := legacy-android-test junit
+LOCAL_STATIC_JAVA_LIBRARIES := junit
 LOCAL_MODULE := uiautomator-instrumentation
 # TODO: change this to 18 when it's available
 LOCAL_SDK_VERSION := current
diff --git a/cmds/uiautomator/library/Android.mk b/cmds/uiautomator/library/Android.mk
index 4bf856f..22cffe6 100644
--- a/cmds/uiautomator/library/Android.mk
+++ b/cmds/uiautomator/library/Android.mk
@@ -28,8 +28,8 @@
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(uiautomator.core_src_files)
 LOCAL_MODULE := uiautomator.core
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
+LOCAL_STATIC_JAVA_LIBRARIES := junit
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 ###############################################
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index e0d60cd..06a9b06 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -16,8 +16,6 @@
 
 package android.accessibilityservice;
 
-import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
-
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -49,6 +47,8 @@
 import java.util.Collections;
 import java.util.List;
 
+import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
+
 /**
  * This class describes an {@link AccessibilityService}. The system notifies an
  * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s
@@ -554,7 +554,7 @@
     }
 
     /**
-     * Updates the properties that an AccessibilityService can change dynamically.
+     * Updates the properties that an AccessibilitySerivice can change dynamically.
      *
      * @param other The info from which to update the properties.
      *
diff --git a/core/java/android/app/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/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 1fe2900..d0f84c8 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -382,6 +382,8 @@
     }
 
     /**
+     * @deprecated Use {@link #isKeyguardLocked()} instead.
+     *
      * If keyguard screen is showing or in restricted key input mode (i.e. in
      * keyguard password emergency screen). When in such mode, certain keys,
      * such as the Home key and the right soft keys, don't work.
@@ -389,11 +391,7 @@
      * @return true if in keyguard restricted input mode.
      */
     public boolean inKeyguardRestrictedInputMode() {
-        try {
-            return mWM.inKeyguardRestrictedInputMode();
-        } catch (RemoteException ex) {
-            return false;
-        }
+        return isKeyguardLocked();
     }
 
     /**
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index de6230c..ebd1014 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1646,9 +1646,12 @@
             if (dead) {
                 mConnection.onBindingDied(name);
             }
-            // If there is a new service, it is now connected.
+            // If there is a new viable service, it is now connected.
             if (service != null) {
                 mConnection.onServiceConnected(name, service);
+            } else {
+                // The binding machinery worked, but the remote returned null from onBind().
+                mConnection.onNullBinding(name);
             }
         }
 
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index e48946f..f831ae2 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -22,6 +22,7 @@
 import android.app.admin.IDevicePolicyManager;
 import android.app.job.IJobScheduler;
 import android.app.job.JobScheduler;
+import android.app.slice.SliceManager;
 import android.app.timezone.RulesManager;
 import android.app.trust.TrustManager;
 import android.app.usage.IStorageStatsManager;
@@ -944,6 +945,16 @@
                                 ICrossProfileApps.Stub.asInterface(b));
                     }
                 });
+
+        registerService(Context.SLICE_SERVICE, SliceManager.class,
+                new CachedServiceFetcher<SliceManager>() {
+                    @Override
+                    public SliceManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        return new SliceManager(ctx.getOuterContext(),
+                                ctx.mMainThread.getHandler());
+                    }
+            });
     }
 
     /**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index acdad1c..562b981 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -57,7 +57,12 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.ContactsContract.Directory;
+import android.security.AttestedKeyPair;
 import android.security.Credentials;
+import android.security.KeyChain;
+import android.security.KeyChainException;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
 import android.service.restrictions.RestrictionsReceiver;
 import android.telephony.TelephonyManager;
 import android.util.ArraySet;
@@ -75,6 +80,7 @@
 import java.net.InetSocketAddress;
 import java.net.Proxy;
 import java.security.KeyFactory;
+import java.security.KeyPair;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.cert.Certificate;
@@ -3943,6 +3949,50 @@
     }
 
     /**
+     * Called by a device or profile owner, or delegated certificate installer, to generate a
+     * new private/public key pair. If the device supports key generation via secure hardware,
+     * this method is useful for creating a key in KeyChain that never left the secure hardware.
+     *
+     * Access to the key is controlled the same way as in {@link #installKeyPair}.
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     *            {@code null} if calling from a delegated certificate installer.
+     * @param algorithm The key generation algorithm, see {@link java.security.KeyPairGenerator}.
+     * @param keySpec Specification of the key to generate, see
+     * {@link java.security.KeyPairGenerator}.
+     * @return A non-null {@code AttestedKeyPair} if the key generation succeeded, null otherwise.
+     * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
+     *         owner.
+     * @throws IllegalArgumentException if the alias in {@code keySpec} is empty, or if the
+     *         algorithm specification in {@code keySpec} is not {@code RSAKeyGenParameterSpec}
+     *         or {@code ECGenParameterSpec}.
+     */
+    public AttestedKeyPair generateKeyPair(@Nullable ComponentName admin,
+            @NonNull String algorithm, @NonNull KeyGenParameterSpec keySpec) {
+        throwIfParentInstance("generateKeyPair");
+        try {
+            final ParcelableKeyGenParameterSpec parcelableSpec =
+                    new ParcelableKeyGenParameterSpec(keySpec);
+            final boolean success = mService.generateKeyPair(
+                    admin, mContext.getPackageName(), algorithm, parcelableSpec);
+            if (!success) {
+                Log.e(TAG, "Error generating key via DevicePolicyManagerService.");
+                return null;
+            }
+
+            final KeyPair keyPair = KeyChain.getKeyPair(mContext, keySpec.getKeystoreAlias());
+            return new AttestedKeyPair(keyPair, null);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (KeyChainException e) {
+            Log.w(TAG, "Failed to generate key", e);
+        } catch (InterruptedException e) {
+            Log.w(TAG, "Interrupted while generating key", e);
+            Thread.currentThread().interrupt();
+        }
+        return null;
+    }
+
+    /**
      * @return the alias of a given CA certificate in the certificate store, or {@code null} if it
      * doesn't exist.
      */
@@ -6219,7 +6269,7 @@
      * @return {@code true} if the user was removed, {@code false} otherwise.
      * @throws SecurityException if {@code admin} is not a device owner.
      */
-    public boolean removeUser(@NonNull ComponentName admin, UserHandle userHandle) {
+    public boolean removeUser(@NonNull ComponentName admin, @NonNull UserHandle userHandle) {
         throwIfParentInstance("removeUser");
         try {
             return mService.removeUser(admin, userHandle);
@@ -6230,6 +6280,7 @@
 
     /**
      * Called by a device owner to switch the specified user to the foreground.
+     * <p> This cannot be used to switch to a managed profile.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param userHandle the user to switch to; null will switch to primary.
@@ -6247,6 +6298,65 @@
     }
 
     /**
+     * Called by a device owner to stop the specified secondary user.
+     * <p> This cannot be used to stop the primary user or a managed profile.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param userHandle the user to be stopped.
+     * @return {@code true} if the user can be stopped, {@code false} otherwise.
+     * @throws SecurityException if {@code admin} is not a device owner.
+     */
+    public boolean stopUser(@NonNull ComponentName admin, @NonNull UserHandle userHandle) {
+        throwIfParentInstance("stopUser");
+        try {
+            return mService.stopUser(admin, userHandle);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Called by a profile owner that is affiliated with the device owner to stop the calling user
+     * and switch back to primary.
+     * <p> This has no effect when called on a managed profile.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @return {@code true} if the exit was successful, {@code false} otherwise.
+     * @throws SecurityException if {@code admin} is not a profile owner affiliated with the device
+     * owner.
+     */
+    public boolean logoutUser(@NonNull ComponentName admin) {
+        throwIfParentInstance("logoutUser");
+        try {
+            return mService.logoutUser(admin);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Called by a device owner to list all secondary users on the device, excluding managed
+     * profiles.
+     * <p> Used for various user management APIs, including {@link #switchUser}, {@link #removeUser}
+     * and {@link #stopUser}.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @return list of other {@link UserHandle}s on the device.
+     * @throws SecurityException if {@code admin} is not a device owner.
+     * @see #switchUser
+     * @see #removeUser
+     * @see #stopUser
+     */
+    public List<UserHandle> getSecondaryUsers(@NonNull ComponentName admin) {
+        throwIfParentInstance("getSecondaryUsers");
+        try {
+            return mService.getSecondaryUsers(admin);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Retrieves the application restrictions for a given target application running in the calling
      * user.
      * <p>
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 4925f34..c525df7 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -36,6 +36,7 @@
 import android.os.PersistableBundle;
 import android.os.RemoteCallback;
 import android.os.UserHandle;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
 
 import java.util.List;
 
@@ -165,6 +166,7 @@
             in byte[] certBuffer, in byte[] certChainBuffer, String alias, boolean requestAccess,
             boolean isUserSelectable);
     boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias);
+    boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, in ParcelableKeyGenParameterSpec keySpec);
     void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback);
 
     void setDelegatedScopes(in ComponentName who, in String delegatePackage, in List<String> scopes);
@@ -215,6 +217,9 @@
     UserHandle createAndManageUser(in ComponentName who, in String name, in ComponentName profileOwner, in PersistableBundle adminExtras, in int flags);
     boolean removeUser(in ComponentName who, in UserHandle userHandle);
     boolean switchUser(in ComponentName who, in UserHandle userHandle);
+    boolean stopUser(in ComponentName who, in UserHandle userHandle);
+    boolean logoutUser(in ComponentName who);
+    List<UserHandle> getSecondaryUsers(in ComponentName who);
 
     void enableSystemApp(in ComponentName admin, in String callerPackage, in String packageName);
     int enableSystemAppWithIntent(in ComponentName admin, in String callerPackage, in Intent intent);
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
new file mode 100644
index 0000000..6e52f38
--- /dev/null
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.slice;
+
+/** @hide */
+interface ISliceManager {
+}
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
new file mode 100644
index 0000000..e99f676
--- /dev/null
+++ b/core/java/android/app/slice/SliceManager.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.slice;
+
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.Handler;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+
+/**
+ * @hide
+ */
+@SystemService(Context.SLICE_SERVICE)
+public class SliceManager {
+
+    private final ISliceManager mService;
+    private final Context mContext;
+
+    public SliceManager(Context context, Handler handler) throws ServiceNotFoundException {
+        mContext = context;
+        mService = ISliceManager.Stub.asInterface(
+                ServiceManager.getServiceOrThrow(Context.SLICE_SERVICE));
+    }
+}
diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java
index fd1b0e0..75ce4fb 100644
--- a/core/java/android/appwidget/AppWidgetProviderInfo.java
+++ b/core/java/android/appwidget/AppWidgetProviderInfo.java
@@ -17,15 +17,17 @@
 package android.appwidget;
 
 import android.annotation.NonNull;
+import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.res.ResourceId;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.content.ComponentName;
 import android.os.UserHandle;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
@@ -69,6 +71,23 @@
     public static final int WIDGET_CATEGORY_SEARCHBOX = 4;
 
     /**
+     * The widget can be reconfigured anytime after it is bound by starting the
+     * {@link #configure} activity.
+     *
+     * @see #widgetFeatures
+     */
+    public static final int WIDGET_FEATURE_RECONFIGURABLE = 1;
+
+    /**
+     * The widget is added directly by the app, and the host may hide this widget when providing
+     * the user with the list of available widgets to choose from.
+     *
+     * @see AppWidgetManager#requestPinAppWidget(ComponentName, Bundle, PendingIntent)
+     * @see #widgetFeatures
+     */
+    public static final int WIDGET_FEATURE_HIDE_FROM_PICKER = 2;
+
+    /**
      * Identity of this AppWidget component.  This component should be a {@link
      * android.content.BroadcastReceiver}, and it will be sent the AppWidget intents
      * {@link android.appwidget as described in the AppWidget package documentation}.
@@ -209,6 +228,15 @@
      */
     public int widgetCategory;
 
+    /**
+     * Flags indicating various features supported by the widget. These are hints to the widget
+     * host, and do not actually change the behavior of the widget.
+     *
+     * @see #WIDGET_FEATURE_RECONFIGURABLE
+     * @see #WIDGET_FEATURE_HIDE_FROM_PICKER
+     */
+    public int widgetFeatures;
+
     /** @hide */
     public ActivityInfo providerInfo;
 
@@ -221,9 +249,7 @@
      */
     @SuppressWarnings("deprecation")
     public AppWidgetProviderInfo(Parcel in) {
-        if (0 != in.readInt()) {
-            this.provider = new ComponentName(in);
-        }
+        this.provider = in.readTypedObject(ComponentName.CREATOR);
         this.minWidth = in.readInt();
         this.minHeight = in.readInt();
         this.minResizeWidth = in.readInt();
@@ -231,16 +257,15 @@
         this.updatePeriodMillis = in.readInt();
         this.initialLayout = in.readInt();
         this.initialKeyguardLayout = in.readInt();
-        if (0 != in.readInt()) {
-            this.configure = new ComponentName(in);
-        }
+        this.configure = in.readTypedObject(ComponentName.CREATOR);
         this.label = in.readString();
         this.icon = in.readInt();
         this.previewImage = in.readInt();
         this.autoAdvanceViewId = in.readInt();
         this.resizeMode = in.readInt();
         this.widgetCategory = in.readInt();
-        this.providerInfo = in.readParcelable(null);
+        this.providerInfo = in.readTypedObject(ActivityInfo.CREATOR);
+        this.widgetFeatures = in.readInt();
     }
 
     /**
@@ -308,13 +333,8 @@
 
     @Override
     @SuppressWarnings("deprecation")
-    public void writeToParcel(android.os.Parcel out, int flags) {
-        if (this.provider != null) {
-            out.writeInt(1);
-            this.provider.writeToParcel(out, flags);
-        } else {
-            out.writeInt(0);
-        }
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeTypedObject(this.provider, flags);
         out.writeInt(this.minWidth);
         out.writeInt(this.minHeight);
         out.writeInt(this.minResizeWidth);
@@ -322,19 +342,15 @@
         out.writeInt(this.updatePeriodMillis);
         out.writeInt(this.initialLayout);
         out.writeInt(this.initialKeyguardLayout);
-        if (this.configure != null) {
-            out.writeInt(1);
-            this.configure.writeToParcel(out, flags);
-        } else {
-            out.writeInt(0);
-        }
+        out.writeTypedObject(this.configure, flags);
         out.writeString(this.label);
         out.writeInt(this.icon);
         out.writeInt(this.previewImage);
         out.writeInt(this.autoAdvanceViewId);
         out.writeInt(this.resizeMode);
         out.writeInt(this.widgetCategory);
-        out.writeParcelable(this.providerInfo, flags);
+        out.writeTypedObject(this.providerInfo, flags);
+        out.writeInt(this.widgetFeatures);
     }
 
     @Override
@@ -357,6 +373,7 @@
         that.resizeMode = this.resizeMode;
         that.widgetCategory = this.widgetCategory;
         that.providerInfo = this.providerInfo;
+        that.widgetFeatures = this.widgetFeatures;
         return that;
     }
 
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 578a5b8..c7be0f3 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1,6 +1,6 @@
 /*
- * Copyright (C) 2009-2016 The Android Open Source Project
- * Copyright (C) 2015 Samsung LSI
+ * Copyright 2009-2016 The Android Open Source Project
+ * Copyright 2015 Samsung LSI
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -132,9 +132,8 @@
      * respectively.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_STATE_CHANGED =
-            "android.bluetooth.adapter.action.STATE_CHANGED";
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
+            ACTION_STATE_CHANGED = "android.bluetooth.adapter.action.STATE_CHANGED";
 
     /**
      * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
@@ -144,8 +143,7 @@
      * {@link #STATE_ON},
      * {@link #STATE_TURNING_OFF},
      */
-    public static final String EXTRA_STATE =
-            "android.bluetooth.adapter.extra.STATE";
+    public static final String EXTRA_STATE = "android.bluetooth.adapter.extra.STATE";
     /**
      * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
      * intents to request the previous power state. Possible values are:
@@ -158,11 +156,17 @@
             "android.bluetooth.adapter.extra.PREVIOUS_STATE";
 
     /** @hide */
-    @IntDef({STATE_OFF, STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, STATE_BLE_TURNING_ON,
-            STATE_BLE_ON, STATE_BLE_TURNING_OFF})
+    @IntDef({
+            STATE_OFF,
+            STATE_TURNING_ON,
+            STATE_ON,
+            STATE_TURNING_OFF,
+            STATE_BLE_TURNING_ON,
+            STATE_BLE_ON,
+            STATE_BLE_TURNING_OFF
+    })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface AdapterState {
-    }
+    public @interface AdapterState {}
 
     /**
      * Indicates the local Bluetooth adapter is off.
@@ -254,9 +258,8 @@
      * application can be notified when the device has ended discoverability.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
      */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_REQUEST_DISCOVERABLE =
-            "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
+            ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
 
     /**
      * Used as an optional int extra field in {@link
@@ -282,9 +285,8 @@
      * for global notification whenever Bluetooth is turned on or off.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
      */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_REQUEST_ENABLE =
-            "android.bluetooth.adapter.action.REQUEST_ENABLE";
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
+            ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE";
 
     /**
      * Activity Action: Show a system activity that allows the user to turn off
@@ -305,9 +307,8 @@
      *
      * @hide
      */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_REQUEST_DISABLE =
-            "android.bluetooth.adapter.action.REQUEST_DISABLE";
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String
+            ACTION_REQUEST_DISABLE = "android.bluetooth.adapter.action.REQUEST_DISABLE";
 
     /**
      * Activity Action: Show a system activity that allows user to enable BLE scans even when
@@ -334,9 +335,8 @@
      * respectively.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
      */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_SCAN_MODE_CHANGED =
-            "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
+            ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
 
     /**
      * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
@@ -359,8 +359,7 @@
     /** @hide */
     @IntDef({SCAN_MODE_NONE, SCAN_MODE_CONNECTABLE, SCAN_MODE_CONNECTABLE_DISCOVERABLE})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface ScanMode {
-    }
+    public @interface ScanMode {}
 
     /**
      * Indicates that both inquiry scan and page scan are disabled on the local
@@ -396,17 +395,15 @@
      * discovery.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_DISCOVERY_STARTED =
-            "android.bluetooth.adapter.action.DISCOVERY_STARTED";
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
+            ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED";
     /**
      * Broadcast Action: The local Bluetooth adapter has finished the device
      * discovery process.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_DISCOVERY_FINISHED =
-            "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
+            ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
 
     /**
      * Broadcast Action: The local Bluetooth adapter has changed its friendly
@@ -416,9 +413,8 @@
      * the name.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_LOCAL_NAME_CHANGED =
-            "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
+            ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
     /**
      * Used as a String extra field in {@link #ACTION_LOCAL_NAME_CHANGED}
      * intents to request the local Bluetooth name.
@@ -451,8 +447,8 @@
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_CONNECTION_STATE_CHANGED =
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String
+            ACTION_CONNECTION_STATE_CHANGED =
             "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
 
     /**
@@ -476,8 +472,7 @@
      *
      * @hide
      */
-    @SystemApi
-    public static final String ACTION_BLE_STATE_CHANGED =
+    @SystemApi public static final String ACTION_BLE_STATE_CHANGED =
             "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
 
     /**
@@ -574,8 +569,7 @@
 
     private final IBluetoothManager mManagerService;
     private IBluetooth mService;
-    private final ReentrantReadWriteLock mServiceLock =
-            new ReentrantReadWriteLock();
+    private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
 
     private final Object mLock = new Object();
     private final Map<LeScanCallback, ScanCallback> mLeScanClients;
@@ -655,8 +649,9 @@
         if (address == null || address.length != 6) {
             throw new IllegalArgumentException("Bluetooth address must have 6 bytes");
         }
-        return new BluetoothDevice(String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
-                address[0], address[1], address[2], address[3], address[4], address[5]));
+        return new BluetoothDevice(
+                String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1],
+                        address[2], address[3], address[4], address[5]));
     }
 
     /**
@@ -668,7 +663,9 @@
      * on this device before calling this method.
      */
     public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
-        if (!getLeAccess()) return null;
+        if (!getLeAccess()) {
+            return null;
+        }
         synchronized (mLock) {
             if (sBluetoothLeAdvertiser == null) {
                 sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService);
@@ -698,8 +695,7 @@
 
         synchronized (mLock) {
             if (sPeriodicAdvertisingManager == null) {
-                sPeriodicAdvertisingManager =
-                        new PeriodicAdvertisingManager(mManagerService);
+                sPeriodicAdvertisingManager = new PeriodicAdvertisingManager(mManagerService);
             }
         }
         return sPeriodicAdvertisingManager;
@@ -709,7 +705,9 @@
      * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations.
      */
     public BluetoothLeScanner getBluetoothLeScanner() {
-        if (!getLeAccess()) return null;
+        if (!getLeAccess()) {
+            return null;
+        }
         synchronized (mLock) {
             if (sBluetoothLeScanner == null) {
                 sBluetoothLeScanner = new BluetoothLeScanner(mManagerService);
@@ -729,7 +727,9 @@
     public boolean isEnabled() {
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isEnabled();
+            if (mService != null) {
+                return mService.isEnabled();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -750,7 +750,9 @@
     @SystemApi
     public boolean isLeEnabled() {
         final int state = getLeState();
-        if (DBG) Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state));
+        if (DBG) {
+            Log.d(TAG, "isLeEnabled(): " + BluetoothAdapter.nameForState(state));
+        }
         return (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON);
     }
 
@@ -781,12 +783,16 @@
      */
     @SystemApi
     public boolean disableBLE() {
-        if (!isBleScanAlwaysAvailable()) return false;
+        if (!isBleScanAlwaysAvailable()) {
+            return false;
+        }
 
         int state = getLeState();
         if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON) {
             String packageName = ActivityThread.currentPackageName();
-            if (DBG) Log.d(TAG, "disableBLE(): de-registering " + packageName);
+            if (DBG) {
+                Log.d(TAG, "disableBLE(): de-registering " + packageName);
+            }
             try {
                 mManagerService.updateBleAppCount(mToken, false, packageName);
             } catch (RemoteException e) {
@@ -795,7 +801,9 @@
             return true;
         }
 
-        if (DBG) Log.d(TAG, "disableBLE(): Already disabled");
+        if (DBG) {
+            Log.d(TAG, "disableBLE(): Already disabled");
+        }
         return false;
     }
 
@@ -832,16 +840,22 @@
      */
     @SystemApi
     public boolean enableBLE() {
-        if (!isBleScanAlwaysAvailable()) return false;
+        if (!isBleScanAlwaysAvailable()) {
+            return false;
+        }
 
         try {
             String packageName = ActivityThread.currentPackageName();
             mManagerService.updateBleAppCount(mToken, true, packageName);
             if (isLeEnabled()) {
-                if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled");
+                if (DBG) {
+                    Log.d(TAG, "enableBLE(): Bluetooth already enabled");
+                }
                 return true;
             }
-            if (DBG) Log.d(TAG, "enableBLE(): Calling enable");
+            if (DBG) {
+                Log.d(TAG, "enableBLE(): Calling enable");
+            }
             return mManagerService.enable(packageName);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
@@ -877,19 +891,16 @@
         }
 
         // Consider all internal states as OFF
-        if (state == BluetoothAdapter.STATE_BLE_ON
-                || state == BluetoothAdapter.STATE_BLE_TURNING_ON
+        if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON
                 || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
             if (VDBG) {
-                Log.d(TAG,
-                        "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF");
+                Log.d(TAG, "Consider " + BluetoothAdapter.nameForState(state) + " state as OFF");
             }
             state = BluetoothAdapter.STATE_OFF;
         }
         if (VDBG) {
-            Log.d(TAG,
-                    "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState(
-                            state));
+            Log.d(TAG, "" + hashCode() + ": getState(). Returning " + BluetoothAdapter.nameForState(
+                    state));
         }
         return state;
     }
@@ -926,7 +937,9 @@
             mServiceLock.readLock().unlock();
         }
 
-        if (VDBG) Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state));
+        if (VDBG) {
+            Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state));
+        }
         return state;
     }
 
@@ -967,7 +980,9 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean enable() {
         if (isEnabled()) {
-            if (DBG) Log.d(TAG, "enable(): BT already enabled!");
+            if (DBG) {
+                Log.d(TAG, "enable(): BT already enabled!");
+            }
             return true;
         }
         try {
@@ -1093,10 +1108,14 @@
      * @hide
      */
     public ParcelUuid[] getUuids() {
-        if (getState() != STATE_ON) return null;
+        if (getState() != STATE_ON) {
+            return null;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.getUuids();
+            if (mService != null) {
+                return mService.getUuids();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1121,10 +1140,14 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean setName(String name) {
-        if (getState() != STATE_ON) return false;
+        if (getState() != STATE_ON) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.setName(name);
+            if (mService != null) {
+                return mService.setName(name);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1143,10 +1166,14 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public BluetoothClass getBluetoothClass() {
-        if (getState() != STATE_ON) return null;
+        if (getState() != STATE_ON) {
+            return null;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.getBluetoothClass();
+            if (mService != null) {
+                return mService.getBluetoothClass();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1168,10 +1195,14 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setBluetoothClass(BluetoothClass bluetoothClass) {
-        if (getState() != STATE_ON) return false;
+        if (getState() != STATE_ON) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.setBluetoothClass(bluetoothClass);
+            if (mService != null) {
+                return mService.setBluetoothClass(bluetoothClass);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1198,10 +1229,14 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     @ScanMode
     public int getScanMode() {
-        if (getState() != STATE_ON) return SCAN_MODE_NONE;
+        if (getState() != STATE_ON) {
+            return SCAN_MODE_NONE;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.getScanMode();
+            if (mService != null) {
+                return mService.getScanMode();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1239,10 +1274,14 @@
      * @hide
      */
     public boolean setScanMode(@ScanMode int mode, int duration) {
-        if (getState() != STATE_ON) return false;
+        if (getState() != STATE_ON) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.setScanMode(mode, duration);
+            if (mService != null) {
+                return mService.setScanMode(mode, duration);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1253,17 +1292,23 @@
 
     /** @hide */
     public boolean setScanMode(int mode) {
-        if (getState() != STATE_ON) return false;
+        if (getState() != STATE_ON) {
+            return false;
+        }
         /* getDiscoverableTimeout() to use the latest from NV than use 0 */
         return setScanMode(mode, getDiscoverableTimeout());
     }
 
     /** @hide */
     public int getDiscoverableTimeout() {
-        if (getState() != STATE_ON) return -1;
+        if (getState() != STATE_ON) {
+            return -1;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.getDiscoverableTimeout();
+            if (mService != null) {
+                return mService.getDiscoverableTimeout();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1274,10 +1319,14 @@
 
     /** @hide */
     public void setDiscoverableTimeout(int timeout) {
-        if (getState() != STATE_ON) return;
+        if (getState() != STATE_ON) {
+            return;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) mService.setDiscoverableTimeout(timeout);
+            if (mService != null) {
+                mService.setDiscoverableTimeout(timeout);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1296,7 +1345,9 @@
     public long getDiscoveryEndMillis() {
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.getDiscoveryEndMillis();
+            if (mService != null) {
+                return mService.getDiscoveryEndMillis();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1336,10 +1387,14 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean startDiscovery() {
-        if (getState() != STATE_ON) return false;
+        if (getState() != STATE_ON) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.startDiscovery();
+            if (mService != null) {
+                return mService.startDiscovery();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1366,10 +1421,14 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean cancelDiscovery() {
-        if (getState() != STATE_ON) return false;
+        if (getState() != STATE_ON) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.cancelDiscovery();
+            if (mService != null) {
+                return mService.cancelDiscovery();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1398,10 +1457,14 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     public boolean isDiscovering() {
-        if (getState() != STATE_ON) return false;
+        if (getState() != STATE_ON) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isDiscovering();
+            if (mService != null) {
+                return mService.isDiscovering();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         } finally {
@@ -1416,10 +1479,14 @@
      * @return true if Multiple Advertisement feature is supported
      */
     public boolean isMultipleAdvertisementSupported() {
-        if (getState() != STATE_ON) return false;
+        if (getState() != STATE_ON) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isMultiAdvertisementSupported();
+            if (mService != null) {
+                return mService.isMultiAdvertisementSupported();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isMultipleAdvertisementSupported, error: ", e);
         } finally {
@@ -1454,10 +1521,14 @@
      * @return true if chipset supports on-chip filtering
      */
     public boolean isOffloadedFilteringSupported() {
-        if (!getLeAccess()) return false;
+        if (!getLeAccess()) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isOffloadedFilteringSupported();
+            if (mService != null) {
+                return mService.isOffloadedFilteringSupported();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e);
         } finally {
@@ -1472,10 +1543,14 @@
      * @return true if chipset supports on-chip scan batching
      */
     public boolean isOffloadedScanBatchingSupported() {
-        if (!getLeAccess()) return false;
+        if (!getLeAccess()) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isOffloadedScanBatchingSupported();
+            if (mService != null) {
+                return mService.isOffloadedScanBatchingSupported();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isOffloadedScanBatchingSupported, error: ", e);
         } finally {
@@ -1490,10 +1565,14 @@
      * @return true if chipset supports LE 2M PHY feature
      */
     public boolean isLe2MPhySupported() {
-        if (!getLeAccess()) return false;
+        if (!getLeAccess()) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isLe2MPhySupported();
+            if (mService != null) {
+                return mService.isLe2MPhySupported();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isExtendedAdvertisingSupported, error: ", e);
         } finally {
@@ -1508,10 +1587,14 @@
      * @return true if chipset supports LE Coded PHY feature
      */
     public boolean isLeCodedPhySupported() {
-        if (!getLeAccess()) return false;
+        if (!getLeAccess()) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isLeCodedPhySupported();
+            if (mService != null) {
+                return mService.isLeCodedPhySupported();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isLeCodedPhySupported, error: ", e);
         } finally {
@@ -1526,10 +1609,14 @@
      * @return true if chipset supports LE Extended Advertising feature
      */
     public boolean isLeExtendedAdvertisingSupported() {
-        if (!getLeAccess()) return false;
+        if (!getLeAccess()) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isLeExtendedAdvertisingSupported();
+            if (mService != null) {
+                return mService.isLeExtendedAdvertisingSupported();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isLeExtendedAdvertisingSupported, error: ", e);
         } finally {
@@ -1544,10 +1631,14 @@
      * @return true if chipset supports LE Periodic Advertising feature
      */
     public boolean isLePeriodicAdvertisingSupported() {
-        if (!getLeAccess()) return false;
+        if (!getLeAccess()) {
+            return false;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.isLePeriodicAdvertisingSupported();
+            if (mService != null) {
+                return mService.isLePeriodicAdvertisingSupported();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get isLePeriodicAdvertisingSupported, error: ", e);
         } finally {
@@ -1563,10 +1654,14 @@
      * @return the maximum LE advertising data length.
      */
     public int getLeMaximumAdvertisingDataLength() {
-        if (!getLeAccess()) return 0;
+        if (!getLeAccess()) {
+            return 0;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.getLeMaximumAdvertisingDataLength();
+            if (mService != null) {
+                return mService.getLeMaximumAdvertisingDataLength();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get getLeMaximumAdvertisingDataLength, error: ", e);
         } finally {
@@ -1582,7 +1677,9 @@
      * @hide
      */
     public boolean isHardwareTrackingFiltersAvailable() {
-        if (!getLeAccess()) return false;
+        if (!getLeAccess()) {
+            return false;
+        }
         try {
             IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
             if (iGatt == null) {
@@ -1669,7 +1766,9 @@
         }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return toDeviceSet(mService.getBondedDevices());
+            if (mService != null) {
+                return toDeviceSet(mService.getBondedDevices());
+            }
             return toDeviceSet(new BluetoothDevice[0]);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
@@ -1723,10 +1822,14 @@
      * @hide
      */
     public int getConnectionState() {
-        if (getState() != STATE_ON) return BluetoothAdapter.STATE_DISCONNECTED;
+        if (getState() != STATE_ON) {
+            return BluetoothAdapter.STATE_DISCONNECTED;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.getAdapterConnectionState();
+            if (mService != null) {
+                return mService.getAdapterConnectionState();
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "getConnectionState:", e);
         } finally {
@@ -1750,10 +1853,14 @@
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     public int getProfileConnectionState(int profile) {
-        if (getState() != STATE_ON) return BluetoothProfile.STATE_DISCONNECTED;
+        if (getState() != STATE_ON) {
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
         try {
             mServiceLock.readLock().lock();
-            if (mService != null) return mService.getProfileConnectionState(profile);
+            if (mService != null) {
+                return mService.getProfileConnectionState(profile);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "getProfileConnectionState:", e);
         } finally {
@@ -1790,7 +1897,7 @@
      * <p>Valid RFCOMM channels are in range 1 to 30.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
      * <p>To auto assign a channel without creating a SDP record use
-     * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number.
+     * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as channel number.
      *
      * @param channel RFCOMM channel to listen on
      * @param mitm enforce man-in-the-middle protection for authentication.
@@ -1802,10 +1909,10 @@
      * @hide
      */
     public BluetoothServerSocket listenUsingRfcommOn(int channel, boolean mitm,
-            boolean min16DigitPin)
-            throws IOException {
-        BluetoothServerSocket socket = new BluetoothServerSocket(
-                BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm, min16DigitPin);
+            boolean min16DigitPin) throws IOException {
+        BluetoothServerSocket socket =
+                new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, true, true, channel, mitm,
+                        min16DigitPin);
         int errno = socket.mSocket.bindListen();
         if (channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
             socket.setChannel(socket.mSocket.getPort());
@@ -1913,8 +2020,8 @@
      * permissions, or channel in use.
      * @hide
      */
-    public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(
-            String name, UUID uuid) throws IOException {
+    public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(String name, UUID uuid)
+            throws IOException {
         return createNewRfcommSocketAndRecord(name, uuid, false, true);
     }
 
@@ -1922,8 +2029,8 @@
     private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid,
             boolean auth, boolean encrypt) throws IOException {
         BluetoothServerSocket socket;
-        socket = new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, auth,
-                encrypt, new ParcelUuid(uuid));
+        socket = new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, auth, encrypt,
+                new ParcelUuid(uuid));
         socket.setServiceName(name);
         int errno = socket.mSocket.bindListen();
         if (errno != 0) {
@@ -1945,8 +2052,8 @@
      * @hide
      */
     public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
-        BluetoothServerSocket socket = new BluetoothServerSocket(
-                BluetoothSocket.TYPE_RFCOMM, false, false, port);
+        BluetoothServerSocket socket =
+                new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, false, port);
         int errno = socket.mSocket.bindListen();
         if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
             socket.setChannel(socket.mSocket.getPort());
@@ -1969,10 +2076,9 @@
      * permissions.
      * @hide
      */
-    public BluetoothServerSocket listenUsingEncryptedRfcommOn(int port)
-            throws IOException {
-        BluetoothServerSocket socket = new BluetoothServerSocket(
-                BluetoothSocket.TYPE_RFCOMM, false, true, port);
+    public BluetoothServerSocket listenUsingEncryptedRfcommOn(int port) throws IOException {
+        BluetoothServerSocket socket =
+                new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, true, port);
         int errno = socket.mSocket.bindListen();
         if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
             socket.setChannel(socket.mSocket.getPort());
@@ -1996,8 +2102,8 @@
      * @hide
      */
     public static BluetoothServerSocket listenUsingScoOn() throws IOException {
-        BluetoothServerSocket socket = new BluetoothServerSocket(
-                BluetoothSocket.TYPE_SCO, false, false, -1);
+        BluetoothServerSocket socket =
+                new BluetoothServerSocket(BluetoothSocket.TYPE_SCO, false, false, -1);
         int errno = socket.mSocket.bindListen();
         if (errno < 0) {
             //TODO(BT): Throw the same exception error code
@@ -2011,7 +2117,7 @@
      * Construct an encrypted, authenticated, L2CAP server socket.
      * Call #accept to retrieve connections to this socket.
      * <p>To auto assign a port without creating a SDP record use
-     * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
+     * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
      *
      * @param port the PSM to listen on
      * @param mitm enforce man-in-the-middle protection for authentication.
@@ -2024,8 +2130,9 @@
      */
     public BluetoothServerSocket listenUsingL2capOn(int port, boolean mitm, boolean min16DigitPin)
             throws IOException {
-        BluetoothServerSocket socket = new BluetoothServerSocket(
-                BluetoothSocket.TYPE_L2CAP, true, true, port, mitm, min16DigitPin);
+        BluetoothServerSocket socket =
+                new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, true, true, port, mitm,
+                        min16DigitPin);
         int errno = socket.mSocket.bindListen();
         if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
             socket.setChannel(socket.mSocket.getPort());
@@ -2043,7 +2150,7 @@
      * Construct an encrypted, authenticated, L2CAP server socket.
      * Call #accept to retrieve connections to this socket.
      * <p>To auto assign a port without creating a SDP record use
-     * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
+     * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
      *
      * @param port the PSM to listen on
      * @return An L2CAP BluetoothServerSocket
@@ -2060,7 +2167,7 @@
      * Construct an insecure L2CAP server socket.
      * Call #accept to retrieve connections to this socket.
      * <p>To auto assign a port without creating a SDP record use
-     * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
+     * {@link #SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
      *
      * @param port the PSM to listen on
      * @return An L2CAP BluetoothServerSocket
@@ -2069,8 +2176,9 @@
      * @hide
      */
     public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException {
-        BluetoothServerSocket socket = new BluetoothServerSocket(
-                BluetoothSocket.TYPE_L2CAP, false, false, port, false, false);
+        BluetoothServerSocket socket =
+                new BluetoothServerSocket(BluetoothSocket.TYPE_L2CAP, false, false, port, false,
+                        false);
         int errno = socket.mSocket.bindListen();
         if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
             socket.setChannel(socket.mSocket.getPort());
@@ -2114,7 +2222,9 @@
      */
     public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
             int profile) {
-        if (context == null || listener == null) return false;
+        if (context == null || listener == null) {
+            return false;
+        }
 
         if (profile == BluetoothProfile.HEADSET) {
             BluetoothHeadset headset = new BluetoothHeadset(context, listener);
@@ -2172,7 +2282,9 @@
      * @param proxy Profile proxy object
      */
     public void closeProfileProxy(int profile, BluetoothProfile proxy) {
-        if (proxy == null) return;
+        if (proxy == null) {
+            return;
+        }
 
         switch (profile) {
             case BluetoothProfile.HEADSET:
@@ -2241,7 +2353,9 @@
     private final IBluetoothManagerCallback mManagerCallback =
             new IBluetoothManagerCallback.Stub() {
                 public void onBluetoothServiceUp(IBluetooth bluetoothService) {
-                    if (DBG) Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService);
+                    if (DBG) {
+                        Log.d(TAG, "onBluetoothServiceUp: " + bluetoothService);
+                    }
 
                     mServiceLock.writeLock().lock();
                     mService = bluetoothService;
@@ -2263,14 +2377,22 @@
                 }
 
                 public void onBluetoothServiceDown() {
-                    if (DBG) Log.d(TAG, "onBluetoothServiceDown: " + mService);
+                    if (DBG) {
+                        Log.d(TAG, "onBluetoothServiceDown: " + mService);
+                    }
 
                     try {
                         mServiceLock.writeLock().lock();
                         mService = null;
-                        if (mLeScanClients != null) mLeScanClients.clear();
-                        if (sBluetoothLeAdvertiser != null) sBluetoothLeAdvertiser.cleanup();
-                        if (sBluetoothLeScanner != null) sBluetoothLeScanner.cleanup();
+                        if (mLeScanClients != null) {
+                            mLeScanClients.clear();
+                        }
+                        if (sBluetoothLeAdvertiser != null) {
+                            sBluetoothLeAdvertiser.cleanup();
+                        }
+                        if (sBluetoothLeScanner != null) {
+                            sBluetoothLeScanner.cleanup();
+                        }
                     } finally {
                         mServiceLock.writeLock().unlock();
                     }
@@ -2291,7 +2413,9 @@
                 }
 
                 public void onBrEdrDown() {
-                    if (VDBG) Log.i(TAG, "onBrEdrDown: " + mService);
+                    if (VDBG) {
+                        Log.i(TAG, "onBrEdrDown: " + mService);
+                    }
                 }
             };
 
@@ -2305,7 +2429,9 @@
     @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN)
     public boolean enableNoAutoConnect() {
         if (isEnabled()) {
-            if (DBG) Log.d(TAG, "enableNoAutoConnect(): BT already enabled!");
+            if (DBG) {
+                Log.d(TAG, "enableNoAutoConnect(): BT already enabled!");
+            }
             return true;
         }
         try {
@@ -2354,7 +2480,10 @@
      * @hide
      */
     public interface BluetoothStateChangeCallback {
-        public void onBluetoothStateChange(boolean on);
+        /**
+         * @hide
+         */
+        void onBluetoothStateChange(boolean on);
     }
 
     /**
@@ -2363,8 +2492,7 @@
     public class StateChangeCallbackWrapper extends IBluetoothStateChangeCallback.Stub {
         private BluetoothStateChangeCallback mCallback;
 
-        StateChangeCallbackWrapper(BluetoothStateChangeCallback
-                callback) {
+        StateChangeCallbackWrapper(BluetoothStateChangeCallback callback) {
             mCallback = callback;
         }
 
@@ -2461,7 +2589,7 @@
          * if no RSSI value is available.
          * @param scanRecord The content of the advertisement record offered by the remote device.
          */
-        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord);
+        void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord);
     }
 
     /**
@@ -2497,20 +2625,28 @@
     @Deprecated
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) {
-        if (DBG) Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids));
+        if (DBG) {
+            Log.d(TAG, "startLeScan(): " + Arrays.toString(serviceUuids));
+        }
         if (callback == null) {
-            if (DBG) Log.e(TAG, "startLeScan: null callback");
+            if (DBG) {
+                Log.e(TAG, "startLeScan: null callback");
+            }
             return false;
         }
         BluetoothLeScanner scanner = getBluetoothLeScanner();
         if (scanner == null) {
-            if (DBG) Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner");
+            if (DBG) {
+                Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner");
+            }
             return false;
         }
 
         synchronized (mLeScanClients) {
             if (mLeScanClients.containsKey(callback)) {
-                if (DBG) Log.e(TAG, "LE Scan has already started");
+                if (DBG) {
+                    Log.e(TAG, "LE Scan has already started");
+                }
                 return false;
             }
 
@@ -2540,7 +2676,9 @@
                             }
                             List<ParcelUuid> scanServiceUuids = scanRecord.getServiceUuids();
                             if (scanServiceUuids == null || !scanServiceUuids.containsAll(uuids)) {
-                                if (DBG) Log.d(TAG, "uuids does not match");
+                                if (DBG) {
+                                    Log.d(TAG, "uuids does not match");
+                                }
                                 return;
                             }
                         }
@@ -2548,16 +2686,18 @@
                                 scanRecord.getBytes());
                     }
                 };
-                ScanSettings settings = new ScanSettings.Builder()
-                        .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
-                        .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
+                ScanSettings settings = new ScanSettings.Builder().setCallbackType(
+                        ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
+                        .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+                        .build();
 
                 List<ScanFilter> filters = new ArrayList<ScanFilter>();
                 if (serviceUuids != null && serviceUuids.length > 0) {
                     // Note scan filter does not support matching an UUID array so we put one
                     // UUID to hardware and match the whole array in callback.
-                    ScanFilter filter = new ScanFilter.Builder().setServiceUuid(
-                            new ParcelUuid(serviceUuids[0])).build();
+                    ScanFilter filter =
+                            new ScanFilter.Builder().setServiceUuid(new ParcelUuid(serviceUuids[0]))
+                                    .build();
                     filters.add(filter);
                 }
                 scanner.startScan(filters, settings, scanCallback);
@@ -2582,7 +2722,9 @@
     @Deprecated
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
     public void stopLeScan(LeScanCallback callback) {
-        if (DBG) Log.d(TAG, "stopLeScan()");
+        if (DBG) {
+            Log.d(TAG, "stopLeScan()");
+        }
         BluetoothLeScanner scanner = getBluetoothLeScanner();
         if (scanner == null) {
             return;
@@ -2590,7 +2732,9 @@
         synchronized (mLeScanClients) {
             ScanCallback scanCallback = mLeScanClients.remove(callback);
             if (scanCallback == null) {
-                if (DBG) Log.d(TAG, "scan not started yet");
+                if (DBG) {
+                    Log.d(TAG, "scan not started yet");
+                }
                 return;
             }
             scanner.stopScan(scanCallback);
diff --git a/core/java/android/bluetooth/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/Context.java b/core/java/android/content/Context.java
index 71998ee..9800ab0 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2801,10 +2801,17 @@
      * example, if this Context is an Activity that is stopped, the service will
      * not be required to continue running until the Activity is resumed.
      *
-     * <p>This function will throw {@link SecurityException} if you do not
+     * <p>If the service does not support binding, it may return {@code null} from
+     * its {@link android.app.Service#onBind(Intent) onBind()} method.  If it does, then
+     * the ServiceConnection's
+     * {@link ServiceConnection#onNullBinding(ComponentName) onNullBinding()} method
+     * will be invoked instead of
+     * {@link ServiceConnection#onServiceConnected(ComponentName, IBinder) onServiceConnected()}.
+     *
+     * <p>This method will throw {@link SecurityException} if the calling app does not
      * have permission to bind to the given service.
      *
-     * <p class="note">Note: this method <em>can not be called from a
+     * <p class="note">Note: this method <em>cannot be called from a
      * {@link BroadcastReceiver} component</em>.  A pattern you can use to
      * communicate from a BroadcastReceiver to a Service is to call
      * {@link #startService} with the arguments containing the command to be
@@ -2827,8 +2834,8 @@
      *          {@link #BIND_WAIVE_PRIORITY}.
      * @return If you have successfully bound to the service, {@code true} is returned;
      *         {@code false} is returned if the connection is not made so you will not
-     *         receive the service object. However, you should still call
-     *         {@link #unbindService} to release the connection.
+     *         receive the service object. You should still call {@link #unbindService}
+     *         to release the connection even if this method returned {@code false}.
      *
      * @throws SecurityException If the caller does not have permission to access the service
      * or the service can not be found.
@@ -3406,6 +3413,14 @@
     public static final String NETWORKMANAGEMENT_SERVICE = "network_management";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a
+     * {@link com.android.server.slice.SliceManagerService} for managing slices.
+     * @hide
+     * @see #getSystemService
+     */
+    public static final String SLICE_SERVICE = "slice";
+
+    /**
      * Use with {@link #getSystemService} to retrieve a {@link
      * android.app.usage.NetworkStatsManager} for querying network usage stats.
      *
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 55ad5c5..28bd928 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4461,6 +4461,16 @@
     public static final String EXTRA_INSTANT_APP_ACTION = "android.intent.extra.INSTANT_APP_ACTION";
 
     /**
+     * A {@link Bundle} of metadata that describes the instanta application that needs to be
+     * installed. This data is populated from the response to
+     * {@link android.content.pm.InstantAppResolveInfo#getExtras()} as provided by the registered
+     * instant application resolver.
+     * @hide
+     */
+    public static final String EXTRA_INSTANT_APP_EXTRAS =
+            "android.intent.extra.INSTANT_APP_EXTRAS";
+
+    /**
      * The version code of the app to install components from.
      * @deprecated Use {@link #EXTRA_LONG_VERSION_CODE).
      * @hide
diff --git a/core/java/android/content/ServiceConnection.java b/core/java/android/content/ServiceConnection.java
index 6ff4900..c16dbbe 100644
--- a/core/java/android/content/ServiceConnection.java
+++ b/core/java/android/content/ServiceConnection.java
@@ -63,4 +63,21 @@
      */
     default void onBindingDied(ComponentName name) {
     }
+
+    /**
+     * Called when the service being bound has returned {@code null} from its
+     * {@link android.app.Service#onBind(Intent) onBind()} method.  This indicates
+     * that the attempting service binding represented by this ServiceConnection
+     * will never become usable.
+     *
+     * <p class="note">The app which requested the binding must still call
+     * {@link Context#unbindService(ServiceConnection)} to release the tracking
+     * resources associated with this ServiceConnection even if this callback was
+     * invoked following {@link Context#bindService Context.bindService() bindService()}.
+     *
+     * @param name The concrete component name of the service whose binding
+     *     has been rejected by the Service implementation.
+     */
+    default void onNullBinding(ComponentName name) {
+    }
 }
diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java
index fb3094c..19cb932 100644
--- a/core/java/android/content/pm/InstantAppResolveInfo.java
+++ b/core/java/android/content/pm/InstantAppResolveInfo.java
@@ -19,8 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.content.IntentFilter;
-import android.net.Uri;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -45,14 +44,17 @@
     private final List<InstantAppIntentFilter> mFilters;
     /** The version code of the app that this class resolves to */
     private final long mVersionCode;
+    /** Data about the app that should be passed along to the Instant App installer on resolve */
+    private final Bundle mExtras;
 
     public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
             @Nullable List<InstantAppIntentFilter> filters, int versionCode) {
-        this(digest, packageName, filters, (long)versionCode);
+        this(digest, packageName, filters, (long) versionCode, null /* extras */);
     }
 
     public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
-            @Nullable List<InstantAppIntentFilter> filters, long versionCode) {
+            @Nullable List<InstantAppIntentFilter> filters, long versionCode,
+            @Nullable Bundle extras) {
         // validate arguments
         if ((packageName == null && (filters != null && filters.size() != 0))
                 || (packageName != null && (filters == null || filters.size() == 0))) {
@@ -67,11 +69,13 @@
         }
         mPackageName = packageName;
         mVersionCode = versionCode;
+        mExtras = extras;
     }
 
     public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName,
             @Nullable List<InstantAppIntentFilter> filters) {
-        this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/);
+        this(new InstantAppDigest(hostName), packageName, filters, -1 /*versionCode*/,
+                null /* extras */);
     }
 
     InstantAppResolveInfo(Parcel in) {
@@ -80,6 +84,7 @@
         mFilters = new ArrayList<InstantAppIntentFilter>();
         in.readList(mFilters, null /*loader*/);
         mVersionCode = in.readLong();
+        mExtras = in.readBundle();
     }
 
     public byte[] getDigestBytes() {
@@ -110,6 +115,11 @@
         return mVersionCode;
     }
 
+    @Nullable
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -121,6 +131,7 @@
         out.writeString(mPackageName);
         out.writeList(mFilters);
         out.writeLong(mVersionCode);
+        out.writeBundle(mExtras);
     }
 
     public static final Parcelable.Creator<InstantAppResolveInfo> CREATOR
diff --git a/core/java/android/content/pm/crossprofile/CrossProfileApps.java b/core/java/android/content/pm/crossprofile/CrossProfileApps.java
index c441b5f..c9f184a 100644
--- a/core/java/android/content/pm/crossprofile/CrossProfileApps.java
+++ b/core/java/android/content/pm/crossprofile/CrossProfileApps.java
@@ -19,12 +19,17 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 
+import com.android.internal.R;
+import com.android.internal.util.UserIcons;
+
 import java.util.List;
 
 /**
@@ -35,11 +40,15 @@
 public class CrossProfileApps {
     private final Context mContext;
     private final ICrossProfileApps mService;
+    private final UserManager mUserManager;
+    private final Resources mResources;
 
     /** @hide */
     public CrossProfileApps(Context context, ICrossProfileApps service) {
         mContext = context;
         mService = service;
+        mUserManager = context.getSystemService(UserManager.class);
+        mResources = context.getResources();
     }
 
     /**
@@ -87,4 +96,58 @@
             throw ex.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Return a label that calling app can show to user for the semantic of profile switching --
+     * launching its own activity in specified user profile. For example, it may return
+     * "Switch to work" if the given user handle is the managed profile one.
+     *
+     * @param userHandle The UserHandle of the target profile, must be one of the users returned by
+     *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+     *        be thrown.
+     * @return a label that calling app can show user for the semantic of launching its own
+     *         activity in the specified user profile.
+     *
+     * @see #startMainActivity(ComponentName, UserHandle, Rect, Bundle)
+     */
+    public @NonNull CharSequence getProfileSwitchingLabel(@NonNull UserHandle userHandle) {
+        verifyCanAccessUser(userHandle);
+
+        final int stringRes = mUserManager.isManagedProfile(userHandle.getIdentifier())
+                ? R.string.managed_profile_label
+                : R.string.user_owner_label;
+        return mResources.getString(stringRes);
+    }
+
+    /**
+     * Return an icon that calling app can show to user for the semantic of profile switching --
+     * launching its own activity in specified user profile. For example, it may return a briefcase
+     * icon if the given user handle is the managed profile one.
+     *
+     * @param userHandle The UserHandle of the target profile, must be one of the users returned by
+     *        {@link #getTargetUserProfiles()}, otherwise a {@link SecurityException} will
+     *        be thrown.
+     * @return an icon that calling app can show user for the semantic of launching its own
+     *         activity in specified user profile.
+     *
+     * @see #startMainActivity(ComponentName, UserHandle, Rect, Bundle)
+     */
+    public @NonNull Drawable getProfileSwitchingIcon(@NonNull UserHandle userHandle) {
+        verifyCanAccessUser(userHandle);
+
+        final boolean isManagedProfile =
+                mUserManager.isManagedProfile(userHandle.getIdentifier());
+        if (isManagedProfile) {
+            return mResources.getDrawable(R.drawable.ic_corp_badge, null);
+        } else {
+            return UserIcons.getDefaultUserIcon(
+                    mResources, UserHandle.USER_SYSTEM, true /* light */);
+        }
+    }
+
+    private void verifyCanAccessUser(UserHandle userHandle) {
+        if (!getTargetUserProfiles().contains(userHandle)) {
+            throw new SecurityException("Not allowed to access " + userHandle);
+        }
+    }
 }
diff --git a/core/java/android/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/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
index fe24e32..3003607 100644
--- a/core/java/android/hardware/display/BrightnessChangeEvent.java
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -21,6 +21,8 @@
 
 /**
  * Data about a brightness settings change.
+ *
+ * {@see DisplayManager.getBrightnessEvents()}
  * TODO make this SystemAPI
  * @hide
  */
@@ -31,7 +33,9 @@
     /** Timestamp of the change {@see System.currentTimeMillis()} */
     public long timeStamp;
 
-    /** Package name of focused activity when brightness was changed. */
+    /** Package name of focused activity when brightness was changed.
+     *  This will be null if the caller of {@see DisplayManager.getBrightnessEvents()}
+     *  does not have access to usage stats {@see UsageStatsManager} */
     public String packageName;
 
     /** User id of of the user running when brightness was changed.
@@ -59,6 +63,20 @@
     public BrightnessChangeEvent() {
     }
 
+    /** @hide */
+    public BrightnessChangeEvent(BrightnessChangeEvent other) {
+        this.brightness = other.brightness;
+        this.timeStamp = other.timeStamp;
+        this.packageName = other.packageName;
+        this.userId = other.userId;
+        this.luxValues = other.luxValues;
+        this.luxTimestamps = other.luxTimestamps;
+        this.batteryLevel = other.batteryLevel;
+        this.nightMode = other.nightMode;
+        this.colorTemperature = other.colorTemperature;
+        this.lastBrightness = other.lastBrightness;
+    }
+
     private BrightnessChangeEvent(Parcel source) {
         brightness = source.readInt();
         timeStamp = source.readLong();
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 8935745..97ca231 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -16,8 +16,10 @@
 
 package android.hardware.display;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.app.KeyguardManager;
@@ -619,8 +621,9 @@
      * Fetch {@link BrightnessChangeEvent}s.
      * @hide until we make it a system api.
      */
+    @RequiresPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE)
     public List<BrightnessChangeEvent> getBrightnessEvents() {
-        return mGlobal.getBrightnessEvents();
+        return mGlobal.getBrightnessEvents(mContext.getOpPackageName());
     }
 
     /**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index d93d0e4..c3f82f5 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -462,9 +462,10 @@
     /**
      * Retrieves brightness change events.
      */
-    public List<BrightnessChangeEvent> getBrightnessEvents() {
+    public List<BrightnessChangeEvent> getBrightnessEvents(String callingPackage) {
         try {
-            ParceledListSlice<BrightnessChangeEvent> events = mDm.getBrightnessEvents();
+            ParceledListSlice<BrightnessChangeEvent> events =
+                    mDm.getBrightnessEvents(callingPackage);
             if (events == null) {
                 return Collections.emptyList();
             }
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index b796cf9..f2ed9e7 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -84,7 +84,7 @@
     Point getStableDisplaySize();
 
     // Requires BRIGHTNESS_SLIDER_USAGE permission.
-    ParceledListSlice getBrightnessEvents();
+    ParceledListSlice getBrightnessEvents(String callingPackage);
 
     // STOPSHIP remove when adaptive brightness code is updated to accept curves.
     // Requires BRIGHTNESS_SLIDER_USAGE permission.
diff --git a/core/java/android/hardware/location/NanoAppFilter.java b/core/java/android/hardware/location/NanoAppFilter.java
index bf35a3d..5ccf546 100644
--- a/core/java/android/hardware/location/NanoAppFilter.java
+++ b/core/java/android/hardware/location/NanoAppFilter.java
@@ -20,7 +20,6 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Log;
 
 /**
  * @hide
@@ -130,6 +129,14 @@
                 (versionsMatch(mVersionRestrictionMask, mAppVersion, info.getAppVersion()));
     }
 
+    @Override
+    public String toString() {
+        return "nanoAppId: 0x" + Long.toHexString(mAppId)
+                + ", nanoAppVersion: 0x" + Integer.toHexString(mAppVersion)
+                + ", versionMask: " + mVersionRestrictionMask
+                + ", vendorMask: " + mAppIdVendorMask;
+    }
+
     public static final Parcelable.Creator<NanoAppFilter> CREATOR
             = new Parcelable.Creator<NanoAppFilter>() {
         public NanoAppFilter createFromParcel(Parcel in) {
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 7836cd0..13b9206 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -21,16 +21,18 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.graphics.Paint.Align;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
-import android.graphics.Region.Op;
 import android.graphics.Typeface;
+import android.graphics.Paint.Align;
+import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
 import android.inputmethodservice.Keyboard.Key;
 import android.media.AudioManager;
 import android.os.Handler;
 import android.os.Message;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.GestureDetector;
@@ -984,9 +986,6 @@
 
     private void sendAccessibilityEventForUnicodeCharacter(int eventType, int code) {
         if (mAccessibilityManager.isEnabled()) {
-            if (!mAccessibilityManager.isObservedEventType(eventType)) {
-                return;
-            }
             AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
             onInitializeAccessibilityEvent(event);
             final String text;
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 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/Binder.java b/core/java/android/os/Binder.java
index ea527329..b7a4645 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -298,7 +298,7 @@
         long callingIdentity = clearCallingIdentity();
         Throwable throwableToPropagate = null;
         try {
-            action.run();
+            action.runOrThrow();
         } catch (Throwable throwable) {
             throwableToPropagate = throwable;
         } finally {
@@ -322,7 +322,7 @@
         long callingIdentity = clearCallingIdentity();
         Throwable throwableToPropagate = null;
         try {
-            return action.get();
+            return action.getOrThrow();
         } catch (Throwable throwable) {
             throwableToPropagate = throwable;
             return null; // overridden by throwing in finally block
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index f977c1d..b1794a6 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -24,6 +24,7 @@
 import android.util.Log;
 
 import java.io.File;
+import java.util.LinkedList;
 
 /**
  * Provides access to environment variables.
@@ -291,8 +292,9 @@
     }
 
     /** {@hide} */
-    public static File getReferenceProfile(String packageName) {
-        return buildPath(getDataDirectory(), "misc", "profiles", "ref", packageName);
+    public static File getProfileSnapshotPath(String packageName, String codePath) {
+        return buildPath(buildPath(getDataDirectory(), "misc", "profiles", "ref", packageName,
+                "primary.prof.snapshot"));
     }
 
     /** {@hide} */
@@ -608,6 +610,79 @@
         return false;
     }
 
+    /** {@hide} */ public static final int HAS_MUSIC = 1 << 0;
+    /** {@hide} */ public static final int HAS_PODCASTS = 1 << 1;
+    /** {@hide} */ public static final int HAS_RINGTONES = 1 << 2;
+    /** {@hide} */ public static final int HAS_ALARMS = 1 << 3;
+    /** {@hide} */ public static final int HAS_NOTIFICATIONS = 1 << 4;
+    /** {@hide} */ public static final int HAS_PICTURES = 1 << 5;
+    /** {@hide} */ public static final int HAS_MOVIES = 1 << 6;
+    /** {@hide} */ public static final int HAS_DOWNLOADS = 1 << 7;
+    /** {@hide} */ public static final int HAS_DCIM = 1 << 8;
+    /** {@hide} */ public static final int HAS_DOCUMENTS = 1 << 9;
+
+    /** {@hide} */ public static final int HAS_ANDROID = 1 << 16;
+    /** {@hide} */ public static final int HAS_OTHER = 1 << 17;
+
+    /**
+     * Classify the content types present on the given external storage device.
+     * <p>
+     * This is typically useful for deciding if an inserted SD card is empty, or
+     * if it contains content like photos that should be preserved.
+     *
+     * @hide
+     */
+    public static int classifyExternalStorageDirectory(File dir) {
+        int res = 0;
+        for (File f : FileUtils.listFilesOrEmpty(dir)) {
+            if (f.isFile() && isInterestingFile(f)) {
+                res |= HAS_OTHER;
+            } else if (f.isDirectory() && hasInterestingFiles(f)) {
+                final String name = f.getName();
+                if (DIRECTORY_MUSIC.equals(name)) res |= HAS_MUSIC;
+                else if (DIRECTORY_PODCASTS.equals(name)) res |= HAS_PODCASTS;
+                else if (DIRECTORY_RINGTONES.equals(name)) res |= HAS_RINGTONES;
+                else if (DIRECTORY_ALARMS.equals(name)) res |= HAS_ALARMS;
+                else if (DIRECTORY_NOTIFICATIONS.equals(name)) res |= HAS_NOTIFICATIONS;
+                else if (DIRECTORY_PICTURES.equals(name)) res |= HAS_PICTURES;
+                else if (DIRECTORY_MOVIES.equals(name)) res |= HAS_MOVIES;
+                else if (DIRECTORY_DOWNLOADS.equals(name)) res |= HAS_DOWNLOADS;
+                else if (DIRECTORY_DCIM.equals(name)) res |= HAS_DCIM;
+                else if (DIRECTORY_DOCUMENTS.equals(name)) res |= HAS_DOCUMENTS;
+                else if (DIRECTORY_ANDROID.equals(name)) res |= HAS_ANDROID;
+                else res |= HAS_OTHER;
+            }
+        }
+        return res;
+    }
+
+    private static boolean hasInterestingFiles(File dir) {
+        final LinkedList<File> explore = new LinkedList<>();
+        explore.add(dir);
+        while (!explore.isEmpty()) {
+            dir = explore.pop();
+            for (File f : FileUtils.listFilesOrEmpty(dir)) {
+                if (isInterestingFile(f)) return true;
+                if (f.isDirectory()) explore.add(f);
+            }
+        }
+        return false;
+    }
+
+    private static boolean isInterestingFile(File file) {
+        if (file.isFile()) {
+            final String name = file.getName().toLowerCase();
+            if (name.endsWith(".exe") || name.equals("autorun.inf")
+                    || name.equals("launchpad.zip") || name.equals(".nomedia")) {
+                return false;
+            } else {
+                return true;
+            }
+        } else {
+            return false;
+        }
+    }
+
     /**
      * Get a top-level shared/external storage directory for placing files of a
      * particular type. This is where the user will typically place and manage
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 0620fa3..9c90c38 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -79,7 +79,7 @@
     void setDefaultGuestRestrictions(in Bundle restrictions);
     Bundle getDefaultGuestRestrictions();
     boolean markGuestForDeletion(int userHandle);
-    void setQuietModeEnabled(int userHandle, boolean enableQuietMode);
+    void setQuietModeEnabled(int userHandle, boolean enableQuietMode, in IntentSender target);
     boolean isQuietModeEnabled(int userHandle);
     boolean trySetQuietModeDisabled(int userHandle, in IntentSender target);
     void setSeedAccountData(int userHandle, in String accountName,
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index d066db1..b303e10 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -109,7 +109,9 @@
     // sometimes we store linked lists of these things
     /*package*/ Message next;
 
-    private static final Object sPoolSync = new Object();
+
+    /** @hide */
+    public static final Object sPoolSync = new Object();
     private static Message sPool;
     private static int sPoolSize = 0;
 
@@ -370,6 +372,12 @@
         return callback;
     }
 
+    /** @hide */
+    public Message setCallback(Runnable r) {
+        callback = r;
+        return this;
+    }
+
     /**
      * Obtains a Bundle of arbitrary data associated with this
      * event, lazily creating it if necessary. Set this value by calling
@@ -411,6 +419,16 @@
     }
 
     /**
+     * Chainable setter for {@link #what}
+     *
+     * @hide
+     */
+    public Message setWhat(int what) {
+        this.what = what;
+        return this;
+    }
+
+    /**
      * Sends this Message to the Handler specified by {@link #getTarget}.
      * Throws a null pointer exception if this field has not been set.
      */
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 9491bec..3ec744d 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -33,6 +33,8 @@
     private static final int EVENT_TYPE_LIST = 3;
     private static final int EVENT_TYPE_FLOAT = 4;
 
+    // Keep this in sync with system/core/logcat/event.logtags
+    private static final int STATS_BUFFER_TAG_ID = 1937006964;
     /**
      * Creates a log_event that is binary-encoded as implemented in
      * system/core/liblog/log_event_list.c; this allows us to use the same parsing logic in statsd
@@ -46,9 +48,14 @@
      */
     public StatsLogEventWrapper(int tag, int fields) {
         // Write four bytes from tag, starting with least-significant bit.
-        write4Bytes(tag);
+        // For pulled data, this tag number is not really used. We use the same tag number as
+        // pushed ones to be consistent.
+        write4Bytes(STATS_BUFFER_TAG_ID);
         mStorage.write(EVENT_TYPE_LIST); // This is required to start the log entry.
         mStorage.write(fields); // Indicate number of elements in this list.
+        mStorage.write(EVENT_TYPE_INT);
+        // The first element is the real atom tag number
+        write4Bytes(tag);
     }
 
     /**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 22967af..61dd462 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2115,7 +2115,7 @@
      */
     public void setQuietModeEnabled(@UserIdInt int userHandle, boolean enableQuietMode) {
         try {
-            mService.setQuietModeEnabled(userHandle, enableQuietMode);
+            mService.setQuietModeEnabled(userHandle, enableQuietMode, null);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -2142,10 +2142,14 @@
      * unlocking. If the user is already unlocked, we call through to {@link #setQuietModeEnabled}
      * directly.
      *
-     * @return true if the quiet mode was disabled immediately
+     * @param userHandle The user that is going to disable quiet mode.
+     * @param target The target to launch when the user is unlocked.
+     * @return {@code true} if quiet mode is disabled without showing confirm credentials screen,
+     *         {@code false} otherwise.
      * @hide
      */
-    public boolean trySetQuietModeDisabled(@UserIdInt int userHandle, IntentSender target) {
+    public boolean trySetQuietModeDisabled(
+            @UserIdInt int userHandle, @Nullable IntentSender target) {
         try {
             return mService.trySetQuietModeDisabled(userHandle, target);
         } catch (RemoteException re) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5505f59..1d4477a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5330,11 +5330,12 @@
         /**
          * Experimental autofill feature.
          *
-         * <p>TODO(b/67867469): remove once feature is finished
+         * <p>TODO(b/67867469): document (or remove) once feature is finished
          * @hide
          */
         @TestApi
-        public static final String AUTOFILL_FEATURE_FIELD_DETECTION = "autofill_field_detection";
+        public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION =
+                "autofill_field_classification";
 
         /**
          * Experimental autofill feature.
@@ -9570,8 +9571,8 @@
          * The following keys are supported:
          *
          * <pre>
-         * screen_brightness_array         (string)
-         * dimming_scrim_array             (string)
+         * screen_brightness_array         (int[])
+         * dimming_scrim_array             (int[])
          * prox_screen_off_delay           (long)
          * prox_cooldown_trigger           (long)
          * prox_cooldown_period            (long)
@@ -10543,6 +10544,15 @@
             SOFT_AP_TIMEOUT_ENABLED
         };
 
+        /**
+         * Global settings that shouldn't be persisted.
+         *
+         * @hide
+         */
+        public static final String[] TRANSIENT_SETTINGS = {
+                LOCATION_GLOBAL_KILL_SWITCH,
+        };
+
         /** @hide */
         public static final String[] LEGACY_RESTORE_SETTINGS = {
         };
@@ -11114,7 +11124,7 @@
          *
          * <pre>
          * default               (int)
-         * options_array         (string)
+         * options_array         (int[])
          * </pre>
          *
          * All delays in integer minutes. Array order is respected.
diff --git a/core/java/android/service/autofill/EditDistanceScorer.java b/core/java/android/service/autofill/EditDistanceScorer.java
new file mode 100644
index 0000000..e25cd04
--- /dev/null
+++ b/core/java/android/service/autofill/EditDistanceScorer.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.autofill.AutofillValue;
+
+/**
+ * Helper used to calculate the classification score between an actual {@link AutofillValue} filled
+ * by the user and the expected value predicted by an autofill service.
+ *
+ * TODO(b/67867469):
+ * - improve javadoc
+ * - document algorithm / copy from InternalScorer
+ * - unhide / remove testApi
+ * @hide
+ */
+@TestApi
+public final class EditDistanceScorer extends InternalScorer implements Scorer, Parcelable {
+
+    private static final EditDistanceScorer sInstance = new EditDistanceScorer();
+
+    /**
+     * Gets the singleton instance.
+     */
+    public static EditDistanceScorer getInstance() {
+        return sInstance;
+    }
+
+    private EditDistanceScorer() {
+    }
+
+    @Override
+    public float getScore(@NonNull AutofillValue actualValue, @NonNull String userData) {
+        if (actualValue == null || !actualValue.isText() || userData == null) return 0;
+        // TODO(b/67867469): implement edit distance - currently it's returning either 0, 100%, or
+        // partial match when number of chars match
+        final String textValue = actualValue.getTextValue().toString();
+        final int total = textValue.length();
+        if (total != userData.length()) return 0F;
+
+        int matches = 0;
+        for (int i = 0; i < total; i++) {
+            if (Character.toLowerCase(textValue.charAt(i)) == Character
+                    .toLowerCase(userData.charAt(i))) {
+                matches++;
+            }
+        }
+
+        return ((float) matches) / total;
+    }
+
+    /////////////////////////////////////
+    // Object "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public String toString() {
+        return "EditDistanceScorer";
+    }
+
+    /////////////////////////////////////
+    // Parcelable "contract" methods. //
+    /////////////////////////////////////
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        // Do nothing
+    }
+
+    public static final Parcelable.Creator<EditDistanceScorer> CREATOR =
+            new Parcelable.Creator<EditDistanceScorer>() {
+        @Override
+        public EditDistanceScorer createFromParcel(Parcel parcel) {
+            return EditDistanceScorer.getInstance();
+        }
+
+        @Override
+        public EditDistanceScorer[] newArray(int size) {
+            return new EditDistanceScorer[size];
+        }
+    };
+}
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
new file mode 100644
index 0000000..0a60208
--- /dev/null
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.autofill.Helper;
+
+import com.android.internal.util.Preconditions;
+
+import com.google.android.collect.Lists;
+
+import java.util.List;
+
+/**
+ * Gets the <a href="#FieldsClassification">fields classification</a> results for a given field.
+ *
+ * TODO(b/67867469):
+ * - improve javadoc
+ * - unhide / remove testApi
+ *
+ * @hide
+ */
+@TestApi
+public final class FieldClassification implements Parcelable {
+
+    private final Match mMatch;
+
+    /** @hide */
+    public FieldClassification(@NonNull Match match) {
+        mMatch = Preconditions.checkNotNull(match);
+    }
+
+    /**
+     * Gets the {@link Match matches} with the highest {@link Match#getScore() scores}.
+     *
+     * <p><b>Note:</b> There's no guarantee of how many matches will be returned. In fact,
+     * the Android System might return just the top match to minimize the impact of field
+     * classification in the device's health.
+     */
+    @NonNull
+    public List<Match> getMatches() {
+        return Lists.newArrayList(mMatch);
+    }
+
+    @Override
+    public String toString() {
+        if (!sDebug) return super.toString();
+
+        return "FieldClassification: " + mMatch;
+    }
+
+    /////////////////////////////////////
+    // Parcelable "contract" methods. //
+    /////////////////////////////////////
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeParcelable(mMatch, flags);
+    }
+
+    public static final Parcelable.Creator<FieldClassification> CREATOR =
+            new Parcelable.Creator<FieldClassification>() {
+
+        @Override
+        public FieldClassification createFromParcel(Parcel parcel) {
+            return new FieldClassification(parcel.readParcelable(null));
+        }
+
+        @Override
+        public FieldClassification[] newArray(int size) {
+            return new FieldClassification[size];
+        }
+    };
+
+    /**
+     * Gets the score of a {@link UserData} entry for the field.
+     *
+     * TODO(b/67867469):
+     * - improve javadoc
+     * - unhide / remove testApi
+     *
+     * @hide
+     */
+    @TestApi
+    public static final class Match implements Parcelable {
+
+        private final String mRemoteId;
+        private final float mScore;
+
+        /** @hide */
+        public Match(String remoteId, float score) {
+            mRemoteId = Preconditions.checkNotNull(remoteId);
+            mScore = score;
+        }
+
+        /**
+         * Gets the remote id of the {@link UserData} entry.
+         */
+        @NonNull
+        public String getRemoteId() {
+            return mRemoteId;
+        }
+
+        /**
+         * Gets a score between the value of this field and the value of the {@link UserData} entry.
+         *
+         * <p>The score is based in a case-insensitive comparisson of all characters from both the
+         * field value and the user data entry, and it ranges from {@code 0} to {@code 1000000}:
+         * <ul>
+         *   <li>{@code 1.0} represents a full match ({@code 100%}).
+         *   <li>{@code 0.0} represents a full mismatch ({@code 0%}).
+         *   <li>Any other value is a partial match.
+         * </ul>
+         *
+         * <p>How the score is calculated depends on the algorithm used by the Android System.
+         * For example, if the user  data is {@code "abc"} and the field value us {@code " abc"},
+         * the result could be:
+         * <ul>
+         *   <li>{@code 1.0} if the algorithm trims the values.
+         *   <li>{@code 0.0} if the algorithm compares the values sequentially.
+         *   <li>{@code 0.75} if the algorithm consideres that 3/4 (75%) of the characters match.
+         * </ul>
+         *
+         * <p>Currently, the autofill service cannot configure the algorithm.
+         */
+        public float getScore() {
+            return mScore;
+        }
+
+        @Override
+        public String toString() {
+            if (!sDebug) return super.toString();
+
+            final StringBuilder string = new StringBuilder("Match: remoteId=");
+            Helper.appendRedacted(string, mRemoteId);
+            return string.append(", score=").append(mScore).toString();
+        }
+
+        /////////////////////////////////////
+        // Parcelable "contract" methods. //
+        /////////////////////////////////////
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeString(mRemoteId);
+            parcel.writeFloat(mScore);
+        }
+
+        @SuppressWarnings("hiding")
+        public static final Parcelable.Creator<Match> CREATOR = new Parcelable.Creator<Match>() {
+
+            @Override
+            public Match createFromParcel(Parcel parcel) {
+                return new Match(parcel.readString(), parcel.readFloat());
+            }
+
+            @Override
+            public Match[] newArray(int size) {
+                return new Match[size];
+            }
+        };
+    }
+}
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index eedb972..facad2d 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -16,6 +16,8 @@
 
 package android.service.autofill;
 
+import static android.view.autofill.Helper.sVerbose;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -24,8 +26,10 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.service.autofill.FieldClassification.Match;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 
@@ -35,6 +39,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -57,6 +62,8 @@
  * the history will clear out after some pre-defined time).
  */
 public final class FillEventHistory implements Parcelable {
+    private static final String TAG = "FillEventHistory";
+
     /**
      * Not in parcel. The ID of the autofill session that created the {@link FillResponse}.
      */
@@ -123,34 +130,35 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeBundle(mClientState);
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeBundle(mClientState);
         if (mEvents == null) {
-            dest.writeInt(0);
+            parcel.writeInt(0);
         } else {
-            dest.writeInt(mEvents.size());
+            parcel.writeInt(mEvents.size());
 
             int numEvents = mEvents.size();
             for (int i = 0; i < numEvents; i++) {
                 Event event = mEvents.get(i);
-                dest.writeInt(event.mEventType);
-                dest.writeString(event.mDatasetId);
-                dest.writeBundle(event.mClientState);
-                dest.writeStringList(event.mSelectedDatasetIds);
-                dest.writeArraySet(event.mIgnoredDatasetIds);
-                dest.writeTypedList(event.mChangedFieldIds);
-                dest.writeStringList(event.mChangedDatasetIds);
+                parcel.writeInt(event.mEventType);
+                parcel.writeString(event.mDatasetId);
+                parcel.writeBundle(event.mClientState);
+                parcel.writeStringList(event.mSelectedDatasetIds);
+                parcel.writeArraySet(event.mIgnoredDatasetIds);
+                parcel.writeTypedList(event.mChangedFieldIds);
+                parcel.writeStringList(event.mChangedDatasetIds);
 
-                dest.writeTypedList(event.mManuallyFilledFieldIds);
+                parcel.writeTypedList(event.mManuallyFilledFieldIds);
                 if (event.mManuallyFilledFieldIds != null) {
                     final int size = event.mManuallyFilledFieldIds.size();
                     for (int j = 0; j < size; j++) {
-                        dest.writeStringList(event.mManuallyFilledDatasetIds.get(j));
+                        parcel.writeStringList(event.mManuallyFilledDatasetIds.get(j));
                     }
                 }
-                dest.writeString(event.mDetectedRemoteId);
-                if (event.mDetectedRemoteId != null) {
-                    dest.writeInt(event.mDetectedFieldScore);
+                final AutofillId[] detectedFields = event.mDetectedFieldIds;
+                parcel.writeParcelableArray(detectedFields, flags);
+                if (detectedFields != null) {
+                    parcel.writeParcelableArray(event.mDetectedMatches, flags);
                 }
             }
         }
@@ -242,8 +250,8 @@
         @Nullable private final ArrayList<AutofillId> mManuallyFilledFieldIds;
         @Nullable private final ArrayList<ArrayList<String>> mManuallyFilledDatasetIds;
 
-        @Nullable private final String mDetectedRemoteId;
-        private final int mDetectedFieldScore;
+        @Nullable private final AutofillId[] mDetectedFieldIds;
+        @Nullable private final Match[] mDetectedMatches;
 
         /**
          * Returns the type of the event.
@@ -347,37 +355,33 @@
         }
 
         /**
-         * Gets the results of the last fields classification request.
-         *
-         * @return map of edit-distance match ({@code 0} means full match,
-         * {@code 1} means 1 character different, etc...) by remote id (as set on
-         * {@link UserData.Builder#add(String, android.view.autofill.AutofillValue)}),
-         * or {@code null} if none of the user-input values
-         * matched the requested detection.
+         * Gets the <a href="#FieldsClassification">fields classification</a> results.
          *
          * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}, when the
          * service requested {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)
-         * fields detection}.
+         * fields classification}.
          *
          * TODO(b/67867469):
          *  - improve javadoc
-         *  - refine score meaning (for example, should 1 be different of -1?)
-         *  - mention when it's set
-         *  - unhide
          *  - unhide / remove testApi
-         *  - add @NonNull / check it / add unit tests
-         *  - add link to AutofillService #FieldsClassification anchor
          *
          * @hide
          */
         @TestApi
-        @NonNull public Map<String, Integer> getFieldsClassification() {
-            if (mDetectedRemoteId == null || mDetectedFieldScore == -1) {
+        @NonNull public Map<AutofillId, FieldClassification> getFieldsClassification() {
+            if (mDetectedFieldIds == null) {
                 return Collections.emptyMap();
             }
-
-            final ArrayMap<String, Integer> map = new ArrayMap<>(1);
-            map.put(mDetectedRemoteId, mDetectedFieldScore);
+            final int size = mDetectedFieldIds.length;
+            final ArrayMap<AutofillId, FieldClassification> map = new ArrayMap<>(size);
+            for (int i = 0; i < size; i++) {
+                final AutofillId id = mDetectedFieldIds[i];
+                final Match match = mDetectedMatches[i];
+                if (sVerbose) {
+                    Log.v(TAG, "getFieldsClassification[" + i + "]: id=" + id + ", match=" + match);
+                }
+                map.put(id, new FieldClassification(match));
+            }
             return map;
         }
 
@@ -464,7 +468,7 @@
          *
          * @hide
          */
-        // TODO(b/67867469): document detection field parameters once stable
+        // TODO(b/67867469): document field classification parameters once stable
         public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState,
                 @Nullable List<String> selectedDatasetIds,
                 @Nullable ArraySet<String> ignoredDatasetIds,
@@ -472,7 +476,7 @@
                 @Nullable ArrayList<String> changedDatasetIds,
                 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
                 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
-                @Nullable String detectedRemoteId, int detectedFieldScore) {
+                @Nullable AutofillId[] detectedFieldIds, @Nullable Match[] detectedMaches) {
             mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_CONTEXT_COMMITTED,
                     "eventType");
             mDatasetId = datasetId;
@@ -495,8 +499,9 @@
             }
             mManuallyFilledFieldIds = manuallyFilledFieldIds;
             mManuallyFilledDatasetIds = manuallyFilledDatasetIds;
-            mDetectedRemoteId = detectedRemoteId;
-            mDetectedFieldScore = detectedFieldScore;
+
+            mDetectedFieldIds = detectedFieldIds;
+            mDetectedMatches = detectedMaches;
         }
 
         @Override
@@ -509,8 +514,8 @@
                     + ", changedDatasetsIds=" + mChangedDatasetIds
                     + ", manuallyFilledFieldIds=" + mManuallyFilledFieldIds
                     + ", manuallyFilledDatasetIds=" + mManuallyFilledDatasetIds
-                    + ", detectedRemoteId=" + mDetectedRemoteId
-                    + ", detectedFieldScore=" + mDetectedFieldScore
+                    + ", detectedFieldIds=" + Arrays.toString(mDetectedFieldIds)
+                    + ", detectedMaches =" + Arrays.toString(mDetectedMatches)
                     + "]";
         }
     }
@@ -546,15 +551,17 @@
                         } else {
                             manuallyFilledDatasetIds = null;
                         }
-                        final String detectedRemoteId = parcel.readString();
-                        final int detectedFieldScore = detectedRemoteId == null ? -1
-                                : parcel.readInt();
+                        final AutofillId[] detectedFieldIds = parcel.readParcelableArray(null,
+                                AutofillId.class);
+                        final Match[] detectedMatches = (detectedFieldIds != null)
+                                ? parcel.readParcelableArray(null, Match.class)
+                                : null;
 
                         selection.addEvent(new Event(eventType, datasetId, clientState,
                                 selectedDatasetIds, ignoredDatasets,
                                 changedFieldIds, changedDatasetIds,
                                 manuallyFilledFieldIds, manuallyFilledDatasetIds,
-                                detectedRemoteId, detectedFieldScore));
+                                detectedFieldIds, detectedMatches));
                     }
                     return selection;
                 }
diff --git a/core/java/android/service/autofill/InternalScorer.java b/core/java/android/service/autofill/InternalScorer.java
new file mode 100644
index 0000000..0da5afc
--- /dev/null
+++ b/core/java/android/service/autofill/InternalScorer.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcelable;
+import android.view.autofill.AutofillValue;
+
+/**
+ * Superclass of all scorer the system understands. As this is not public all
+ * subclasses have to implement {@link Scorer} again.
+ *
+ * @hide
+ */
+@TestApi
+public abstract class InternalScorer implements Scorer, Parcelable {
+
+    /**
+     * Returns the classification score between an actual {@link AutofillValue} filled
+     * by the user and the expected value predicted by an autofill service.
+     *
+     * <p>A full-match is {@code 1.0} (representing 100%), a full mismatch is {@code 0.0} and
+     * partial mathces are something in between, typically using edit-distance algorithms.
+     */
+    public abstract float getScore(@NonNull AutofillValue actualValue, @NonNull String userData);
+}
diff --git a/core/java/android/service/autofill/Scorer.java b/core/java/android/service/autofill/Scorer.java
new file mode 100644
index 0000000..f6a802a
--- /dev/null
+++ b/core/java/android/service/autofill/Scorer.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.autofill;
+
+import android.annotation.TestApi;
+
+/**
+ * Helper class used to calculate a score.
+ *
+ * <p>Typically used to calculate the field classification score between an actual
+ * {@link android.view.autofill.AutofillValue}  filled by the user and the expected value predicted
+ * by an autofill service.
+ *
+ * TODO(b/67867469):
+ * - improve javadoc
+ * - unhide / remove testApi
+ * @hide
+ */
+@TestApi
+public interface Scorer {
+
+}
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 16d8d4a..0d37815 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -29,7 +29,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.provider.Settings;
-import android.util.ArraySet;
 import android.util.Log;
 import android.view.autofill.Helper;
 
@@ -57,10 +56,12 @@
     private static final int DEFAULT_MIN_VALUE_LENGTH = 5;
     private static final int DEFAULT_MAX_VALUE_LENGTH = 100;
 
+    private final InternalScorer mScorer;
     private final String[] mRemoteIds;
     private final String[] mValues;
 
     private UserData(Builder builder) {
+        mScorer = builder.mScorer;
         mRemoteIds = new String[builder.mRemoteIds.size()];
         builder.mRemoteIds.toArray(mRemoteIds);
         mValues = new String[builder.mValues.size()];
@@ -68,6 +69,11 @@
     }
 
     /** @hide */
+    public InternalScorer getScorer() {
+        return mScorer;
+    }
+
+    /** @hide */
     public String[] getRemoteIds() {
         return mRemoteIds;
     }
@@ -79,10 +85,17 @@
 
     /** @hide */
     public void dump(String prefix, PrintWriter pw) {
-        // Cannot disclose remote ids because they could contain PII
+        pw.print(prefix); pw.print("Scorer: "); pw.println(mScorer);
+        // Cannot disclose remote ids or values because they could contain PII
         pw.print(prefix); pw.print("Remote ids size: "); pw.println(mRemoteIds.length);
+        for (int i = 0; i < mRemoteIds.length; i++) {
+            pw.print(prefix); pw.print(prefix); pw.print(i); pw.print(": ");
+            pw.println(Helper.getRedacted(mRemoteIds[i]));
+        }
+        pw.print(prefix); pw.print("Values size: "); pw.println(mValues.length);
         for (int i = 0; i < mValues.length; i++) {
-            pw.print(prefix); pw.print(prefix); pw.print(i); pw.print(": "); pw.println(mValues[i]);
+            pw.print(prefix); pw.print(prefix); pw.print(i); pw.print(": ");
+            pw.println(Helper.getRedacted(mValues[i]));
         }
     }
 
@@ -104,7 +117,8 @@
      */
     @TestApi
     public static final class Builder {
-        private final ArraySet<String> mRemoteIds;
+        private final InternalScorer mScorer;
+        private final ArrayList<String> mRemoteIds;
         private final ArrayList<String> mValues;
         private boolean mDestroyed;
 
@@ -112,15 +126,23 @@
          * Creates a new builder for the user data used for <a href="#FieldsClassification">fields
          * classification</a>.
          *
-         * @throws IllegalArgumentException if {@code remoteId} or {@code value} are empty or if the
-         * length of {@code value} is lower than {@link UserData#getMinValueLength()}
-         * or higher than {@link UserData#getMaxValueLength()}.
+         * @throws IllegalArgumentException if any of the following occurs:
+         * <ol>
+         *   <li>{@code remoteId} is empty
+         *   <li>{@code value} is empty
+         *   <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}
+         *   <li>the length of {@code value} is higher than {@link UserData#getMaxValueLength()}
+         *   <li>{@code scorer} is not instance of a class provided by the Android System.
+         * </ol>
          */
-        public Builder(@NonNull String remoteId, @NonNull String value) {
+        public Builder(@NonNull Scorer scorer, @NonNull String remoteId, @NonNull String value) {
+            Preconditions.checkArgument((scorer instanceof InternalScorer),
+                    "not provided by Android System: " + scorer);
+            mScorer = (InternalScorer) scorer;
             checkValidRemoteId(remoteId);
             checkValidValue(value);
             final int capacity = getMaxUserDataSize();
-            mRemoteIds = new ArraySet<>(capacity);
+            mRemoteIds = new ArrayList<>(capacity);
             mValues = new ArrayList<>(capacity);
             mRemoteIds.add(remoteId);
             mValues.add(value);
@@ -149,10 +171,14 @@
             Preconditions.checkState(!mRemoteIds.contains(remoteId),
                     // Don't include remoteId on message because it could contain PII
                     "already has entry with same remoteId");
+            Preconditions.checkState(!mValues.contains(value),
+                    // Don't include remoteId on message because it could contain PII
+                    "already has entry with same value");
             Preconditions.checkState(mRemoteIds.size() < getMaxUserDataSize(),
                     "already added " + mRemoteIds.size() + " elements");
             mRemoteIds.add(remoteId);
             mValues.add(value);
+
             return this;
         }
 
@@ -197,8 +223,9 @@
     public String toString() {
         if (!sDebug) return super.toString();
 
-        // Cannot disclose keys or values because they could contain PII
-        final StringBuilder builder = new StringBuilder("UserData: [remoteIds=");
+        final StringBuilder builder = new StringBuilder("UserData: [scorer=").append(mScorer);
+        // Cannot disclose remote ids or values because they could contain PII
+        builder.append(", remoteIds=");
         Helper.appendRedacted(builder, mRemoteIds);
         builder.append(", values=");
         Helper.appendRedacted(builder, mValues);
@@ -216,6 +243,7 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeParcelable(mScorer, flags);
         parcel.writeStringArray(mRemoteIds);
         parcel.writeStringArray(mValues);
     }
@@ -227,9 +255,10 @@
             // Always go through the builder to ensure the data ingested by
             // the system obeys the contract of the builder to avoid attacks
             // using specially crafted parcels.
+            final InternalScorer scorer = parcel.readParcelable(null);
             final String[] remoteIds = parcel.readStringArray();
             final String[] values = parcel.readStringArray();
-            final Builder builder = new Builder(remoteIds[0], values[0]);
+            final Builder builder = new Builder(scorer, remoteIds[0], values[0]);
             for (int i = 1; i < remoteIds.length; i++) {
                 builder.add(remoteIds[i], values[i]);
             }
@@ -259,14 +288,14 @@
     }
 
     /**
-     * Gets the minimum length of values passed to {@link Builder#Builder(String, String)}.
+     * Gets the minimum length of values passed to {@link Builder#Builder(Scorer, String, String)}.
      */
     public static int getMinValueLength() {
         return getInt(AUTOFILL_USER_DATA_MIN_VALUE_LENGTH, DEFAULT_MIN_VALUE_LENGTH);
     }
 
     /**
-     * Gets the maximum length of values passed to {@link Builder#Builder(String, String)}.
+     * Gets the maximum length of values passed to {@link Builder#Builder(Scorer, String, String)}.
      */
     public static int getMaxValueLength() {
         return getInt(AUTOFILL_USER_DATA_MAX_VALUE_LENGTH, DEFAULT_MAX_VALUE_LENGTH);
diff --git a/services/core/java/com/android/server/notification/ScheduleCalendar.java b/core/java/android/service/notification/ScheduleCalendar.java
similarity index 77%
rename from services/core/java/com/android/server/notification/ScheduleCalendar.java
rename to core/java/android/service/notification/ScheduleCalendar.java
index 5ff0e21..8a7ff4d 100644
--- a/services/core/java/com/android/server/notification/ScheduleCalendar.java
+++ b/core/java/android/service/notification/ScheduleCalendar.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (c) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.notification;
+package android.service.notification;
 
 import android.service.notification.ZenModeConfig.ScheduleInfo;
 import android.util.ArraySet;
@@ -24,7 +24,12 @@
 import java.util.Objects;
 import java.util.TimeZone;
 
+/**
+ * @hide
+ */
 public class ScheduleCalendar {
+    public static final String TAG = "ScheduleCalendar";
+    public static final boolean DEBUG = Log.isLoggable("ConditionProviders", Log.DEBUG);
     private final ArraySet<Integer> mDays = new ArraySet<Integer>();
     private final Calendar mCalendar = Calendar.getInstance();
 
@@ -35,12 +40,28 @@
         return "ScheduleCalendar[mDays=" + mDays + ", mSchedule=" + mSchedule + "]";
     }
 
+    /**
+     * @return true if schedule will exit on alarm, else false
+     */
+    public boolean exitAtAlarm() {
+        return mSchedule.exitAtAlarm;
+    }
+
+    /**
+     * Sets schedule information
+     */
     public void setSchedule(ScheduleInfo schedule) {
         if (Objects.equals(mSchedule, schedule)) return;
         mSchedule = schedule;
         updateDays();
     }
 
+    /**
+     * Sets next alarm of the schedule if the saved next alarm has passed or is further
+     * in the future than given nextAlarm
+     * @param now current time in milliseconds
+     * @param nextAlarm time of next alarm in milliseconds
+     */
     public void maybeSetNextAlarm(long now, long nextAlarm) {
         if (mSchedule != null && mSchedule.exitAtAlarm) {
             // alarm canceled
@@ -56,19 +77,26 @@
                     mSchedule.nextAlarm = Math.min(mSchedule.nextAlarm, nextAlarm);
                 }
             } else if (mSchedule.nextAlarm < now) {
-                if (ScheduleConditionProvider.DEBUG) {
-                    Log.d(ScheduleConditionProvider.TAG,
-                            "All alarms are in the past " + mSchedule.nextAlarm);
+                if (DEBUG) {
+                    Log.d(TAG, "All alarms are in the past " + mSchedule.nextAlarm);
                 }
                 mSchedule.nextAlarm = 0;
             }
         }
     }
 
+    /**
+     * Set calendar time zone to tz
+     * @param tz current time zone
+     */
     public void setTimeZone(TimeZone tz) {
         mCalendar.setTimeZone(tz);
     }
 
+    /**
+     * @param now current time in milliseconds
+     * @return next time this rule changes (starts or ends)
+     */
     public long getNextChangeTime(long now) {
         if (mSchedule == null) return 0;
         final long nextStart = getNextTime(now, mSchedule.startHour, mSchedule.startMinute);
@@ -92,6 +120,10 @@
         return mCalendar.getTimeInMillis();
     }
 
+    /**
+     * @param time milliseconds since Epoch
+     * @return true if time is within the schedule, else false
+     */
     public boolean isInSchedule(long time) {
         if (mSchedule == null || mDays.size() == 0) return false;
         final long start = getTime(time, mSchedule.startHour, mSchedule.startMinute);
@@ -102,6 +134,10 @@
         return isInSchedule(-1, time, start, end) || isInSchedule(0, time, start, end);
     }
 
+    /**
+     * @param time milliseconds since Epoch
+     * @return true if should exit at time for next alarm, else false
+     */
     public boolean shouldExitForAlarm(long time) {
         if (mSchedule == null) {
             return false;
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 1ec2406..f658ae0 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -46,8 +46,10 @@
 import java.util.Calendar;
 import java.util.Date;
 import java.util.GregorianCalendar;
+import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
+import java.util.TimeZone;
 import java.util.UUID;
 
 /**
@@ -64,11 +66,13 @@
     public static final int MAX_SOURCE = SOURCE_STAR;
     private static final int DEFAULT_SOURCE = SOURCE_CONTACT;
 
+    public static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
+    public static final String EVERY_NIGHT_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
+    public static final List<String> DEFAULT_RULE_IDS = Arrays.asList(EVERY_NIGHT_DEFAULT_RULE_ID,
+            EVENTS_DEFAULT_RULE_ID);
+
     public static final int[] ALL_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
             Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY };
-    public static final int[] WEEKNIGHT_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
-            Calendar.WEDNESDAY, Calendar.THURSDAY };
-    public static final int[] WEEKEND_DAYS = { Calendar.FRIDAY, Calendar.SATURDAY };
 
     public static final int[] MINUTE_BUCKETS = generateMinuteBuckets();
     private static final int SECONDS_MS = 1000;
@@ -529,6 +533,13 @@
         rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
         rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
         rt.condition = readConditionXml(parser);
+
+        // all default rules and user created rules updated to zenMode important interruptions
+        if (rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+                && Condition.isValidId(rt.conditionId, SYSTEM_AUTHORITY)) {
+            Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name);
+            rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        }
         return rt;
     }
 
@@ -692,6 +703,20 @@
                 suppressedVisualEffects);
     }
 
+    /**
+     * Creates scheduleCalendar from a condition id
+     * @param conditionId
+     * @return ScheduleCalendar with info populated with conditionId
+     */
+    public static ScheduleCalendar toScheduleCalendar(Uri conditionId) {
+        final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(conditionId);
+        if (schedule == null || schedule.days == null || schedule.days.length == 0) return null;
+        final ScheduleCalendar sc = new ScheduleCalendar();
+        sc.setSchedule(schedule);
+        sc.setTimeZone(TimeZone.getDefault());
+        return sc;
+    }
+
     private static int sourceToPrioritySenders(int source, int def) {
         switch (source) {
             case SOURCE_ANYONE: return Policy.PRIORITY_SENDERS_ANY;
@@ -793,7 +818,10 @@
                 Condition.FLAG_RELEVANT_NOW);
     }
 
-    private static CharSequence getFormattedTime(Context context, long time, boolean isSameDay,
+    /**
+     * Creates readable time from time in milliseconds
+     */
+    public static CharSequence getFormattedTime(Context context, long time, boolean isSameDay,
             int userHandle) {
         String skeleton = (!isSameDay ? "EEE " : "")
                 + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma");
@@ -801,7 +829,10 @@
         return DateFormat.format(pattern, time);
     }
 
-    private static boolean isToday(long time) {
+    /**
+     * Determines whether a time in milliseconds is today or not
+     */
+    public static boolean isToday(long time) {
         GregorianCalendar now = new GregorianCalendar();
         GregorianCalendar endTime = new GregorianCalendar();
         endTime.setTimeInMillis(time);
@@ -1081,7 +1112,10 @@
         return UUID.randomUUID().toString().replace("-", "");
     }
 
-    private static String getOwnerCaption(Context context, String owner) {
+    /**
+     * Gets the name of the app associated with owner
+     */
+    public static String getOwnerCaption(Context context, String owner) {
         final PackageManager pm = context.getPackageManager();
         try {
             final ApplicationInfo info = pm.getApplicationInfo(owner, 0);
diff --git a/core/java/android/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/KeyValueListParser.java b/core/java/android/util/KeyValueListParser.java
index d50395e..0a00794 100644
--- a/core/java/android/util/KeyValueListParser.java
+++ b/core/java/android/util/KeyValueListParser.java
@@ -149,6 +149,34 @@
     }
 
     /**
+     * Get the value for key as an integer array.
+     *
+     * The value should be encoded as "0:1:2:3:4"
+     *
+     * @param key The key to lookup.
+     * @param def The value to return if the key was not found.
+     * @return the int[] value associated with the key.
+     */
+    public int[] getIntArray(String key, int[] def) {
+        String value = mValues.get(key);
+        if (value != null) {
+            try {
+                String[] parts = value.split(":");
+                if (parts.length > 0) {
+                    int[] ret = new int[parts.length];
+                    for (int i = 0; i < parts.length; i++) {
+                        ret[i] = Integer.parseInt(parts[i]);
+                    }
+                    return ret;
+                }
+            } catch (NumberFormatException e) {
+                // fallthrough
+            }
+        }
+        return def;
+    }
+
+    /**
      * @return the number of keys.
      */
     public int size() {
diff --git a/core/java/android/util/Pools.java b/core/java/android/util/Pools.java
index 70581be..f0b7e01 100644
--- a/core/java/android/util/Pools.java
+++ b/core/java/android/util/Pools.java
@@ -130,22 +130,29 @@
     }
 
     /**
-     * Synchronized) pool of objects.
+     * Synchronized pool of objects.
      *
      * @param <T> The pooled type.
      */
     public static class SynchronizedPool<T> extends SimplePool<T> {
-        private final Object mLock = new Object();
+        private final Object mLock;
 
         /**
          * Creates a new instance.
          *
          * @param maxPoolSize The max pool size.
+         * @param lock an optional custom object to synchronize on
          *
          * @throws IllegalArgumentException If the max pool size is less than zero.
          */
-        public SynchronizedPool(int maxPoolSize) {
+        public SynchronizedPool(int maxPoolSize, Object lock) {
             super(maxPoolSize);
+            mLock = lock;
+        }
+
+        /** @see #SynchronizedPool(int, Object)  */
+        public SynchronizedPool(int maxPoolSize) {
+            this(maxPoolSize, new Object());
         }
 
         @Override
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 2ffa1c5..ba6b6cf 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -164,6 +164,7 @@
     private long mLastFrameTimeNanos;
     private long mFrameIntervalNanos;
     private boolean mDebugPrintNextFrameTimeDelta;
+    private int mFPSDivisor = 1;
 
     /**
      * Contains information about the current frame for jank-tracking,
@@ -601,6 +602,11 @@
         }
     }
 
+    void setFPSDivisor(int divisor) {
+        if (divisor <= 0) divisor = 1;
+        mFPSDivisor = divisor;
+    }
+
     void doFrame(long frameTimeNanos, int frame) {
         final long startNanos;
         synchronized (mLock) {
@@ -643,6 +649,14 @@
                 return;
             }
 
+            if (mFPSDivisor > 1) {
+                long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
+                if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
+                    scheduleVsyncLocked();
+                    return;
+                }
+            }
+
             mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
             mFrameScheduled = false;
             mLastFrameTimeNanos = frameTimeNanos;
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 29032a7..9e103a3 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -147,7 +147,6 @@
     void exitKeyguardSecurely(IOnKeyguardExitResult callback);
     boolean isKeyguardLocked();
     boolean isKeyguardSecure();
-    boolean inKeyguardRestrictedInputMode();
     void dismissKeyguard(IKeyguardDismissCallback callback);
 
     // Requires INTERACT_ACROSS_USERS_FULL permission
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index e3da757..420a1bb 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -702,7 +702,7 @@
      */
     @Deprecated
     public static void mergeToGlobalTransaction(Transaction t) {
-        synchronized(sGlobalTransaction) {
+        synchronized(SurfaceControl.class) {
             sGlobalTransaction.merge(t);
         }
     }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 7c76bab..0a69772 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -190,6 +190,17 @@
     public static final String DEBUG_SHOW_NON_RECTANGULAR_CLIP_PROPERTY =
             "debug.hwui.show_non_rect_clip";
 
+    /**
+     * Sets the FPS devisor to lower the FPS.
+     *
+     * Sets a positive integer as a divisor. 1 (the default value) menas the full FPS, and 2
+     * means half the full FPS.
+     *
+     *
+     * @hide
+     */
+    public static final String DEBUG_FPS_DIVISOR = "debug.hwui.fps_divisor";
+
     static {
         // Try to check OpenGL support early if possible.
         isAvailable();
@@ -955,6 +966,9 @@
             if (mInitialized) return;
             mInitialized = true;
             mAppContext = context.getApplicationContext();
+
+            // b/68769804: For low FPS experiments.
+            setFPSDivisor(SystemProperties.getInt(DEBUG_FPS_DIVISOR, 1));
             initSched(renderProxy);
             initGraphicsStats();
         }
@@ -1007,6 +1021,13 @@
         observer.mNative = null;
     }
 
+    /** b/68769804: For low FPS experiments. */
+    public static void setFPSDivisor(int divisor) {
+        if (divisor <= 0) divisor = 1;
+        Choreographer.getInstance().setFPSDivisor(divisor);
+        nHackySetRTAnimationsEnabled(divisor == 1);
+    }
+
     /** Not actually public - internal use only. This doc to make lint happy */
     public static native void disableVsync();
 
@@ -1075,4 +1096,6 @@
 
     private static native Bitmap nCreateHardwareBitmap(long renderNode, int width, int height);
     private static native void nSetHighContrastText(boolean enabled);
+    // For temporary experimentation b/66945974
+    private static native void nHackySetRTAnimationsEnabled(boolean enabled);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0525ab1..9cfae8f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -893,6 +893,15 @@
      */
     private static boolean sAutoFocusableOffUIThreadWontNotifyParents;
 
+    /**
+     * Prior to P things like setScaleX() allowed passing float values that were bogus such as
+     * Float.NaN. If the app is targetting P or later then passing these values will result in an
+     * exception being thrown. If the app is targetting an earlier SDK version, then we will
+     * silently clamp these values to avoid crashes elsewhere when the rendering code hits
+     * these bogus values.
+     */
+    private static boolean sThrowOnInvalidFloatProperties;
+
     /** @hide */
     @IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO})
     @Retention(RetentionPolicy.SOURCE)
@@ -4752,6 +4761,8 @@
             sUseDefaultFocusHighlight = context.getResources().getBoolean(
                     com.android.internal.R.bool.config_useDefaultFocusHighlight);
 
+            sThrowOnInvalidFloatProperties = targetSdkVersion >= Build.VERSION_CODES.P;
+
             sCompatibilityDone = true;
         }
     }
@@ -7208,8 +7219,7 @@
      * @param text The announcement text.
      */
     public void announceForAccessibility(CharSequence text) {
-        if (AccessibilityManager.getInstance(mContext).isObservedEventType(
-                AccessibilityEvent.TYPE_ANNOUNCEMENT) && mParent != null) {
+        if (AccessibilityManager.getInstance(mContext).isEnabled() && mParent != null) {
             AccessibilityEvent event = AccessibilityEvent.obtain(
                     AccessibilityEvent.TYPE_ANNOUNCEMENT);
             onInitializeAccessibilityEvent(event);
@@ -10968,8 +10978,7 @@
         if ((mPrivateFlags2 & PFLAG2_ACCESSIBILITY_FOCUSED) != 0) {
             mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_FOCUSED;
             invalidate();
-            if (AccessibilityManager.getInstance(mContext).isObservedEventType(
-                    AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED)) {
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                 AccessibilityEvent event = AccessibilityEvent.obtain(
                         AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
                 event.setAction(action);
@@ -11794,8 +11803,7 @@
 
     private void sendViewTextTraversedAtGranularityEvent(int action, int granularity,
             int fromIndex, int toIndex) {
-        if (mParent == null || !AccessibilityManager.getInstance(mContext).isObservedEventType(
-                AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY)) {
+        if (mParent == null) {
             return;
         }
         AccessibilityEvent event = AccessibilityEvent.obtain(
@@ -14275,7 +14283,7 @@
      */
     public void setScaleX(float scaleX) {
         if (scaleX != getScaleX()) {
-            requireIsFinite(scaleX, "scaleX");
+            scaleX = sanitizeFloatPropertyValue(scaleX, "scaleX");
             invalidateViewProperty(true, false);
             mRenderNode.setScaleX(scaleX);
             invalidateViewProperty(false, true);
@@ -14312,7 +14320,7 @@
      */
     public void setScaleY(float scaleY) {
         if (scaleY != getScaleY()) {
-            requireIsFinite(scaleY, "scaleY");
+            scaleY = sanitizeFloatPropertyValue(scaleY, "scaleY");
             invalidateViewProperty(true, false);
             mRenderNode.setScaleY(scaleY);
             invalidateViewProperty(false, true);
@@ -14862,13 +14870,41 @@
         }
     }
 
-    private static void requireIsFinite(float transform, String propertyName) {
-        if (Float.isNaN(transform)) {
-            throw new IllegalArgumentException("Cannot set '" + propertyName + "' to Float.NaN");
+    private static float sanitizeFloatPropertyValue(float value, String propertyName) {
+        return sanitizeFloatPropertyValue(value, propertyName, -Float.MAX_VALUE, Float.MAX_VALUE);
+    }
+
+    private static float sanitizeFloatPropertyValue(float value, String propertyName,
+            float min, float max) {
+        // The expected "nothing bad happened" path
+        if (value >= min && value <= max) return value;
+
+        if (value < min || value == Float.NEGATIVE_INFINITY) {
+            if (sThrowOnInvalidFloatProperties) {
+                throw new IllegalArgumentException("Cannot set '" + propertyName + "' to "
+                        + value + ", the value must be >= " + min);
+            }
+            return min;
         }
-        if (Float.isInfinite(transform)) {
-            throw new IllegalArgumentException("Cannot set '" + propertyName + "' to infinity");
+
+        if (value > max || value == Float.POSITIVE_INFINITY) {
+            if (sThrowOnInvalidFloatProperties) {
+                throw new IllegalArgumentException("Cannot set '" + propertyName + "' to "
+                        + value + ", the value must be <= " + max);
+            }
+            return max;
         }
+
+        if (Float.isNaN(value)) {
+            if (sThrowOnInvalidFloatProperties) {
+                throw new IllegalArgumentException(
+                        "Cannot set '" + propertyName + "' to Float.NaN");
+            }
+            return 0; // Unclear which direction this NaN went so... 0?
+        }
+
+        // Shouldn't be possible to reach this.
+        throw new IllegalStateException("How do you get here?? " + value);
     }
 
     /**
@@ -14957,7 +14993,7 @@
      */
     public void setElevation(float elevation) {
         if (elevation != getElevation()) {
-            requireIsFinite(elevation, "elevation");
+            elevation = sanitizeFloatPropertyValue(elevation, "elevation");
             invalidateViewProperty(true, false);
             mRenderNode.setElevation(elevation);
             invalidateViewProperty(false, true);
@@ -15050,7 +15086,7 @@
      */
     public void setTranslationZ(float translationZ) {
         if (translationZ != getTranslationZ()) {
-            requireIsFinite(translationZ, "translationZ");
+            translationZ = sanitizeFloatPropertyValue(translationZ, "translationZ");
             invalidateViewProperty(true, false);
             mRenderNode.setTranslationZ(translationZ);
             invalidateViewProperty(false, true);
@@ -26185,8 +26221,7 @@
 
         @Override
         public void run() {
-            if (AccessibilityManager.getInstance(mContext).isObservedEventType(
-                    AccessibilityEvent.TYPE_VIEW_SCROLLED)) {
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                 AccessibilityEvent event = AccessibilityEvent.obtain(
                         AccessibilityEvent.TYPE_VIEW_SCROLLED);
                 event.setScrollDeltaX(mDeltaX);
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 0375635..35f6acb 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -24,7 +24,6 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -188,7 +187,6 @@
      *
      * @hide
      */
-    @TestApi
     public interface AccessibilityServicesStateChangeListener {
 
         /**
@@ -454,18 +452,6 @@
     }
 
     /**
-     * Returns whether there are observers registered for this event type. If
-     * this method returns false you shuold not generate events of this type
-     * to conserve resources.
-     *
-     * @param type The event type.
-     * @return Whether the event is being observed.
-     */
-    public boolean isObservedEventType(@AccessibilityEvent.EventType int type) {
-        return mIsEnabled && (mRelevantEventTypes & type) != 0;
-    }
-
-    /**
      * Requests feedback interruption from all accessibility services.
      */
     public void interrupt() {
@@ -697,7 +683,6 @@
      *                for a callback on the process's main handler.
      * @hide
      */
-    @TestApi
     public void addAccessibilityServicesStateChangeListener(
             @NonNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler) {
         synchronized (mLock) {
@@ -713,7 +698,6 @@
      *
      * @hide
      */
-    @TestApi
     public void removeAccessibilityServicesStateChangeListener(
             @NonNull AccessibilityServicesStateChangeListener listener) {
         // Final CopyOnWriteArrayList - no lock needed.
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 9a99e53..4c47b89 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1057,6 +1057,23 @@
     }
 
     /**
+     * TODO(b/67867469):
+     * - proper javadoc
+     * - mention this method in other places
+     * - unhide / remove testApi
+     * @hide
+     */
+    @TestApi
+    public boolean isFieldClassificationEnabled() {
+        try {
+            return mService.isFieldClassificationEnabled();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
+
+    /**
      * Returns {@code true} if autofill is supported by the current device and
      * is supported for this user.
      *
diff --git a/core/java/android/view/autofill/Helper.java b/core/java/android/view/autofill/Helper.java
index b95704a..4b2c53c 100644
--- a/core/java/android/view/autofill/Helper.java
+++ b/core/java/android/view/autofill/Helper.java
@@ -18,11 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.Bundle;
-
-import java.util.Arrays;
-import java.util.Objects;
-import java.util.Set;
 
 /** @hide */
 public final class Helper {
@@ -31,37 +26,20 @@
     public static boolean sDebug = false;
     public static boolean sVerbose = false;
 
-    public static final String REDACTED = "[REDACTED]";
-
-    static StringBuilder append(StringBuilder builder, Bundle bundle) {
-        if (bundle == null || !sDebug) {
-            builder.append("N/A");
-        } else if (!sVerbose) {
-            builder.append(REDACTED);
-        } else {
-            final Set<String> keySet = bundle.keySet();
-            builder.append("[Bundle with ").append(keySet.size()).append(" extras:");
-            for (String key : keySet) {
-                final Object value = bundle.get(key);
-                builder.append(' ').append(key).append('=');
-                builder.append((value instanceof Object[])
-                        ? Arrays.toString((Objects[]) value) : value);
-            }
-            builder.append(']');
-        }
-        return builder;
-    }
-
     /**
      * Appends {@code value} to the {@code builder} redacting its contents.
      */
     public static void appendRedacted(@NonNull StringBuilder builder,
             @Nullable CharSequence value) {
-        if (value == null) {
-            builder.append("null");
-        } else {
-            builder.append(value.length()).append("_chars");
-        }
+        builder.append(getRedacted(value));
+    }
+
+    /**
+     * Gets the redacted version of a value.
+     */
+    @NonNull
+    public static String getRedacted(@Nullable CharSequence value) {
+        return (value == null) ? "null" : value.length() + "_chars";
     }
 
     /**
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 7d6a19f..f49aa5b 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -56,4 +56,5 @@
     void onPendingSaveUi(int operation, IBinder token);
     UserData getUserData();
     void setUserData(in UserData userData);
+    boolean isFieldClassificationEnabled();
 }
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index f0645b8..c69543f 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -67,6 +67,11 @@
     final ResolveInfo mService;
 
     /**
+     * IME only supports VR mode.
+     */
+    final boolean mIsVrOnly;
+
+    /**
      * The unique string Id to identify the input method.  This is generated
      * from the input method component.
      */
@@ -149,6 +154,7 @@
 
         PackageManager pm = context.getPackageManager();
         String settingsActivityComponent = null;
+        boolean isVrOnly;
         int isDefaultResId = 0;
 
         XmlResourceParser parser = null;
@@ -179,6 +185,7 @@
                     com.android.internal.R.styleable.InputMethod);
             settingsActivityComponent = sa.getString(
                     com.android.internal.R.styleable.InputMethod_settingsActivity);
+            isVrOnly = sa.getBoolean(com.android.internal.R.styleable.InputMethod_isVrOnly, false);
             isDefaultResId = sa.getResourceId(
                     com.android.internal.R.styleable.InputMethod_isDefault, 0);
             supportsSwitchingToNextInputMethod = sa.getBoolean(
@@ -254,6 +261,8 @@
         mIsDefaultResId = isDefaultResId;
         mIsAuxIme = isAuxIme;
         mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
+        // TODO(b/68948291): remove this meta-data before release.
+        mIsVrOnly = isVrOnly || service.serviceInfo.metaData.getBoolean("isVrOnly", false);
     }
 
     InputMethodInfo(Parcel source) {
@@ -262,6 +271,7 @@
         mIsDefaultResId = source.readInt();
         mIsAuxIme = source.readInt() == 1;
         mSupportsSwitchingToNextInputMethod = source.readInt() == 1;
+        mIsVrOnly = source.readBoolean();
         mService = ResolveInfo.CREATOR.createFromParcel(source);
         mSubtypes = new InputMethodSubtypeArray(source);
         mForceDefault = false;
@@ -274,7 +284,8 @@
             CharSequence label, String settingsActivity) {
         this(buildDummyResolveInfo(packageName, className, label), false /* isAuxIme */,
                 settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
-                false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */);
+                false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
+                false /* isVrOnly */);
     }
 
     /**
@@ -285,7 +296,7 @@
             String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
             boolean forceDefault) {
         this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
-                true /* supportsSwitchingToNextInputMethod */);
+                true /* supportsSwitchingToNextInputMethod */, false /* isVrOnly */);
     }
 
     /**
@@ -294,7 +305,7 @@
      */
     public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
             List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
-            boolean supportsSwitchingToNextInputMethod) {
+            boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) {
         final ServiceInfo si = ri.serviceInfo;
         mService = ri;
         mId = new ComponentName(si.packageName, si.name).flattenToShortString();
@@ -304,6 +315,7 @@
         mSubtypes = new InputMethodSubtypeArray(subtypes);
         mForceDefault = forceDefault;
         mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
+        mIsVrOnly = isVrOnly;
     }
 
     private static ResolveInfo buildDummyResolveInfo(String packageName, String className,
@@ -398,6 +410,14 @@
     }
 
     /**
+     * Returns true if IME supports VR mode only.
+     * @hide
+     */
+    public boolean isVrOnly() {
+        return mIsVrOnly;
+    }
+
+    /**
      * Return the count of the subtypes of Input Method.
      */
     public int getSubtypeCount() {
@@ -444,6 +464,7 @@
     public void dump(Printer pw, String prefix) {
         pw.println(prefix + "mId=" + mId
                 + " mSettingsActivityName=" + mSettingsActivityName
+                + " mIsVrOnly=" + mIsVrOnly
                 + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod);
         pw.println(prefix + "mIsDefaultResId=0x"
                 + Integer.toHexString(mIsDefaultResId));
@@ -509,6 +530,7 @@
         dest.writeInt(mIsDefaultResId);
         dest.writeInt(mIsAuxIme ? 1 : 0);
         dest.writeInt(mSupportsSwitchingToNextInputMethod ? 1 : 0);
+        dest.writeBoolean(mIsVrOnly);
         mService.writeToParcel(dest, flags);
         mSubtypes.writeToParcel(dest);
     }
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index b379280..4d3189e 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1952,8 +1952,7 @@
             CharSequence beforeText = mInputText.getText();
             if (!text.equals(beforeText.toString())) {
                 mInputText.setText(text);
-                if (AccessibilityManager.getInstance(mContext).isObservedEventType(
-                        AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)) {
+                if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
                     mInputText.onInitializeAccessibilityEvent(event);
@@ -2613,7 +2612,7 @@
         }
 
         private void sendAccessibilityEventForVirtualText(int eventType) {
-            if (AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                 AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
                 mInputText.onInitializeAccessibilityEvent(event);
                 mInputText.onPopulateAccessibilityEvent(event);
@@ -2624,7 +2623,7 @@
 
         private void sendAccessibilityEventForVirtualButton(int virtualViewId, int eventType,
                 String text) {
-            if (AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
                 AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
                 event.setClassName(Button.class.getName());
                 event.setPackageName(mContext.getPackageName());
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 71532a7..d9bc51f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10836,10 +10836,6 @@
 
     void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText,
             int fromIndex, int removedCount, int addedCount) {
-        if (!AccessibilityManager.getInstance(mContext).isObservedEventType(
-                AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)) {
-            return;
-        }
         AccessibilityEvent event =
                 AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
         event.setFromIndex(fromIndex);
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index bfde6ac..d807120 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -504,8 +504,7 @@
         private void trySendAccessibilityEvent() {
             AccessibilityManager accessibilityManager =
                     AccessibilityManager.getInstance(mView.getContext());
-            if (!accessibilityManager.isObservedEventType(
-                    AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)) {
+            if (!accessibilityManager.isEnabled()) {
                 return;
             }
             // treat toasts as notifications since they are used to
diff --git a/services/core/java/com/android/server/policy/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
similarity index 62%
rename from services/core/java/com/android/server/policy/AccessibilityShortcutController.java
rename to core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 55c582e..293471c 100644
--- a/services/core/java/com/android/server/policy/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Google Inc.
+ * Copyright 2017 Google Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.server.policy;
+package com.android.internal.accessibility;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.ActivityManager;
@@ -35,6 +35,7 @@
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Slog;
 import android.view.Window;
 import android.view.WindowManager;
@@ -43,20 +44,30 @@
 import android.widget.Toast;
 import com.android.internal.R;
 
-import java.util.List;
+import java.util.Collections;
+import java.util.Map;
 
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
 
+import static com.android.internal.util.ArrayUtils.convertToLongArray;
+
 /**
  * Class to help manage the accessibility shortcut
  */
 public class AccessibilityShortcutController {
     private static final String TAG = "AccessibilityShortcutController";
+
+    // Dummy component names for framework features
+    public static final ComponentName COLOR_INVERSION_COMPONENT_NAME =
+            new ComponentName("com.android.server.accessibility", "ColorInversion");
+    public static final ComponentName DALTONIZER_COMPONENT_NAME =
+            new ComponentName("com.android.server.accessibility", "Daltonizer");
+
     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
             .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY)
             .build();
-
+    private static Map<ComponentName, ToggleableFrameworkFeatureInfo> sFrameworkShortcutFeaturesMap;
 
     private final Context mContext;
     private AlertDialog mAlertDialog;
@@ -67,6 +78,15 @@
     // Visible for testing
     public FrameworkObjectProvider mFrameworkObjectProvider = new FrameworkObjectProvider();
 
+    /**
+     * Get the component name string for the service or feature currently assigned to the
+     * accessiblity shortcut
+     *
+     * @param context A valid context
+     * @param userId The user ID of interest
+     * @return The flattened component name string of the service selected by the user, or the
+     *         string for the default service if the user has not made a selection
+     */
     public static String getTargetServiceComponentNameString(
             Context context, int userId) {
         final String currentShortcutServiceId = Settings.Secure.getStringForUser(
@@ -78,6 +98,29 @@
         return context.getString(R.string.config_defaultAccessibilityService);
     }
 
+    /**
+     * @return An immutable map from dummy component names to feature info for toggling a framework
+     *         feature
+     */
+    public static Map<ComponentName, ToggleableFrameworkFeatureInfo>
+        getFrameworkShortcutFeaturesMap() {
+        if (sFrameworkShortcutFeaturesMap == null) {
+            Map<ComponentName, ToggleableFrameworkFeatureInfo> featuresMap = new ArrayMap<>(2);
+            featuresMap.put(COLOR_INVERSION_COMPONENT_NAME,
+                    new ToggleableFrameworkFeatureInfo(
+                            Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
+                            "1" /* Value to enable */, "0" /* Value to disable */,
+                            R.string.color_inversion_feature_name));
+            featuresMap.put(DALTONIZER_COMPONENT_NAME,
+                    new ToggleableFrameworkFeatureInfo(
+                            Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
+                            "1" /* Value to enable */, "0" /* Value to disable */,
+                            R.string.color_correction_feature_name));
+            sFrameworkShortcutFeaturesMap = Collections.unmodifiableMap(featuresMap);
+        }
+        return sFrameworkShortcutFeaturesMap;
+    }
+
     public AccessibilityShortcutController(Context context, Handler handler, int initialUserId) {
         mContext = context;
         mUserId = initialUserId;
@@ -160,8 +203,8 @@
         if ((vibrator != null) && vibrator.hasVibrator()) {
             // Don't check if haptics are disabled, as we need to alert the user that their
             // way of interacting with the phone may change if they activate the shortcut
-            long[] vibePattern = PhoneWindowManager.getLongIntArray(mContext.getResources(),
-                    R.array.config_longPressVibePattern);
+            long[] vibePattern = convertToLongArray(
+                    mContext.getResources().getIntArray(R.array.config_longPressVibePattern));
             vibrator.vibrate(vibePattern, -1, VIBRATION_ATTRIBUTES);
         }
 
@@ -187,22 +230,24 @@
             }
 
             // Show a toast alerting the user to what's happening
-            final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
-            if (serviceInfo == null) {
+            final String serviceName = getShortcutFeatureDescription(false /* no summary */);
+            if (serviceName == null) {
                 Slog.e(TAG, "Accessibility shortcut set to invalid service");
                 return;
             }
-            String toastMessageFormatString = mContext.getString(isServiceEnabled(serviceInfo)
-                    ? R.string.accessibility_shortcut_disabling_service
-                    : R.string.accessibility_shortcut_enabling_service);
-            String toastMessage = String.format(toastMessageFormatString,
-                    serviceInfo.getResolveInfo()
-                            .loadLabel(mContext.getPackageManager()).toString());
-            Toast warningToast = mFrameworkObjectProvider.makeToastFromText(
-                    mContext, toastMessage, Toast.LENGTH_LONG);
-            warningToast.getWindowParams().privateFlags |=
-                    WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
-            warningToast.show();
+            // For accessibility services, show a toast explaining what we're doing.
+            final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
+            if (serviceInfo != null) {
+                String toastMessageFormatString = mContext.getString(isServiceEnabled(serviceInfo)
+                        ? R.string.accessibility_shortcut_disabling_service
+                        : R.string.accessibility_shortcut_enabling_service);
+                String toastMessage = String.format(toastMessageFormatString, serviceName);
+                Toast warningToast = mFrameworkObjectProvider.makeToastFromText(
+                        mContext, toastMessage, Toast.LENGTH_LONG);
+                warningToast.getWindowParams().privateFlags |=
+                        WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                warningToast.show();
+            }
 
             mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext)
                     .performAccessibilityShortcut();
@@ -210,18 +255,18 @@
     }
 
     private AlertDialog createShortcutWarningDialog(int userId) {
-        final AccessibilityServiceInfo serviceInfo = getInfoForTargetService();
+        final String serviceDescription = getShortcutFeatureDescription(true /* Include summary */);
 
-        if (serviceInfo == null) {
+        if (serviceDescription == null) {
             return null;
         }
 
         final String warningMessage = String.format(
                 mContext.getString(R.string.accessibility_shortcut_toogle_warning),
-                serviceInfo.getResolveInfo().loadLabel(mContext.getPackageManager()).toString());
+                serviceDescription);
         final AlertDialog alertDialog = mFrameworkObjectProvider.getAlertDialogBuilder(
                 // Use SystemUI context so we pick up any theme set in a vendor overlay
-                ActivityThread.currentActivityThread().getSystemUiContext())
+                mFrameworkObjectProvider.getSystemUiContext())
                 .setTitle(R.string.accessibility_shortcut_warning_dialog_title)
                 .setMessage(warningMessage)
                 .setCancelable(false)
@@ -253,6 +298,34 @@
                         ComponentName.unflattenFromString(currentShortcutServiceString));
     }
 
+    private String getShortcutFeatureDescription(boolean includeSummary) {
+        final String currentShortcutServiceString = getTargetServiceComponentNameString(
+                mContext, UserHandle.USER_CURRENT);
+        if (currentShortcutServiceString == null) {
+            return null;
+        }
+        final ComponentName targetComponentName =
+                ComponentName.unflattenFromString(currentShortcutServiceString);
+        final ToggleableFrameworkFeatureInfo frameworkFeatureInfo =
+                getFrameworkShortcutFeaturesMap().get(targetComponentName);
+        if (frameworkFeatureInfo != null) {
+            return frameworkFeatureInfo.getLabel(mContext);
+        }
+        final AccessibilityServiceInfo serviceInfo = mFrameworkObjectProvider
+                .getAccessibilityManagerInstance(mContext).getInstalledServiceInfoWithComponentName(
+                        targetComponentName);
+        if (serviceInfo == null) {
+            return null;
+        }
+        final PackageManager pm = mContext.getPackageManager();
+        String label = serviceInfo.getResolveInfo().loadLabel(pm).toString();
+        String summary = serviceInfo.loadSummary(pm).toString();
+        if (!includeSummary || TextUtils.isEmpty(summary)) {
+            return label;
+        }
+        return String.format("%s\n%s", label, summary);
+    }
+
     private boolean isServiceEnabled(AccessibilityServiceInfo serviceInfo) {
         AccessibilityManager accessibilityManager =
                 mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext);
@@ -264,6 +337,51 @@
         return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
     }
 
+    /**
+     * Immutable class to hold info about framework features that can be controlled by shortcut
+     */
+    public static class ToggleableFrameworkFeatureInfo {
+        private final String mSettingKey;
+        private final String mSettingOnValue;
+        private final String mSettingOffValue;
+        private final int mLabelStringResourceId;
+        // These go to the settings wrapper
+        private int mIconDrawableId;
+
+        ToggleableFrameworkFeatureInfo(String settingKey, String settingOnValue,
+                String settingOffValue, int labelStringResourceId) {
+            mSettingKey = settingKey;
+            mSettingOnValue = settingOnValue;
+            mSettingOffValue = settingOffValue;
+            mLabelStringResourceId = labelStringResourceId;
+        }
+
+        /**
+         * @return The settings key to toggle between two values
+         */
+        public String getSettingKey() {
+            return mSettingKey;
+        }
+
+        /**
+         * @return The value to write to settings to turn the feature on
+         */
+        public String getSettingOnValue() {
+            return mSettingOnValue;
+        }
+
+        /**
+         * @return The value to write to settings to turn the feature off
+         */
+        public String getSettingOffValue() {
+            return mSettingOffValue;
+        }
+
+        public String getLabel(Context context) {
+            return context.getString(mLabelStringResourceId);
+        }
+    }
+
     // Class to allow mocking of static framework calls
     public static class FrameworkObjectProvider {
         public AccessibilityManager getAccessibilityManagerInstance(Context context) {
@@ -277,5 +395,9 @@
         public Toast makeToastFromText(Context context, CharSequence charSequence, int duration) {
             return Toast.makeText(context, charSequence, duration);
         }
+
+        public Context getSystemUiContext() {
+            return ActivityThread.currentActivityThread().getSystemUiContext();
+        }
     }
 }
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index 0a539f1..8016a65 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -111,14 +111,7 @@
     @Override
     public void onClick(DialogInterface dialog, int which) {
         if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE && which == DialogInterface.BUTTON_POSITIVE) {
-            if (UserManager.get(this).trySetQuietModeDisabled(mUserId, mTarget)
-                    && mTarget != null) {
-                try {
-                    startIntentSenderForResult(mTarget, -1, null, 0, 0, 0);
-                } catch (IntentSender.SendIntentException e) {
-                    /* ignore */
-                }
-            }
+            UserManager.get(this).trySetQuietModeDisabled(mUserId, mTarget);
         }
     }
 
diff --git a/core/java/com/android/internal/os/BackgroundThread.java b/core/java/com/android/internal/os/BackgroundThread.java
index cffba01..7558f8c 100644
--- a/core/java/com/android/internal/os/BackgroundThread.java
+++ b/core/java/com/android/internal/os/BackgroundThread.java
@@ -35,7 +35,7 @@
         if (sInstance == null) {
             sInstance = new BackgroundThread();
             sInstance.start();
-            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
             sHandler = new Handler(sInstance.getLooper());
         }
     }
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 9336ddd..aa85668 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -292,6 +292,15 @@
         return array;
     }
 
+    public static @Nullable long[] convertToLongArray(@Nullable int[] intArray) {
+        if (intArray == null) return null;
+        long[] array = new long[intArray.length];
+        for (int i = 0; i < intArray.length; i++) {
+            array[i] = (long) intArray[i];
+        }
+        return array;
+    }
+
     /**
      * Adds value to given array if not already present, providing set-like
      * behavior.
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/java/com/android/internal/widget/ExploreByTouchHelper.java b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
index 759a41a..50ad547 100644
--- a/core/java/com/android/internal/widget/ExploreByTouchHelper.java
+++ b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
@@ -186,9 +186,6 @@
         }
 
         final AccessibilityEvent event = createEvent(virtualViewId, eventType);
-        if (event == null) {
-            return false;
-        }
         return parent.requestSendAccessibilityEvent(mView, event);
     }
 
@@ -243,9 +240,6 @@
             if (parent != null) {
                 final AccessibilityEvent event = createEvent(virtualViewId,
                         AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
-                if (event == null) {
-                    return;
-                }
                 event.setContentChangeTypes(changeTypes);
                 parent.requestSendAccessibilityEvent(mView, event);
             }
@@ -311,9 +305,6 @@
      *         the specified item.
      */
     private AccessibilityEvent createEventForHost(int eventType) {
-        if (!AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
-            return null;
-        }
         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
         mView.onInitializeAccessibilityEvent(event);
 
@@ -334,9 +325,6 @@
      *         the specified item.
      */
     private AccessibilityEvent createEventForChild(int virtualViewId, int eventType) {
-        if (!AccessibilityManager.getInstance(mContext).isObservedEventType(eventType)) {
-            return null;
-        }
         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
         event.setEnabled(true);
         event.setClassName(DEFAULT_CLASS_NAME);
diff --git a/core/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_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 71742cb..0a90b97 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -124,7 +124,7 @@
 static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
         jclass clazz, jstring nameObj) {
     const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
-    String8 name(nameChars);
+    std::string name = nameChars;
     env->ReleaseStringUTFChars(nameObj, nameChars);
 
     sp<InputChannel> serverChannel;
@@ -166,7 +166,7 @@
     if (nativeInputChannel) {
         if (finalized) {
             ALOGW("Input channel object '%s' was finalized without being disposed!",
-                    nativeInputChannel->getInputChannel()->getName().string());
+                    nativeInputChannel->getInputChannel()->getName().c_str());
         }
 
         nativeInputChannel->invokeAndRemoveDisposeCallback(env, obj);
@@ -212,7 +212,7 @@
                 return;
             }
 
-            InputChannel* inputChannel = new InputChannel(name, dupFd);
+            InputChannel* inputChannel = new InputChannel(name.string(), dupFd);
             NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
 
             android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel);
@@ -230,7 +230,7 @@
             sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
 
             parcel->writeInt32(1);
-            parcel->writeString8(inputChannel->getName());
+            parcel->writeString8(String8(inputChannel->getName().c_str()));
             parcel->writeDupFileDescriptor(inputChannel->getFd());
         } else {
             parcel->writeInt32(0);
@@ -245,7 +245,7 @@
         return NULL;
     }
 
-    jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().string());
+    jstring name = env->NewStringUTF(nativeInputChannel->getInputChannel()->getName().c_str());
     return name;
 }
 
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index c457ab0..52c84a4 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -79,7 +79,7 @@
     void setFdEvents(int events);
 
     const char* getInputChannelName() {
-        return mInputConsumer.getChannel()->getName().string();
+        return mInputConsumer.getChannel()->getName().c_str();
     }
 
     virtual int handleEvent(int receiveFd, int events, void* data);
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 58ccef18..f87abac 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -71,7 +71,7 @@
     uint32_t mNextPublishedSeq;
 
     const char* getInputChannelName() {
-        return mInputPublisher.getChannel()->getName().string();
+        return mInputPublisher.getChannel()->getName().c_str();
     }
 
     virtual int handleEvent(int receiveFd, int events, void* data);
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 519a885..9f3475a 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -937,6 +937,11 @@
     Properties::enableHighContrastText = enable;
 }
 
+static void android_view_ThreadedRenderer_hackySetRTAnimationsEnabled(JNIEnv*, jclass,
+        jboolean enable) {
+    Properties::enableRTAnimations = enable;
+}
+
 // ----------------------------------------------------------------------------
 // FrameMetricsObserver
 // ----------------------------------------------------------------------------
@@ -1041,6 +1046,8 @@
             (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode },
     { "disableVsync", "()V", (void*)android_view_ThreadedRenderer_disableVsync },
     { "nSetHighContrastText", "(Z)V", (void*)android_view_ThreadedRenderer_setHighContrastText },
+    { "nHackySetRTAnimationsEnabled", "(Z)V",
+            (void*)android_view_ThreadedRenderer_hackySetRTAnimationsEnabled },
 };
 
 static JavaVM* mJvm = nullptr;
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3b49d9d..51ffa60 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3294,6 +3294,9 @@
              and subtype in order to provide the consistent user experience in switching
              between IMEs and subtypes. -->
         <attr name="supportsSwitchingToNextInputMethod" format="boolean" />
+        <!-- Specifies if an IME can only be used while a device is in VR mode or on a dedicated
+             device -->
+        <attr name="isVrOnly" format="boolean"/>
         <attr name="__removed2" format="boolean" />
     </declare-styleable>
 
@@ -7568,6 +7571,15 @@
             <flag name="keyguard" value="0x2" />
             <flag name="searchbox" value="0x4" />
         </attr>
+        <!-- Flags indicating various features supported by the widget. These are hints to the
+         widget host, and do not actually change the behavior of the widget. -->
+        <attr name="widgetFeatures" format="integer">
+            <!-- The widget can be reconfigured anytime after it is bound -->
+            <flag name="reconfigurable" value="0x1" />
+            <!-- The widget is added directly by the app, and does not need to appear in
+                 the global list of available widgets -->
+            <flag name="hide_from_picker" value="0x2" />
+        </attr>
     </declare-styleable>
 
     <!-- =============================== -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 43e1f73..373a656 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2856,6 +2856,9 @@
       <public name="buttonCornerRadius" />
       <public name="versionCodeMajor" />
       <public name="versionMajor" />
+      <!-- @hide @SystemApi -->
+      <public name="isVrOnly"/>
+      <public name="widgetFeatures" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8958af4..ef8f6af 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4034,6 +4034,14 @@
     shortcut enabled.-->
     <string name="leave_accessibility_shortcut_on">Use Shortcut</string>
 
+    <!-- Title of Color Inversion feature shown in the warning dialog about the accessibility
+    shortcut. -->
+    <string name="color_inversion_feature_name">Color Inversion</string>
+
+    <!-- Title of Color Correction feature, which is mostly used to help users who are colorblind,
+     shown in the warning dialog about the accessibility shortcut. -->
+    <string name="color_correction_feature_name">Color Correction</string>
+
     <!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility
     service.-->
     <string name="accessibility_shortcut_enabling_service">Accessibility Shortcut turned
@@ -4466,6 +4474,9 @@
     <!-- Zen mode - name of default automatic calendar event-based rule. [CHAR LIMIT=40] -->
     <string name="zen_mode_default_events_name">Event</string>
 
+    <!-- Zen mode - name of default automatic calendar time-based rule that is triggered every night (when sleeping). [CHAR LIMIT=40] -->
+    <string name="zen_mode_default_every_night_name">Sleeping</string>
+
     <!-- Indication that the current volume and other effects (vibration) are being suppressed by a third party, such as a notification listener. [CHAR LIMIT=30] -->
     <string name="muted_by">Muted by <xliff:g id="third_party">%1$s</xliff:g></string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cbd34f9..6da24bd 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1067,6 +1067,8 @@
   <java-symbol type="string" name="action_bar_home_description_format" />
   <java-symbol type="string" name="action_bar_home_subtitle_description_format" />
   <java-symbol type="string" name="wireless_display_route_description" />
+  <java-symbol type="string" name="user_owner_label" />
+  <java-symbol type="string" name="managed_profile_label" />
   <java-symbol type="string" name="managed_profile_label_badge" />
   <java-symbol type="string" name="managed_profile_label_badge_2" />
   <java-symbol type="string" name="managed_profile_label_badge_3" />
@@ -2262,6 +2264,7 @@
   <java-symbol type="string" name="zen_mode_default_weeknights_name" />
   <java-symbol type="string" name="zen_mode_default_weekends_name" />
   <java-symbol type="string" name="zen_mode_default_events_name" />
+  <java-symbol type="string" name="zen_mode_default_every_night_name" />
   <java-symbol type="array" name="config_system_condition_providers" />
   <java-symbol type="string" name="muted_by" />
   <java-symbol type="string" name="zen_mode_alarm" />
@@ -2911,6 +2914,8 @@
   <java-symbol type="string" name="accessibility_shortcut_disabling_service" />
   <java-symbol type="string" name="disable_accessibility_shortcut" />
   <java-symbol type="string" name="leave_accessibility_shortcut_on" />
+  <java-symbol type="string" name="color_inversion_feature_name" />
+  <java-symbol type="string" name="color_correction_feature_name" />
   <java-symbol type="string" name="config_defaultAccessibilityService" />
 
   <!-- Accessibility Button -->
diff --git a/core/tests/BroadcastRadioTests/Android.mk b/core/tests/BroadcastRadioTests/Android.mk
index c409e3a..8df3827 100644
--- a/core/tests/BroadcastRadioTests/Android.mk
+++ b/core/tests/BroadcastRadioTests/Android.mk
@@ -26,6 +26,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test testng
 
+LOCAL_JAVA_LIBRARIES := android.test.base
+
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index f2eb872..47990a1 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -30,6 +30,7 @@
 LOCAL_JACK_FLAGS := --multi-dex native
 LOCAL_AAPT_FLAGS = -0 dat -0 gld -c fa
 LOCAL_STATIC_JAVA_LIBRARIES := \
+    frameworks-base-testutils \
     core-tests-support \
     android-common \
     frameworks-core-util-lib \
@@ -44,7 +45,14 @@
     truth-prebuilt \
     print-test-util-lib
 
-LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt telephony-common org.apache.http.legacy
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner \
+    conscrypt \
+    telephony-common \
+    org.apache.http.legacy \
+    android.test.base \
+    android.test.mock \
+
 LOCAL_PACKAGE_NAME := FrameworksCoreTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
 
diff --git a/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttf b/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttf
new file mode 100644
index 0000000..1bad6fe
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttx b/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttx
new file mode 100644
index 0000000..0cf0f79
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttx
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="0em"/>
+    <GlyphID id="2" name="1em"/>
+    <GlyphID id="3" name="3em"/>
+    <GlyphID id="4" name="5em"/>
+    <GlyphID id="5" name="7em"/>
+    <GlyphID id="6" name="10em"/>
+    <GlyphID id="7" name="50em"/>
+    <GlyphID id="8" name="100em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="100"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="0x00010000"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="50" lsb="0"/>
+    <mtx name="0em" width="0" lsb="0"/>
+    <mtx name="1em" width="100" lsb="0"/>
+    <mtx name="3em" width="300" lsb="0"/>
+    <mtx name="5em" width="500" lsb="0"/>
+    <mtx name="7em" width="700" lsb="0"/>
+    <mtx name="10em" width="1000" lsb="0"/>
+    <mtx name="50em" width="5000" lsb="0"/>
+    <mtx name="100em" width="10000" lsb="0"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_12 format="12" reserved="0" length="6" nGroups="1" platformID="3" platEncID="10" language="0">
+      <map code="0x0020" name="10em" />
+      <map code="0x002e" name="10em" />  <!-- . -->
+      <map code="0x0043" name="100em" />  <!-- C -->
+      <map code="0x0049" name="1em" />  <!-- I -->
+      <map code="0x004c" name="50em" />  <!-- L -->
+      <map code="0x0056" name="5em" />  <!-- V -->
+      <map code="0x0058" name="10em" />  <!-- X -->
+      <map code="0x005f" name="0em" /> <!-- _ -->
+      <map code="0xfffd" name="7em" /> <!-- REPLACEMENT CHAR -->
+      <map code="0x10331" name="10em" />
+    </cmap_format_12>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="0em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="5em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="7em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="10em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="50em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="100em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Font for StaticLayoutLineBreakingTest
+    </namerecord>
+    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      Font for StaticLayoutLineBreakingTest
+    </namerecord>
+    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/res/xml/ime_meta_vr_only.xml b/core/tests/coretests/res/xml/ime_meta_vr_only.xml
new file mode 100644
index 0000000..653a8ff
--- /dev/null
+++ b/core/tests/coretests/res/xml/ime_meta_vr_only.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+    Copyright (C) 2017 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<input-method
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity"
+    android:isVrOnly="true">
+</input-method>
diff --git a/core/tests/coretests/src/android/content/pm/crossprofile/CrossProfileAppsTest.java b/core/tests/coretests/src/android/content/pm/crossprofile/CrossProfileAppsTest.java
new file mode 100644
index 0000000..80e4c02
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/crossprofile/CrossProfileAppsTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm.crossprofile;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.internal.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Build/Install/Run:
+ * bit FrameworksCoreTests:android.content.pm.crossprofile.CrossProfileAppsTest
+ */
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public class CrossProfileAppsTest {
+    private static final UserHandle PERSONAL_PROFILE = UserHandle.of(0);
+    private static final UserHandle MANAGED_PROFILE = UserHandle.of(10);
+    private static final String MY_PACKAGE = "my.package";
+
+    private List<UserHandle> mTargetProfiles;
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private ICrossProfileApps mService;
+    @Mock
+    private Resources mResources;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Drawable mDrawable;
+    private CrossProfileApps mCrossProfileApps;
+
+    @Before
+    public void initCrossProfileApps() {
+        mCrossProfileApps = new CrossProfileApps(mContext, mService);
+    }
+
+    @Before
+    public void mockContext() {
+        when(mContext.getPackageName()).thenReturn(MY_PACKAGE);
+        when(mContext.getSystemServiceName(UserManager.class)).thenReturn(Context.USER_SERVICE);
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+    }
+
+    @Before
+    public void mockResources() {
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.getDrawable(anyInt(), nullable(Resources.Theme.class)))
+                .thenReturn(mDrawable);
+    }
+
+    @Before
+    public void initUsers() throws Exception {
+        when(mUserManager.isManagedProfile(PERSONAL_PROFILE.getIdentifier())).thenReturn(false);
+        when(mUserManager.isManagedProfile(MANAGED_PROFILE.getIdentifier())).thenReturn(true);
+
+        mTargetProfiles = new ArrayList<>();
+        when(mService.getTargetUserProfiles(MY_PACKAGE)).thenReturn(mTargetProfiles);
+    }
+
+    @Test
+    public void getProfileSwitchingLabel_managedProfile() {
+        setValidTargetProfile(MANAGED_PROFILE);
+
+        mCrossProfileApps.getProfileSwitchingLabel(MANAGED_PROFILE);
+        verify(mResources).getString(R.string.managed_profile_label);
+    }
+
+    @Test
+    public void getProfileSwitchingLabel_personalProfile() {
+        setValidTargetProfile(PERSONAL_PROFILE);
+
+        mCrossProfileApps.getProfileSwitchingLabel(PERSONAL_PROFILE);
+        verify(mResources).getString(R.string.user_owner_label);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void getProfileSwitchingLabel_securityException() {
+        mCrossProfileApps.getProfileSwitchingLabel(PERSONAL_PROFILE);
+    }
+
+    @Test
+    public void getProfileSwitchingIcon_managedProfile() {
+        setValidTargetProfile(MANAGED_PROFILE);
+
+        mCrossProfileApps.getProfileSwitchingIcon(MANAGED_PROFILE);
+        verify(mResources).getDrawable(R.drawable.ic_corp_badge, null);
+    }
+
+    @Test
+    public void getProfileSwitchingIcon_personalProfile() {
+        setValidTargetProfile(PERSONAL_PROFILE);
+
+        mCrossProfileApps.getProfileSwitchingIcon(PERSONAL_PROFILE);
+        verify(mResources).getDrawable(R.drawable.ic_account_circle, null);
+    }
+
+    @Test(expected = SecurityException.class)
+    public void getProfileSwitchingIcon_securityException() {
+        mCrossProfileApps.getProfileSwitchingIcon(PERSONAL_PROFILE);
+    }
+
+    private void setValidTargetProfile(UserHandle userHandle) {
+        mTargetProfiles.add(userHandle);
+    }
+}
diff --git a/core/tests/coretests/src/android/os/EnvironmentTest.java b/core/tests/coretests/src/android/os/EnvironmentTest.java
new file mode 100644
index 0000000..5189df5
--- /dev/null
+++ b/core/tests/coretests/src/android/os/EnvironmentTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static android.os.Environment.HAS_ANDROID;
+import static android.os.Environment.HAS_DCIM;
+import static android.os.Environment.HAS_DOWNLOADS;
+import static android.os.Environment.HAS_OTHER;
+import static android.os.Environment.classifyExternalStorageDirectory;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@RunWith(AndroidJUnit4.class)
+public class EnvironmentTest {
+    private File dir;
+
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        dir = getContext().getDir("testing", Context.MODE_PRIVATE);
+        FileUtils.deleteContents(dir);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(dir);
+    }
+
+    @Test
+    public void testClassify_empty() {
+        assertEquals(0, classifyExternalStorageDirectory(dir));
+    }
+
+    @Test
+    public void testClassify_emptyDirs() {
+        Environment.buildPath(dir, "DCIM").mkdirs();
+        Environment.buildPath(dir, "DCIM", "January").mkdirs();
+        Environment.buildPath(dir, "Downloads").mkdirs();
+        Environment.buildPath(dir, "LOST.DIR").mkdirs();
+        assertEquals(0, classifyExternalStorageDirectory(dir));
+    }
+
+    @Test
+    public void testClassify_emptyFactory() throws Exception {
+        Environment.buildPath(dir, "autorun.inf").createNewFile();
+        Environment.buildPath(dir, "LaunchU3.exe").createNewFile();
+        Environment.buildPath(dir, "LaunchPad.zip").createNewFile();
+        assertEquals(0, classifyExternalStorageDirectory(dir));
+    }
+
+    @Test
+    public void testClassify_photos() throws Exception {
+        Environment.buildPath(dir, "DCIM").mkdirs();
+        Environment.buildPath(dir, "DCIM", "IMG_1024.JPG").createNewFile();
+        Environment.buildPath(dir, "Download").mkdirs();
+        Environment.buildPath(dir, "Download", "foobar.pdf").createNewFile();
+        assertEquals(HAS_DCIM | HAS_DOWNLOADS, classifyExternalStorageDirectory(dir));
+    }
+
+    @Test
+    public void testClassify_other() throws Exception {
+        Environment.buildPath(dir, "Android").mkdirs();
+        Environment.buildPath(dir, "Android", "com.example").mkdirs();
+        Environment.buildPath(dir, "Android", "com.example", "internal.dat").createNewFile();
+        Environment.buildPath(dir, "Linux").mkdirs();
+        Environment.buildPath(dir, "Linux", "install-amd64-minimal-20170907.iso").createNewFile();
+        assertEquals(HAS_ANDROID | HAS_OTHER, classifyExternalStorageDirectory(dir));
+    }
+
+    @Test
+    public void testClassify_otherRoot() throws Exception {
+        Environment.buildPath(dir, "Taxes.pdf").createNewFile();
+        assertEquals(HAS_OTHER, classifyExternalStorageDirectory(dir));
+    }
+}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 3a751af..cd20192 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -21,15 +21,24 @@
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.WEEK_IN_MILLIS;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import android.content.Context;
 import android.provider.DocumentsContract.Document;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.MediumTest;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
 
 import libcore.io.IoUtils;
 
 import com.google.android.collect.Sets;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -37,8 +46,8 @@
 import java.util.Arrays;
 import java.util.HashSet;
 
-@MediumTest
-public class FileUtilsTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class FileUtilsTest {
     private static final String TEST_DATA =
             "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 
@@ -47,10 +56,12 @@
     private File mCopyFile;
     private File mTarget;
 
+    private Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    @Before
+    public void setUp() throws Exception {
         mDir = getContext().getDir("testing", Context.MODE_PRIVATE);
         mTestFile = new File(mDir, "test.file");
         mCopyFile = new File(mDir, "copy.file");
@@ -59,14 +70,15 @@
         FileUtils.deleteContents(mTarget);
     }
 
-    @Override
-    protected void tearDown() throws Exception {
+    @After
+    public void tearDown() throws Exception {
         IoUtils.deleteContents(mDir);
         FileUtils.deleteContents(mTarget);
     }
 
     // TODO: test setPermissions(), getPermissions()
 
+    @Test
     public void testCopyFile() throws Exception {
         stageFile(mTestFile, TEST_DATA);
         assertFalse(mCopyFile.exists());
@@ -75,6 +87,7 @@
         assertEquals(TEST_DATA, FileUtils.readTextFile(mCopyFile, 0, null));
     }
 
+    @Test
     public void testCopyToFile() throws Exception {
         final String s = "Foo Bar";
         assertFalse(mCopyFile.exists());
@@ -83,6 +96,7 @@
         assertEquals(s, FileUtils.readTextFile(mCopyFile, 0, null));
     }
 
+    @Test
     public void testIsFilenameSafe() throws Exception {
         assertTrue(FileUtils.isFilenameSafe(new File("foobar")));
         assertTrue(FileUtils.isFilenameSafe(new File("a_b-c=d.e/0,1+23")));
@@ -90,6 +104,7 @@
         assertFalse(FileUtils.isFilenameSafe(new File("foo\nbar")));
     }
 
+    @Test
     public void testReadTextFile() throws Exception {
         stageFile(mTestFile, TEST_DATA);
 
@@ -110,6 +125,7 @@
         assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, -100, "<>"));
     }
 
+    @Test
     public void testReadTextFileWithZeroLengthFile() throws Exception {
         stageFile(mTestFile, TEST_DATA);
         new FileOutputStream(mTestFile).close();  // Zero out the file
@@ -120,6 +136,7 @@
         assertEquals("", FileUtils.readTextFile(mTestFile, -10, "<>"));
     }
 
+    @Test
     public void testContains() throws Exception {
         assertTrue(FileUtils.contains(new File("/"), new File("/moo.txt")));
         assertTrue(FileUtils.contains(new File("/"), new File("/")));
@@ -137,11 +154,13 @@
         assertFalse(FileUtils.contains(new File("/sdcard/"), new File("/sdcard.txt")));
     }
 
+    @Test
     public void testDeleteOlderEmptyDir() throws Exception {
         FileUtils.deleteOlderFiles(mDir, 10, WEEK_IN_MILLIS);
         assertDirContents();
     }
 
+    @Test
     public void testDeleteOlderTypical() throws Exception {
         touch("file1", HOUR_IN_MILLIS);
         touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
@@ -152,6 +171,7 @@
         assertDirContents("file1", "file2", "file3");
     }
 
+    @Test
     public void testDeleteOlderInFuture() throws Exception {
         touch("file1", -HOUR_IN_MILLIS);
         touch("file2", HOUR_IN_MILLIS);
@@ -166,6 +186,7 @@
         assertDirContents("file1", "file2");
     }
 
+    @Test
     public void testDeleteOlderOnlyAge() throws Exception {
         touch("file1", HOUR_IN_MILLIS);
         touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
@@ -177,6 +198,7 @@
         assertDirContents("file1");
     }
 
+    @Test
     public void testDeleteOlderOnlyCount() throws Exception {
         touch("file1", HOUR_IN_MILLIS);
         touch("file2", 1 * DAY_IN_MILLIS + HOUR_IN_MILLIS);
@@ -188,6 +210,7 @@
         assertDirContents("file1", "file2");
     }
 
+    @Test
     public void testValidExtFilename() throws Exception {
         assertTrue(FileUtils.isValidExtFilename("a"));
         assertTrue(FileUtils.isValidExtFilename("foo.bar"));
@@ -208,6 +231,7 @@
         assertEquals("foo.bar", FileUtils.buildValidExtFilename("foo.bar"));
     }
 
+    @Test
     public void testValidFatFilename() throws Exception {
         assertTrue(FileUtils.isValidFatFilename("a"));
         assertTrue(FileUtils.isValidFatFilename("foo bar.baz"));
@@ -233,6 +257,7 @@
         assertEquals("foo_bar__baz", FileUtils.buildValidFatFilename("foo?bar**baz"));
     }
 
+    @Test
     public void testTrimFilename() throws Exception {
         assertEquals("short.txt", FileUtils.trimFilename("short.txt", 16));
         assertEquals("extrem...eme.txt", FileUtils.trimFilename("extremelylongfilename.txt", 16));
@@ -245,6 +270,7 @@
         assertEquals("a...z", FileUtils.trimFilename(unicode, 6));
     }
 
+    @Test
     public void testBuildUniqueFile_normal() throws Exception {
         assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test"));
         assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
@@ -263,6 +289,7 @@
                 FileUtils.buildUniqueFile(mTarget, "application/x-flac", "test.flac"));
     }
 
+    @Test
     public void testBuildUniqueFile_unknown() throws Exception {
         assertNameEquals("test",
                 FileUtils.buildUniqueFile(mTarget, "application/octet-stream", "test"));
@@ -275,6 +302,7 @@
         assertNameEquals("test.lolz", FileUtils.buildUniqueFile(mTarget, "lolz/lolz", "test.lolz"));
     }
 
+    @Test
     public void testBuildUniqueFile_dir() throws Exception {
         assertNameEquals("test", FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
         new File(mTarget, "test").mkdir();
@@ -288,6 +316,7 @@
                 FileUtils.buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
     }
 
+    @Test
     public void testBuildUniqueFile_increment() throws Exception {
         assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
         new File(mTarget, "test.jpg").createNewFile();
@@ -298,6 +327,7 @@
                 FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
     }
 
+    @Test
     public void testBuildUniqueFile_mimeless() throws Exception {
         assertNameEquals("test.jpg", FileUtils.buildUniqueFile(mTarget, "test.jpg"));
         new File(mTarget, "test.jpg").createNewFile();
@@ -312,6 +342,7 @@
         assertNameEquals("test.foo (1).bar", FileUtils.buildUniqueFile(mTarget, "test.foo.bar"));
     }
 
+    @Test
     public void testRoundStorageSize() throws Exception {
         final long M128 = 128000000L;
         final long M256 = 256000000L;
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 0367275..77c6c3e 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -420,7 +420,7 @@
                  Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
                  // TODO(b/67867469): Move autofill settings below to
                  // BACKUP_BLACKLISTED_SYSTEM_SETTINGS once feature is moved out of experimental
-                 Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION,
+                 Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
                  Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
                  Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE,
                  Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH,
diff --git a/core/tests/coretests/src/android/text/MeasuredTextTest.java b/core/tests/coretests/src/android/text/MeasuredTextTest.java
new file mode 100644
index 0000000..ddef0c6
--- /dev/null
+++ b/core/tests/coretests/src/android/text/MeasuredTextTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MeasuredTextTest {
+    private static final TextDirectionHeuristic LTR = TextDirectionHeuristics.LTR;
+    private static final TextDirectionHeuristic RTL = TextDirectionHeuristics.RTL;
+
+    private static final TextPaint PAINT = new TextPaint();
+    static {
+        // The test font has following coverage and width.
+        // U+0020: 10em
+        // U+002E (.): 10em
+        // U+0043 (C): 100em
+        // U+0049 (I): 1em
+        // U+004C (L): 50em
+        // U+0056 (V): 5em
+        // U+0058 (X): 10em
+        // U+005F (_): 0em
+        // U+FFFD (invalid surrogate will be replaced to this): 7em
+        // U+10331 (\uD800\uDF31): 10em
+        Context context = InstrumentationRegistry.getTargetContext();
+        PAINT.setTypeface(Typeface.createFromAsset(context.getAssets(),
+                  "fonts/StaticLayoutLineBreakingTestFont.ttf"));
+        PAINT.setTextSize(1.0f);  // Make 1em == 1px.
+    }
+
+    private String charsToString(char[] chars) {
+        return (new StringBuilder()).append(chars).toString();
+    }
+
+    @Test
+    public void buildForBidi() {
+        MeasuredText mt = null;
+
+        mt = MeasuredText.buildForBidi("XXX", 0, 3, LTR, null);
+        assertNotNull(mt);
+        assertNotNull(mt.getChars());
+        assertEquals("XXX", charsToString(mt.getChars()));
+        assertEquals(Layout.DIR_LEFT_TO_RIGHT, mt.getParagraphDir());
+        assertNotNull(mt.getDirections(0, 3));
+        assertEquals(0, mt.getWholeWidth(), 0);
+        assertEquals(0, mt.getWidths().size());
+        assertEquals(0, mt.getSpanEndCache().size());
+        assertEquals(0, mt.getFontMetrics().size());
+        assertEquals(0, mt.getNativePtr());
+
+        // Recycle it
+        MeasuredText mt2 = MeasuredText.buildForBidi("_VVV_", 1, 4, RTL, mt);
+        assertEquals(mt2, mt);
+        assertNotNull(mt2.getChars());
+        assertEquals("VVV", charsToString(mt.getChars()));
+        assertNotNull(mt2.getDirections(0, 3));
+        assertEquals(0, mt2.getWholeWidth(), 0);
+        assertEquals(0, mt2.getWidths().size());
+        assertEquals(0, mt2.getSpanEndCache().size());
+        assertEquals(0, mt2.getFontMetrics().size());
+        assertEquals(0, mt2.getNativePtr());
+
+        mt2.recycle();
+    }
+
+    @Test
+    public void buildForMeasurement() {
+        MeasuredText mt = null;
+
+        mt = MeasuredText.buildForMeasurement(PAINT, "XXX", 0, 3, LTR, null);
+        assertNotNull(mt);
+        assertNotNull(mt.getChars());
+        assertEquals("XXX", charsToString(mt.getChars()));
+        assertEquals(Layout.DIR_LEFT_TO_RIGHT, mt.getParagraphDir());
+        assertNotNull(mt.getDirections(0, 3));
+        assertEquals(30, mt.getWholeWidth(), 0);
+        assertEquals(3, mt.getWidths().size());
+        assertEquals(10, mt.getWidths().get(0), 0);
+        assertEquals(10, mt.getWidths().get(1), 0);
+        assertEquals(10, mt.getWidths().get(2), 0);
+        assertEquals(0, mt.getSpanEndCache().size());
+        assertEquals(0, mt.getFontMetrics().size());
+        assertEquals(0, mt.getNativePtr());
+
+        // Recycle it
+        MeasuredText mt2 = MeasuredText.buildForMeasurement(PAINT, "_VVV_", 1, 4, RTL, mt);
+        assertEquals(mt2, mt);
+        assertNotNull(mt2.getChars());
+        assertEquals("VVV", charsToString(mt.getChars()));
+        assertEquals(Layout.DIR_RIGHT_TO_LEFT, mt2.getParagraphDir());
+        assertNotNull(mt2.getDirections(0, 3));
+        assertEquals(15, mt2.getWholeWidth(), 0);
+        assertEquals(3, mt2.getWidths().size());
+        assertEquals(5, mt2.getWidths().get(0), 0);
+        assertEquals(5, mt2.getWidths().get(1), 0);
+        assertEquals(5, mt2.getWidths().get(2), 0);
+        assertEquals(0, mt2.getSpanEndCache().size());
+        assertEquals(0, mt2.getFontMetrics().size());
+        assertEquals(0, mt2.getNativePtr());
+
+        mt2.recycle();
+    }
+
+    @Test
+    public void buildForStaticLayout() {
+        MeasuredText mt = null;
+
+        mt = MeasuredText.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, null);
+        assertNotNull(mt);
+        assertNotNull(mt.getChars());
+        assertEquals("XXX", charsToString(mt.getChars()));
+        assertEquals(Layout.DIR_LEFT_TO_RIGHT, mt.getParagraphDir());
+        assertNotNull(mt.getDirections(0, 3));
+        assertEquals(0, mt.getWholeWidth(), 0);
+        assertEquals(0, mt.getWidths().size());
+        assertEquals(1, mt.getSpanEndCache().size());
+        assertEquals(3, mt.getSpanEndCache().get(0));
+        assertNotEquals(0, mt.getFontMetrics().size());
+        assertNotEquals(0, mt.getNativePtr());
+
+        // Recycle it
+        MeasuredText mt2 = MeasuredText.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, mt);
+        assertEquals(mt2, mt);
+        assertNotNull(mt2.getChars());
+        assertEquals("VVV", charsToString(mt.getChars()));
+        assertEquals(Layout.DIR_RIGHT_TO_LEFT, mt2.getParagraphDir());
+        assertNotNull(mt2.getDirections(0, 3));
+        assertEquals(0, mt2.getWholeWidth(), 0);
+        assertEquals(0, mt2.getWidths().size());
+        assertEquals(1, mt2.getSpanEndCache().size());
+        assertEquals(4, mt2.getSpanEndCache().get(0));
+        assertNotEquals(0, mt2.getFontMetrics().size());
+        assertNotEquals(0, mt2.getNativePtr());
+
+        mt2.recycle();
+    }
+
+    @Test
+    public void testFor70146381() {
+        MeasuredText.buildForMeasurement(PAINT, "X…", 0, 2, RTL, null);
+    }
+}
diff --git a/core/tests/coretests/src/android/util/KeyValueListParserTest.java b/core/tests/coretests/src/android/util/KeyValueListParserTest.java
new file mode 100644
index 0000000..038f0b7
--- /dev/null
+++ b/core/tests/coretests/src/android/util/KeyValueListParserTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link KeyValueListParser}.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class KeyValueListParserTest {
+    private static final String TAG = "KeyValueListParserTest";
+    private static final int[] DEFAULT = {1, 2, 3, 4};
+
+    private KeyValueListParser mParser;
+
+    @Before
+    public void setUp() {
+        mParser = new KeyValueListParser(',');
+    }
+
+    @Test
+    public void testParseIntArrayNullInput() throws Exception {
+        mParser.setString(null);
+        int[] result = mParser.getIntArray("test", DEFAULT);
+        assertEquals(DEFAULT, result);
+    }
+
+    @Test
+    public void testParseIntArrayEmptyInput() throws Exception {
+        mParser.setString("test=");
+        int[] result = mParser.getIntArray("test", DEFAULT);
+        assertEquals(DEFAULT, result);
+    }
+
+    @Test
+    public void testParseIntArrayNullKey() throws Exception {
+        mParser.setString("foo=bar,test=100:200,baz=123");
+        int[] result = mParser.getIntArray(null, DEFAULT);
+        assertEquals(DEFAULT, result);
+    }
+
+    @Test
+    public void testParseIntArrayComplexInput() throws Exception {
+        mParser.setString("foo=bar,test=100:200,baz=123");
+        int[] result = mParser.getIntArray("test", DEFAULT);
+        assertEquals(2, result.length);
+        assertEquals(100, result[0]);  // respect order
+        assertEquals(200, result[1]);
+    }
+
+    @Test
+    public void testParseIntArrayLeadingSep() throws Exception {
+        mParser.setString("test=:4:5:6");
+        int[] result = mParser.getIntArray("test", DEFAULT);
+        assertEquals(DEFAULT, result);
+    }
+
+    @Test
+    public void testParseIntArrayEmptyItem() throws Exception {
+        mParser.setString("test=:4::6");
+        int[] result = mParser.getIntArray("test", DEFAULT);
+        assertEquals(DEFAULT, result);
+    }
+
+    @Test
+    public void testParseIntArrayTrailingSep() throws Exception {
+        mParser.setString("test=4:5:6:");
+        int[] result = mParser.getIntArray("test", DEFAULT);
+        assertEquals(3, result.length);
+        assertEquals(4, result[0]);  // respect order
+        assertEquals(5, result[1]);
+        assertEquals(6, result[2]);
+    }
+
+    @Test
+    public void testParseIntArrayGoodData() throws Exception {
+        mParser.setString("test=4:5:6");
+        int[] result = mParser.getIntArray("test", DEFAULT);
+        assertEquals(3, result.length);
+        assertEquals(4, result[0]);  // respect order
+        assertEquals(5, result[1]);
+        assertEquals(6, result[2]);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
index 13cef52..e3c91e6 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -68,6 +68,17 @@
         assertThat(clone.supportsSwitchingToNextInputMethod(), is(true));
     }
 
+    @Test
+    public void testIsVrOnly() throws Exception {
+        final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta_vr_only);
+
+        assertThat(imi.isVrOnly(), is(true));
+
+        final InputMethodInfo clone = cloneViaParcel(imi);
+
+        assertThat(clone.isVrOnly(), is(true));
+    }
+
     private InputMethodInfo buildInputMethodForTest(final @XmlRes int metaDataRes)
             throws Exception {
         final Context context = InstrumentationRegistry.getContext();
diff --git a/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
similarity index 88%
rename from services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java
rename to core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index a4e3988..449e374 100644
--- a/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.policy;
+package com.android.internal.accessibility;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.app.AlertDialog;
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.os.Handler;
@@ -38,7 +39,7 @@
 import android.widget.Toast;
 import com.android.internal.R;
 import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.policy.AccessibilityShortcutController.FrameworkObjectProvider;
+import com.android.internal.accessibility.AccessibilityShortcutController.FrameworkObjectProvider;
 
 import org.junit.After;
 import org.junit.Before;
@@ -50,6 +51,7 @@
 
 import java.lang.reflect.Field;
 import java.util.Collections;
+import java.util.Map;
 
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN;
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED;
@@ -58,7 +60,10 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.fail;
 import static org.mockito.AdditionalMatchers.aryEq;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyObject;
@@ -72,6 +77,7 @@
 @RunWith(AndroidJUnit4.class)
 public class AccessibilityShortcutControllerTest {
     private static final String SERVICE_NAME_STRING = "fake.package/fake.service.name";
+    private static final String SERVICE_NAME_SUMMARY = "Summary";
     private static final long VIBRATOR_PATTERN_1 = 100L;
     private static final long VIBRATOR_PATTERN_2 = 150L;
     private static final int[] VIBRATOR_PATTERN_INT = {(int) VIBRATOR_PATTERN_1,
@@ -95,6 +101,7 @@
     private @Mock Toast mToast;
     private @Mock Vibrator mVibrator;
     private @Mock ApplicationInfo mApplicationInfo;
+    private @Mock PackageManager mPackageManager;
 
     private MockContentResolver mContentResolver;
     private WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
@@ -108,6 +115,10 @@
         when(mContext.getResources()).thenReturn(mResources);
         when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo);
         when(mContext.getSystemService(Context.VIBRATOR_SERVICE)).thenReturn(mVibrator);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+
+        // We're not checking the text. Just prevent us crashing when getting text.
+        when(mPackageManager.getText(any(), anyInt(), any())).thenReturn("text");
 
         mContentResolver = new MockContentResolver(mContext);
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
@@ -125,6 +136,7 @@
                 .thenReturn(mAlertDialogBuilder);
         when(mFrameworkObjectProvider.makeToastFromText(eq(mContext), anyObject(), anyInt()))
                 .thenReturn(mToast);
+        when(mFrameworkObjectProvider.getSystemUiContext()).thenReturn(mContext);
 
         when(mResources.getString(anyInt())).thenReturn("Howdy %s");
         when(mResources.getIntArray(anyInt())).thenReturn(VIBRATOR_PATTERN_INT);
@@ -134,6 +146,7 @@
         when(mServiceInfo.getResolveInfo()).thenReturn(resolveInfo);
         when(mServiceInfo.getComponentName())
                 .thenReturn(ComponentName.unflattenFromString(SERVICE_NAME_STRING));
+        when(mServiceInfo.loadSummary(any())).thenReturn(SERVICE_NAME_SUMMARY);
 
         when(mAlertDialogBuilder.setTitle(anyInt())).thenReturn(mAlertDialogBuilder);
         when(mAlertDialogBuilder.setCancelable(anyBoolean())).thenReturn(mAlertDialogBuilder);
@@ -369,6 +382,33 @@
         verify(mAccessibilityManagerService).performAccessibilityShortcut();
     }
 
+    @Test
+    public void getFrameworkFeatureMap_shouldBeNonNullAndUnmodifiable() {
+        Map<ComponentName, AccessibilityShortcutController.ToggleableFrameworkFeatureInfo>
+                frameworkFeatureMap =
+                AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
+        assertTrue("Framework features not supported", frameworkFeatureMap.size() > 0);
+
+        try {
+            frameworkFeatureMap.clear();
+            fail("Framework feature map should be unmodifieable");
+        } catch (UnsupportedOperationException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public void testOnAccessibilityShortcut_forFrameworkFeature_callsServiceWithNoToast()
+            throws Exception {
+        configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
+        configureFirstFrameworkFeature();
+        Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1);
+        getController().performAccessibilityShortcut();
+
+        verifyZeroInteractions(mToast);
+        verify(mAccessibilityManagerService).performAccessibilityShortcut();
+    }
+
     private void configureNoShortcutService() {
         Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "");
     }
@@ -378,6 +418,14 @@
                 mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, SERVICE_NAME_STRING);
     }
 
+    private void configureFirstFrameworkFeature() {
+        ComponentName featureComponentName =
+                (ComponentName) AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
+                        .keySet().toArray()[0];
+        Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+                featureComponentName.flattenToString());
+    }
+
     private void configureShortcutEnabled(int enabledValue) {
         final boolean enabled;
         final boolean lockscreen;
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index 515e558..9061c44 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -38,6 +38,7 @@
     private static final String DUMMY_SETTING_ACTIVITY_NAME = "";
     private static final boolean DUMMY_IS_AUX_IME = false;
     private static final boolean DUMMY_FORCE_DEFAULT = false;
+    private static final boolean DUMMY_IS_VR_IME = false;
     private static final int DUMMY_IS_DEFAULT_RES_ID = 0;
     private static final String SYSTEM_LOCALE = "en_US";
     private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
@@ -75,7 +76,7 @@
         }
         final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
                 DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
-                DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod);
+                DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod, DUMMY_IS_VR_IME);
         if (subtypes == null) {
             items.add(new ImeSubtypeListItem(imeName, null /* variableName */, imi,
                     NOT_A_SUBTYPE_ID, null, SYSTEM_LOCALE));
@@ -111,7 +112,8 @@
                 .build());
         final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
                 DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
-                DUMMY_FORCE_DEFAULT, true /* supportsSwitchingToNextInputMethod */);
+                DUMMY_FORCE_DEFAULT, true /* supportsSwitchingToNextInputMethod */,
+                DUMMY_IS_VR_IME);
         return new ImeSubtypeListItem(imeName, subtypeName, imi, subtypeIndex, subtypeLocale,
                 systemLocale);
     }
diff --git a/core/tests/featureflagtests/Android.mk b/core/tests/featureflagtests/Android.mk
index f2d2058..6330b8e 100644
--- a/core/tests/featureflagtests/Android.mk
+++ b/core/tests/featureflagtests/Android.mk
@@ -10,7 +10,7 @@
 
 LOCAL_DX_FLAGS := --core-library
 LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib android-support-test
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 LOCAL_PACKAGE_NAME := FrameworksCoreFeatureFlagTests
 
 LOCAL_CERTIFICATE := platform
diff --git a/core/tests/systemproperties/Android.mk b/core/tests/systemproperties/Android.mk
index 4c2e224..57e2059 100644
--- a/core/tests/systemproperties/Android.mk
+++ b/core/tests/systemproperties/Android.mk
@@ -10,7 +10,7 @@
 
 LOCAL_DX_FLAGS := --core-library
 LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 LOCAL_PACKAGE_NAME := FrameworksCoreSystemPropertiesTests
 
 LOCAL_CERTIFICATE := platform
diff --git a/core/tests/utillib/Android.mk b/core/tests/utillib/Android.mk
index 8811256..be1ab1f 100644
--- a/core/tests/utillib/Android.mk
+++ b/core/tests/utillib/Android.mk
@@ -19,7 +19,8 @@
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_MODULE := frameworks-core-util-lib
-LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := junit
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
diff --git a/core/tests/utiltests/Android.mk b/core/tests/utiltests/Android.mk
index 233d070..2dc1059 100644
--- a/core/tests/utiltests/Android.mk
+++ b/core/tests/utiltests/Android.mk
@@ -19,7 +19,7 @@
     frameworks-base-testutils \
     mockito-target-minus-junit4 \
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock
 
 LOCAL_PACKAGE_NAME := FrameworksUtilTests
 
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index b3bba07..74f8c71 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -404,6 +404,8 @@
 # key 503 KEY_BRL_DOT7
 # key 504 KEY_BRL_DOT8
 
+key 522   STAR
+key 523   POUND
 key 580   APP_SWITCH
 key 582   VOICE_ASSIST
 
diff --git a/data/keyboards/Generic_Iot.kl b/data/keyboards/Generic_Iot.kl
deleted file mode 100644
index 89df224..0000000
--- a/data/keyboards/Generic_Iot.kl
+++ /dev/null
@@ -1,443 +0,0 @@
-# Copyright (C) 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-#
-# Generic key layout file for full alphabetic US English PC style external keyboards.
-#
-# This file is intentionally very generic and is intended to support a broad range of keyboards.
-# Do not edit the generic key layout to support a specific keyboard; instead, create
-# a new key layout file with the required keyboard configuration.
-#
-
-key 1     ESCAPE
-key 2     1
-key 3     2
-key 4     3
-key 5     4
-key 6     5
-key 7     6
-key 8     7
-key 9     8
-key 10    9
-key 11    0
-key 12    MINUS
-key 13    EQUALS
-key 14    DEL
-key 15    TAB
-key 16    Q
-key 17    W
-key 18    E
-key 19    R
-key 20    T
-key 21    Y
-key 22    U
-key 23    I
-key 24    O
-key 25    P
-key 26    LEFT_BRACKET
-key 27    RIGHT_BRACKET
-key 28    ENTER
-key 29    CTRL_LEFT
-key 30    A
-key 31    S
-key 32    D
-key 33    F
-key 34    G
-key 35    H
-key 36    J
-key 37    K
-key 38    L
-key 39    SEMICOLON
-key 40    APOSTROPHE
-key 41    GRAVE
-key 42    SHIFT_LEFT
-key 43    BACKSLASH
-key 44    Z
-key 45    X
-key 46    C
-key 47    V
-key 48    B
-key 49    N
-key 50    M
-key 51    COMMA
-key 52    PERIOD
-key 53    SLASH
-key 54    SHIFT_RIGHT
-key 55    NUMPAD_MULTIPLY
-key 56    ALT_LEFT
-key 57    SPACE
-key 58    CAPS_LOCK
-key 59    F1
-key 60    F2
-key 61    F3
-key 62    F4
-key 63    F5
-key 64    F6
-key 65    F7
-key 66    F8
-key 67    F9
-key 68    F10
-key 69    NUM_LOCK
-key 70    SCROLL_LOCK
-key 71    NUMPAD_7
-key 72    NUMPAD_8
-key 73    NUMPAD_9
-key 74    NUMPAD_SUBTRACT
-key 75    NUMPAD_4
-key 76    NUMPAD_5
-key 77    NUMPAD_6
-key 78    NUMPAD_ADD
-key 79    NUMPAD_1
-key 80    NUMPAD_2
-key 81    NUMPAD_3
-key 82    NUMPAD_0
-key 83    NUMPAD_DOT
-# key 84 (undefined)
-key 85    ZENKAKU_HANKAKU
-key 86    BACKSLASH
-key 87    F11
-key 88    F12
-key 89    RO
-# key 90 "KEY_KATAKANA"
-# key 91 "KEY_HIRAGANA"
-key 92    HENKAN
-key 93    KATAKANA_HIRAGANA
-key 94    MUHENKAN
-key 95    NUMPAD_COMMA
-key 96    NUMPAD_ENTER
-key 97    CTRL_RIGHT
-key 98    NUMPAD_DIVIDE
-key 99    SYSRQ
-key 100   ALT_RIGHT
-# key 101 "KEY_LINEFEED"
-key 102   MOVE_HOME
-key 103   DPAD_UP
-key 104   PAGE_UP
-key 105   DPAD_LEFT
-key 106   DPAD_RIGHT
-key 107   MOVE_END
-key 108   DPAD_DOWN
-key 109   PAGE_DOWN
-key 110   INSERT
-key 111   FORWARD_DEL
-# key 112 "KEY_MACRO"
-key 113   VOLUME_MUTE
-key 114   VOLUME_DOWN
-key 115   VOLUME_UP
-key 116   POWER
-key 117   NUMPAD_EQUALS
-# key 118 "KEY_KPPLUSMINUS"
-key 119   BREAK
-# key 120 (undefined)
-key 121   NUMPAD_COMMA
-key 122   KANA
-key 123   EISU
-key 124   YEN
-key 125   META_LEFT
-key 126   META_RIGHT
-key 127   MENU
-key 128   MEDIA_STOP
-# key 129 "KEY_AGAIN"
-# key 130 "KEY_PROPS"
-# key 131 "KEY_UNDO"
-# key 132 "KEY_FRONT"
-key 133   COPY
-# key 134 "KEY_OPEN"
-key 135   PASTE
-# key 136 "KEY_FIND"
-key 137   CUT
-# key 138 "KEY_HELP"
-key 139   MENU
-key 140   CALCULATOR
-# key 141 "KEY_SETUP"
-key 142   SLEEP
-key 143   WAKEUP
-# key 144 "KEY_FILE"
-# key 145 "KEY_SENDFILE"
-# key 146 "KEY_DELETEFILE"
-# key 147 "KEY_XFER"
-# key 148 "KEY_PROG1"
-# key 149 "KEY_PROG2"
-key 150   EXPLORER
-# key 151 "KEY_MSDOS"
-key 152   POWER
-# key 153 "KEY_DIRECTION"
-# key 154 "KEY_CYCLEWINDOWS"
-key 155   ENVELOPE
-key 156   BOOKMARK
-# key 157 "KEY_COMPUTER"
-key 158   BACK
-key 159   FORWARD
-key 160   MEDIA_CLOSE
-key 161   MEDIA_EJECT
-key 162   MEDIA_EJECT
-key 163   MEDIA_NEXT
-key 164   MEDIA_PLAY_PAUSE
-key 165   MEDIA_PREVIOUS
-key 166   MEDIA_STOP
-key 167   MEDIA_RECORD
-key 168   MEDIA_REWIND
-key 169   CALL
-# key 170 "KEY_ISO"
-key 171   MUSIC
-key 172   HOME
-# key 173 "KEY_REFRESH"
-# key 174 "KEY_EXIT"
-# key 175 "KEY_MOVE"
-# key 176 "KEY_EDIT"
-key 177   PAGE_UP
-key 178   PAGE_DOWN
-key 179   NUMPAD_LEFT_PAREN
-key 180   NUMPAD_RIGHT_PAREN
-# key 181 "KEY_NEW"
-# key 182 "KEY_REDO"
-# key 183   F13
-# key 184   F14
-# key 185   F15
-# key 186   F16
-# key 187   F17
-# key 188   F18
-# key 189   F19
-# key 190   F20
-# key 191   F21
-# key 192   F22
-# key 193   F23
-# key 194   F24
-# key 195 (undefined)
-# key 196 (undefined)
-# key 197 (undefined)
-# key 198 (undefined)
-# key 199 (undefined)
-key 200   MEDIA_PLAY
-key 201   MEDIA_PAUSE
-# key 202 "KEY_PROG3"
-# key 203 "KEY_PROG4"
-# key 204 (undefined)
-# key 205 "KEY_SUSPEND"
-# key 206 "KEY_CLOSE"
-key 207   MEDIA_PLAY
-key 208   MEDIA_FAST_FORWARD
-# key 209 "KEY_BASSBOOST"
-# key 210 "KEY_PRINT"
-# key 211 "KEY_HP"
-key 212   CAMERA
-key 213   MUSIC
-# key 214 "KEY_QUESTION"
-key 215   ENVELOPE
-# key 216 "KEY_CHAT"
-key 217   SEARCH
-# key 218 "KEY_CONNECT"
-# key 219 "KEY_FINANCE"
-# key 220 "KEY_SPORT"
-# key 221 "KEY_SHOP"
-# key 222 "KEY_ALTERASE"
-# key 223 "KEY_CANCEL"
-key 224   BRIGHTNESS_DOWN
-key 225   BRIGHTNESS_UP
-key 226   HEADSETHOOK
-key 227   POUND
-key 228   STAR
-
-key 256   BUTTON_1
-key 257   BUTTON_2
-key 258   BUTTON_3
-key 259   BUTTON_4
-key 260   BUTTON_5
-key 261   BUTTON_6
-key 262   BUTTON_7
-key 263   BUTTON_8
-key 264   BUTTON_9
-key 265   BUTTON_10
-key 266   BUTTON_11
-key 267   BUTTON_12
-key 268   BUTTON_13
-key 269   BUTTON_14
-key 270   BUTTON_15
-key 271   BUTTON_16
-
-key 288   BUTTON_1
-key 289   BUTTON_2
-key 290   BUTTON_3
-key 291   BUTTON_4
-key 292   BUTTON_5
-key 293   BUTTON_6
-key 294   BUTTON_7
-key 295   BUTTON_8
-key 296   BUTTON_9
-key 297   BUTTON_10
-key 298   BUTTON_11
-key 299   BUTTON_12
-key 300   BUTTON_13
-key 301   BUTTON_14
-key 302   BUTTON_15
-key 303   BUTTON_16
-
-
-key 304   BUTTON_A
-key 305   BUTTON_B
-key 306   BUTTON_C
-key 307   BUTTON_X
-key 308   BUTTON_Y
-key 309   BUTTON_Z
-key 310   BUTTON_L1
-key 311   BUTTON_R1
-key 312   BUTTON_L2
-key 313   BUTTON_R2
-key 314   BUTTON_SELECT
-key 315   BUTTON_START
-key 316   BUTTON_MODE
-key 317   BUTTON_THUMBL
-key 318   BUTTON_THUMBR
-
-
-# key 352 "KEY_OK"
-key 353   DPAD_CENTER
-# key 354 "KEY_GOTO"
-# key 355 "KEY_CLEAR"
-# key 356 "KEY_POWER2"
-# key 357 "KEY_OPTION"
-# key 358 "KEY_INFO"
-# key 359 "KEY_TIME"
-# key 360 "KEY_VENDOR"
-# key 361 "KEY_ARCHIVE"
-key 362   GUIDE
-# key 363 "KEY_CHANNEL"
-# key 364 "KEY_FAVORITES"
-# key 365 "KEY_EPG"
-key 366   DVR
-# key 367 "KEY_MHP"
-# key 368 "KEY_LANGUAGE"
-# key 369 "KEY_TITLE"
-# key 370 "KEY_SUBTITLE"
-# key 371 "KEY_ANGLE"
-# key 372 "KEY_ZOOM"
-# key 373 "KEY_MODE"
-# key 374 "KEY_KEYBOARD"
-# key 375 "KEY_SCREEN"
-# key 376 "KEY_PC"
-key 377   TV
-# key 378 "KEY_TV2"
-# key 379 "KEY_VCR"
-# key 380 "KEY_VCR2"
-# key 381 "KEY_SAT"
-# key 382 "KEY_SAT2"
-# key 383 "KEY_CD"
-# key 384 "KEY_TAPE"
-# key 385 "KEY_RADIO"
-# key 386 "KEY_TUNER"
-# key 387 "KEY_PLAYER"
-# key 388 "KEY_TEXT"
-# key 389 "KEY_DVD"
-# key 390 "KEY_AUX"
-# key 391 "KEY_MP3"
-# key 392 "KEY_AUDIO"
-# key 393 "KEY_VIDEO"
-# key 394 "KEY_DIRECTORY"
-# key 395 "KEY_LIST"
-# key 396 "KEY_MEMO"
-key 397   CALENDAR
-# key 398 "KEY_RED"
-# key 399 "KEY_GREEN"
-# key 400 "KEY_YELLOW"
-# key 401 "KEY_BLUE"
-key 402   CHANNEL_UP
-key 403   CHANNEL_DOWN
-# key 404 "KEY_FIRST"
-# key 405 "KEY_LAST"
-# key 406 "KEY_AB"
-# key 407 "KEY_NEXT"
-# key 408 "KEY_RESTART"
-# key 409 "KEY_SLOW"
-# key 410 "KEY_SHUFFLE"
-# key 411 "KEY_BREAK"
-# key 412 "KEY_PREVIOUS"
-# key 413 "KEY_DIGITS"
-# key 414 "KEY_TEEN"
-# key 415 "KEY_TWEN"
-
-key 429   CONTACTS
-
-# key 448 "KEY_DEL_EOL"
-# key 449 "KEY_DEL_EOS"
-# key 450 "KEY_INS_LINE"
-# key 451 "KEY_DEL_LINE"
-
-
-key 464   FUNCTION
-key 465   ESCAPE            FUNCTION
-key 466   F1                FUNCTION
-key 467   F2                FUNCTION
-key 468   F3                FUNCTION
-key 469   F4                FUNCTION
-key 470   F5                FUNCTION
-key 471   F6                FUNCTION
-key 472   F7                FUNCTION
-key 473   F8                FUNCTION
-key 474   F9                FUNCTION
-key 475   F10               FUNCTION
-key 476   F11               FUNCTION
-key 477   F12               FUNCTION
-key 478   1                 FUNCTION
-key 479   2                 FUNCTION
-key 480   D                 FUNCTION
-key 481   E                 FUNCTION
-key 482   F                 FUNCTION
-key 483   S                 FUNCTION
-key 484   B                 FUNCTION
-
-
-# key 497 KEY_BRL_DOT1
-# key 498 KEY_BRL_DOT2
-# key 499 KEY_BRL_DOT3
-# key 500 KEY_BRL_DOT4
-# key 501 KEY_BRL_DOT5
-# key 502 KEY_BRL_DOT6
-# key 503 KEY_BRL_DOT7
-# key 504 KEY_BRL_DOT8
-
-key 580   APP_SWITCH
-key 582   VOICE_ASSIST
-
-# Keys defined by HID usages
-key usage 0x0c006F BRIGHTNESS_UP
-key usage 0x0c0070 BRIGHTNESS_DOWN
-
-# Joystick and game controller axes.
-# Axes that are not mapped will be assigned generic axis numbers by the input subsystem.
-axis 0x00 X
-axis 0x01 Y
-axis 0x02 Z
-axis 0x03 RX
-axis 0x04 RY
-axis 0x05 RZ
-axis 0x06 THROTTLE
-axis 0x07 RUDDER
-axis 0x08 WHEEL
-axis 0x09 GAS
-axis 0x0a BRAKE
-axis 0x10 HAT_X
-axis 0x11 HAT_Y
-
-# LEDs
-led 0x00 NUM_LOCK
-led 0x01 CAPS_LOCK
-led 0x02 SCROLL_LOCK
-led 0x03 COMPOSE
-led 0x04 KANA
-led 0x05 SLEEP
-led 0x06 SUSPEND
-led 0x07 MUTE
-led 0x08 MISC
-led 0x09 MAIL
-led 0x0a CHARGING
diff --git a/keystore/java/android/security/AttestedKeyPair.java b/keystore/java/android/security/AttestedKeyPair.java
new file mode 100644
index 0000000..c6bff5c
--- /dev/null
+++ b/keystore/java/android/security/AttestedKeyPair.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import java.security.KeyPair;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * The {@code AttestedKeyPair} class contains a {@code KeyPair} instance of
+ * keys generated by Keystore and owned by KeyChain, as well as an attestation
+ * record for the key.
+ *
+ * <p>Such keys can be obtained by calling
+ * {@link android.app.admin.DevicePolicyManager#generateKeyPair}.
+ */
+
+public final class AttestedKeyPair {
+    private final KeyPair mKeyPair;
+    private final Certificate[] mAttestationRecord;
+
+    /**
+     * @hide Only created by the platform, no need to expose as public API.
+     */
+    public AttestedKeyPair(KeyPair keyPair, Certificate[] attestationRecord) {
+        mKeyPair = keyPair;
+        mAttestationRecord = attestationRecord;
+    }
+
+    /**
+     * Returns the generated key pair associated with the attestation record
+     * in this instance.
+     */
+    public KeyPair getKeyPair() {
+        return mKeyPair;
+    }
+
+    /**
+     * Returns the attestation record for the key pair in this instance.
+     *
+     * The attestation record is a chain of certificates. The leaf certificate links to the public
+     * key of this key pair and other properties of the key or the device. If the key is in secure
+     * hardware, and if the secure hardware supports attestation, the leaf certificate will be
+     * signed by a chain of certificates rooted at a trustworthy CA key. Otherwise the chain will be
+     * rooted at an untrusted certificate.
+     *
+     * The attestation record could be for properties of the key, or include device identifiers.
+     *
+     * See {@link android.security.keystore.KeyGenParameterSpec.Builder#setAttestationChallenge}
+     * and  <a href="https://developer.android.com/training/articles/security-key-attestation.html">
+     * Key Attestation</a> for the format of the attestation record inside the certificate.
+     */
+    public List<Certificate> getAttestationRecord() {
+        if (mAttestationRecord == null) {
+            return new ArrayList();
+        }
+        return Arrays.asList(mAttestationRecord);
+    }
+}
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 635432d..b4331b2 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -16,6 +16,7 @@
 package android.security;
 
 import android.content.pm.StringParceledListSlice;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
 
 /**
  * Caller is required to ensure that {@link KeyStore#unlock
@@ -31,6 +32,8 @@
     boolean isUserSelectable(String alias);
     void setUserSelectable(String alias, boolean isUserSelectable);
 
+    boolean generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
+
     // APIs used by CertInstaller and DevicePolicyManager
     String installCaCertificate(in byte[] caCertificate);
 
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index ed40b77..87677d4 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -195,7 +195,7 @@
  * <pre> {@code
  * KeyGenerator keyGenerator = KeyGenerator.getInstance(
  *         KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
- * keyGenerator.initialize(
+ * keyGenerator.init(
  *         new KeyGenParameterSpec.Builder("key2",
  *                 KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
  *                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
@@ -219,7 +219,7 @@
  * <pre> {@code
  * KeyGenerator keyGenerator = KeyGenerator.getInstance(
  *         KeyProperties.KEY_ALGORITHM_HMAC_SHA256, "AndroidKeyStore");
- * keyGenerator.initialize(
+ * keyGenerator.init(
  *         new KeyGenParameterSpec.Builder("key2", KeyProperties.PURPOSE_SIGN).build());
  * SecretKey key = keyGenerator.generateKey();
  * Mac mac = Mac.getInstance("HmacSHA256");
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.aidl b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.aidl
new file mode 100644
index 0000000..3fb7b4f
--- /dev/null
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+parcelable ParcelableKeyGenParameterSpec;
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
new file mode 100644
index 0000000..b15e0a2
--- /dev/null
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import java.math.BigInteger;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * A parcelable version of KeyGenParameterSpec
+ * @hide only used for communicating with the DPMS.
+ */
+public final class ParcelableKeyGenParameterSpec implements Parcelable {
+    private static final int ALGORITHM_PARAMETER_SPEC_NONE = 1;
+    private static final int ALGORITHM_PARAMETER_SPEC_RSA = 2;
+    private static final int ALGORITHM_PARAMETER_SPEC_EC = 3;
+
+    private final KeyGenParameterSpec mSpec;
+
+    public ParcelableKeyGenParameterSpec(
+            KeyGenParameterSpec spec) {
+        mSpec = spec;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    private static void writeOptionalDate(Parcel out, Date date) {
+        if (date != null) {
+            out.writeBoolean(true);
+            out.writeLong(date.getTime());
+        } else {
+            out.writeBoolean(false);
+        }
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mSpec.getKeystoreAlias());
+        out.writeInt(mSpec.getPurposes());
+        out.writeInt(mSpec.getUid());
+        out.writeInt(mSpec.getKeySize());
+
+        // Only needs to support RSAKeyGenParameterSpec and ECGenParameterSpec.
+        AlgorithmParameterSpec algoSpec = mSpec.getAlgorithmParameterSpec();
+        if (algoSpec == null) {
+            out.writeInt(ALGORITHM_PARAMETER_SPEC_NONE);
+        } else if (algoSpec instanceof RSAKeyGenParameterSpec) {
+            RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) algoSpec;
+            out.writeInt(ALGORITHM_PARAMETER_SPEC_RSA);
+            out.writeInt(rsaSpec.getKeysize());
+            out.writeByteArray(rsaSpec.getPublicExponent().toByteArray());
+        } else if (algoSpec instanceof ECGenParameterSpec) {
+            ECGenParameterSpec ecSpec = (ECGenParameterSpec) algoSpec;
+            out.writeInt(ALGORITHM_PARAMETER_SPEC_EC);
+            out.writeString(ecSpec.getName());
+        } else {
+            throw new IllegalArgumentException(
+                    String.format("Unknown algorithm parameter spec: %s", algoSpec.getClass()));
+        }
+        out.writeByteArray(mSpec.getCertificateSubject().getEncoded());
+        out.writeByteArray(mSpec.getCertificateSerialNumber().toByteArray());
+        writeOptionalDate(out, mSpec.getCertificateNotBefore());
+        writeOptionalDate(out, mSpec.getCertificateNotAfter());
+        writeOptionalDate(out, mSpec.getKeyValidityStart());
+        writeOptionalDate(out, mSpec.getKeyValidityForOriginationEnd());
+        writeOptionalDate(out, mSpec.getKeyValidityForConsumptionEnd());
+        out.writeStringArray(mSpec.getDigests());
+        out.writeStringArray(mSpec.getEncryptionPaddings());
+        out.writeStringArray(mSpec.getSignaturePaddings());
+        out.writeStringArray(mSpec.getBlockModes());
+        out.writeBoolean(mSpec.isRandomizedEncryptionRequired());
+        out.writeBoolean(mSpec.isUserAuthenticationRequired());
+        out.writeInt(mSpec.getUserAuthenticationValidityDurationSeconds());
+        out.writeByteArray(mSpec.getAttestationChallenge());
+        out.writeBoolean(mSpec.isUniqueIdIncluded());
+        out.writeBoolean(mSpec.isUserAuthenticationValidWhileOnBody());
+        out.writeBoolean(mSpec.isInvalidatedByBiometricEnrollment());
+    }
+
+    private static Date readDateOrNull(Parcel in) {
+        boolean hasDate = in.readBoolean();
+        if (hasDate) {
+            return new Date(in.readLong());
+        } else {
+            return null;
+        }
+    }
+
+    private ParcelableKeyGenParameterSpec(Parcel in) {
+        String keystoreAlias = in.readString();
+        int purposes = in.readInt();
+        KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keystoreAlias, purposes);
+        builder.setUid(in.readInt());
+        builder.setKeySize(in.readInt());
+
+        int keySpecType = in.readInt();
+        AlgorithmParameterSpec algorithmSpec = null;
+        if (keySpecType == ALGORITHM_PARAMETER_SPEC_NONE) {
+            algorithmSpec = null;
+        } else if (keySpecType == ALGORITHM_PARAMETER_SPEC_RSA) {
+            int rsaKeySize = in.readInt();
+            BigInteger publicExponent = new BigInteger(in.createByteArray());
+            algorithmSpec = new RSAKeyGenParameterSpec(rsaKeySize, publicExponent);
+        } else if (keySpecType == ALGORITHM_PARAMETER_SPEC_EC) {
+            String stdName = in.readString();
+            algorithmSpec = new ECGenParameterSpec(stdName);
+        } else {
+            throw new IllegalArgumentException(
+                    String.format("Unknown algorithm parameter spec: %d", algorithmSpec));
+        }
+        builder.setAlgorithmParameterSpec(algorithmSpec);
+        builder.setCertificateSubject(new X500Principal(in.createByteArray()));
+        builder.setCertificateSerialNumber(new BigInteger(in.createByteArray()));
+        builder.setCertificateNotBefore(readDateOrNull(in));
+        builder.setCertificateNotAfter(readDateOrNull(in));
+        builder.setKeyValidityStart(readDateOrNull(in));
+        builder.setKeyValidityForOriginationEnd(readDateOrNull(in));
+        builder.setKeyValidityForConsumptionEnd(readDateOrNull(in));
+        builder.setDigests(in.createStringArray());
+        builder.setEncryptionPaddings(in.createStringArray());
+        builder.setSignaturePaddings(in.createStringArray());
+        builder.setBlockModes(in.createStringArray());
+        builder.setRandomizedEncryptionRequired(in.readBoolean());
+        builder.setUserAuthenticationRequired(in.readBoolean());
+        builder.setUserAuthenticationValidityDurationSeconds(in.readInt());
+        builder.setAttestationChallenge(in.createByteArray());
+        builder.setUniqueIdIncluded(in.readBoolean());
+        builder.setUserAuthenticationValidWhileOnBody(in.readBoolean());
+        builder.setInvalidatedByBiometricEnrollment(in.readBoolean());
+        mSpec = builder.build();
+    }
+
+    public static final Creator<ParcelableKeyGenParameterSpec> CREATOR = new Creator<ParcelableKeyGenParameterSpec>() {
+        @Override
+        public ParcelableKeyGenParameterSpec createFromParcel(Parcel in) {
+            return new ParcelableKeyGenParameterSpec(in);
+        }
+
+        @Override
+        public ParcelableKeyGenParameterSpec[] newArray(int size) {
+            return new ParcelableKeyGenParameterSpec[size];
+        }
+    };
+
+    public KeyGenParameterSpec getSpec() {
+        return mSpec;
+    }
+}
diff --git a/keystore/tests/Android.mk b/keystore/tests/Android.mk
new file mode 100644
index 0000000..51adde4
--- /dev/null
+++ b/keystore/tests/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# LOCAL_MODULE := keystore
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    legacy-android-test
+
+LOCAL_PACKAGE_NAME := KeystoreTests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
diff --git a/keystore/tests/AndroidManifest.xml b/keystore/tests/AndroidManifest.xml
new file mode 100644
index 0000000..9bf2d0c
--- /dev/null
+++ b/keystore/tests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.security.tests">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.security.tests"
+        android:label="Tests for Keystore">
+    </instrumentation>
+</manifest>
+
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
new file mode 100644
index 0000000..73b489f
--- /dev/null
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import android.os.Parcel;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.support.test.runner.AndroidJUnit4;
+import java.math.BigInteger;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.RSAKeyGenParameterSpec;
+import java.util.Date;
+import javax.security.auth.x500.X500Principal;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link ParcelableKeyGenParameterSpec}. */
+@RunWith(AndroidJUnit4.class)
+public final class ParcelableKeyGenParameterSpecTest {
+    static final String ALIAS = "keystore-alias";
+    static final String ANOTHER_ALIAS = "another-keystore-alias";
+    static final int KEY_PURPOSES = KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY;
+    static final int UID = 1230;
+    static final int KEYSIZE = 2048;
+    static final X500Principal SUBJECT = new X500Principal("CN=subject");
+    static final BigInteger SERIAL = new BigInteger("1234567890");
+    static final Date NOT_BEFORE = new Date(1511799590);
+    static final Date NOT_AFTER = new Date(1511899590);
+    static final Date KEY_VALIDITY_START = new Date(1511799591);
+    static final Date KEY_VALIDITY_FOR_ORIG_END = new Date(1511799593);
+    static final Date KEY_VALIDITY_FOR_CONSUMPTION_END = new Date(1511799594);
+    static final String DIGEST = KeyProperties.DIGEST_SHA256;
+    static final String ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1;
+    static final String SIGNATURE_PADDING = KeyProperties.SIGNATURE_PADDING_RSA_PSS;
+    static final String BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
+    static final int USER_AUTHENTICATION_DURATION = 300;
+    static final byte[] ATTESTATION_CHALLENGE = new byte[] {'c', 'h'};
+
+    KeyGenParameterSpec configureDefaultSpec() {
+        return new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+                .setUid(UID)
+                .setKeySize(KEYSIZE)
+                .setCertificateSubject(SUBJECT)
+                .setCertificateSerialNumber(SERIAL)
+                .setCertificateNotBefore(NOT_BEFORE)
+                .setCertificateNotAfter(NOT_AFTER)
+                .setKeyValidityStart(KEY_VALIDITY_START)
+                .setKeyValidityForOriginationEnd(KEY_VALIDITY_FOR_ORIG_END)
+                .setKeyValidityForConsumptionEnd(KEY_VALIDITY_FOR_CONSUMPTION_END)
+                .setDigests(DIGEST)
+                .setEncryptionPaddings(ENCRYPTION_PADDING)
+                .setSignaturePaddings(SIGNATURE_PADDING)
+                .setBlockModes(BLOCK_MODE)
+                .setRandomizedEncryptionRequired(true)
+                .setUserAuthenticationRequired(true)
+                .setUserAuthenticationValidityDurationSeconds(USER_AUTHENTICATION_DURATION)
+                .setAttestationChallenge(ATTESTATION_CHALLENGE)
+                .setUniqueIdIncluded(true)
+                .setUserAuthenticationValidWhileOnBody(true)
+                .setInvalidatedByBiometricEnrollment(true)
+                .build();
+    }
+
+    void validateSpecValues(KeyGenParameterSpec spec, int uid, String alias) {
+        assertThat(spec.getKeystoreAlias(), is(alias));
+        assertThat(spec.getPurposes(), is(KEY_PURPOSES));
+        assertThat(spec.getUid(), is(uid));
+        assertThat(spec.getKeySize(), is(KEYSIZE));
+        assertThat(spec.getCertificateSubject(), is(SUBJECT));
+        assertThat(spec.getCertificateSerialNumber(), is(SERIAL));
+        assertThat(spec.getCertificateNotBefore(), is(NOT_BEFORE));
+        assertThat(spec.getCertificateNotAfter(), is(NOT_AFTER));
+        assertThat(spec.getKeyValidityStart(), is(KEY_VALIDITY_START));
+        assertThat(spec.getKeyValidityForOriginationEnd(), is(KEY_VALIDITY_FOR_ORIG_END));
+        assertThat(spec.getKeyValidityForConsumptionEnd(), is(KEY_VALIDITY_FOR_CONSUMPTION_END));
+        assertThat(spec.getDigests(), is(new String[] {DIGEST}));
+        assertThat(spec.getEncryptionPaddings(), is(new String[] {ENCRYPTION_PADDING}));
+        assertThat(spec.getSignaturePaddings(), is(new String[] {SIGNATURE_PADDING}));
+        assertThat(spec.getBlockModes(), is(new String[] {BLOCK_MODE}));
+        assertThat(spec.isRandomizedEncryptionRequired(), is(true));
+        assertThat(spec.isUserAuthenticationRequired(), is(true));
+        assertThat(
+                spec.getUserAuthenticationValidityDurationSeconds(),
+                is(USER_AUTHENTICATION_DURATION));
+        assertThat(spec.getAttestationChallenge(), is(ATTESTATION_CHALLENGE));
+        assertThat(spec.isUniqueIdIncluded(), is(true));
+        assertThat(spec.isUserAuthenticationValidWhileOnBody(), is(true));
+        assertThat(spec.isInvalidatedByBiometricEnrollment(), is(true));
+    }
+
+    private Parcel parcelForReading(ParcelableKeyGenParameterSpec spec) {
+        Parcel parcel = Parcel.obtain();
+        spec.writeToParcel(parcel, spec.describeContents());
+
+        parcel.setDataPosition(0);
+        return parcel;
+    }
+
+    @Test
+    public void testParcelingWithAllValues() {
+        ParcelableKeyGenParameterSpec spec =
+            new ParcelableKeyGenParameterSpec(configureDefaultSpec());
+        Parcel parcel = parcelForReading(spec);
+        ParcelableKeyGenParameterSpec fromParcel =
+            ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel);
+        validateSpecValues(fromParcel.getSpec(), UID, ALIAS);
+        assertThat(parcel.dataAvail(), is(0));
+    }
+
+    @Test
+    public void testParcelingWithNullValues() {
+        ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+            new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES).build());
+
+        Parcel parcel = parcelForReading(spec);
+        KeyGenParameterSpec fromParcel = ParcelableKeyGenParameterSpec.CREATOR
+            .createFromParcel(parcel)
+            .getSpec();
+        assertThat(fromParcel.getKeystoreAlias(), is(ALIAS));
+        assertThat(fromParcel.getPurposes(), is(KEY_PURPOSES));
+        assertThat(fromParcel.getCertificateNotBefore(), is(new Date(0L)));
+        assertThat(fromParcel.getCertificateNotAfter(), is(new Date(2461449600000L)));
+        assertThat(parcel.dataAvail(), is(0));
+    }
+
+    @Test
+    public void testParcelingRSAAlgoParameter() {
+        RSAKeyGenParameterSpec rsaSpec =
+                new RSAKeyGenParameterSpec(2048, new BigInteger("5231123"));
+        ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+            new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+            .setAlgorithmParameterSpec(rsaSpec)
+            .build());
+
+        Parcel parcel = parcelForReading(spec);
+        KeyGenParameterSpec fromParcel =
+            ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel).getSpec();
+        RSAKeyGenParameterSpec parcelSpec =
+                (RSAKeyGenParameterSpec) fromParcel.getAlgorithmParameterSpec();
+        // Compare individual fields as RSAKeyGenParameterSpec, on android, does not
+        // implement equals()
+        assertEquals(parcelSpec.getKeysize(), rsaSpec.getKeysize());
+        assertEquals(parcelSpec.getPublicExponent(), rsaSpec.getPublicExponent());
+    }
+
+    @Test
+    public void testParcelingECAlgoParameter() {
+        ECGenParameterSpec ecSpec = new ECGenParameterSpec("P-256");
+        ParcelableKeyGenParameterSpec spec = new ParcelableKeyGenParameterSpec(
+                new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
+                        .setAlgorithmParameterSpec(ecSpec)
+                        .build());
+        Parcel parcel = parcelForReading(spec);
+        KeyGenParameterSpec fromParcel =
+            ParcelableKeyGenParameterSpec.CREATOR.createFromParcel(parcel).getSpec();
+        // Compare individual fields as ECGenParameterSpec, on android, does not
+        // implement equals()
+        ECGenParameterSpec parcelSpec = (ECGenParameterSpec) fromParcel.getAlgorithmParameterSpec();
+        assertEquals(parcelSpec.getName(), ecSpec.getName());
+    }
+}
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index d41db63..4243e7e 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -59,6 +59,7 @@
 bool Properties::filterOutTestOverhead = false;
 bool Properties::disableVsync = false;
 bool Properties::skpCaptureEnabled = false;
+bool Properties::enableRTAnimations = true;
 
 static int property_get_int(const char* key, int defaultValue) {
     char buf[PROPERTY_VALUE_MAX] = {
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 9c30e4a..af4b694 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -255,6 +255,9 @@
 
     static bool skpCaptureEnabled;
 
+    // For experimentation b/68769804
+    ANDROID_API static bool enableRTAnimations;
+
     // Used for testing only to change the render pipeline.
     static void overrideRenderPipelineType(RenderPipelineType);
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b7bb2d15..820789d 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -34,6 +34,7 @@
 #include "renderstate/Stencil.h"
 #include "utils/GLUtils.h"
 #include "utils/TimeUtils.h"
+#include "../Properties.h"
 
 #include <cutils/properties.h>
 #include <google/protobuf/io/zero_copy_stream_impl.h>
@@ -375,6 +376,9 @@
     }
 
     if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
+        if (CC_UNLIKELY(!Properties::enableRTAnimations)) {
+            info.out.requiresUiRedraw = true;
+        }
         if (!info.out.requiresUiRedraw) {
             // If animationsNeedsRedraw is set don't bother posting for an RT anim
             // as we will just end up fighting the UI thread.
diff --git a/location/tests/locationtests/Android.mk b/location/tests/locationtests/Android.mk
index 73b2bb5..44d290e 100644
--- a/location/tests/locationtests/Android.mk
+++ b/location/tests/locationtests/Android.mk
@@ -7,7 +7,7 @@
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 LOCAL_PACKAGE_NAME := FrameworksLocationTests
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
diff --git a/lowpan/tests/Android.mk b/lowpan/tests/Android.mk
index bb0a944..99499dc 100644
--- a/lowpan/tests/Android.mk
+++ b/lowpan/tests/Android.mk
@@ -56,6 +56,7 @@
 
 LOCAL_JAVA_LIBRARIES := \
 	android.test.runner \
+	android.test.base \
 
 LOCAL_PACKAGE_NAME := FrameworksLowpanApiTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index aaf18e7..ba29d2d 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -423,6 +423,12 @@
         }
     }
 
+    private void doScanDirectory(String path) {
+        String[] scanPath;
+        scanPath = new String[] { path };
+        mMediaScanner.scanDirectories(scanPath);
+    }
+
     private Cursor createObjectQuery(int storageID, int format, int parent) throws RemoteException {
         String where;
         String[] whereArgs;
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index fe2a939..4e8c72b 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -55,6 +55,7 @@
 
 static jmethodID method_beginSendObject;
 static jmethodID method_endSendObject;
+static jmethodID method_doScanDirectory;
 static jmethodID method_getObjectList;
 static jmethodID method_getNumObjects;
 static jmethodID method_getSupportedPlaybackFormats;
@@ -119,6 +120,8 @@
                                             MtpObjectFormat format,
                                             bool succeeded);
 
+    virtual void                    doScanDirectory(const char* path);
+
     virtual MtpObjectHandleList*    getObjectList(MtpStorageID storageID,
                                     MtpObjectFormat format,
                                     MtpObjectHandle parent);
@@ -265,6 +268,16 @@
     checkAndClearExceptionFromCallback(env, __FUNCTION__);
 }
 
+void MyMtpDatabase::doScanDirectory(const char* path) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jstring pathStr = env->NewStringUTF(path);
+    env->CallVoidMethod(mDatabase, method_doScanDirectory, pathStr);
+
+    if (pathStr)
+        env->DeleteLocalRef(pathStr);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
 MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID,
                                                   MtpObjectFormat format,
                                                   MtpObjectHandle parent) {
@@ -1311,6 +1324,11 @@
         ALOGE("Can't find endSendObject");
         return -1;
     }
+    method_doScanDirectory = env->GetMethodID(clazz, "doScanDirectory", "(Ljava/lang/String;)V");
+    if (method_doScanDirectory == NULL) {
+        ALOGE("Can't find doScanDirectory");
+        return -1;
+    }
     method_getObjectList = env->GetMethodID(clazz, "getObjectList", "(III)[I");
     if (method_getObjectList == NULL) {
         ALOGE("Can't find getObjectList");
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index ab0da07..21410ea 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -24,7 +24,6 @@
 #define USE_SHARED_MEM_BUFFER
 
 #include <media/AudioTrack.h>
-#include <media/IMediaHTTPService.h>
 #include <media/mediaplayer.h>
 #include "SoundPool.h"
 #include "SoundPoolThread.h"
diff --git a/packages/Osu2/tests/Android.mk b/packages/Osu2/tests/Android.mk
index 4b6e0e6..afc743d 100644
--- a/packages/Osu2/tests/Android.mk
+++ b/packages/Osu2/tests/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 
 LOCAL_JACK_FLAGS := --multi-dex native
 
diff --git a/packages/SettingsLib/res/layout/preference_dropdown_material_settings.xml b/packages/SettingsLib/res/layout/preference_dropdown_material_settings.xml
new file mode 100644
index 0000000..a0b8155
--- /dev/null
+++ b/packages/SettingsLib/res/layout/preference_dropdown_material_settings.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2017 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+
+<!-- Based off frameworks/base/core/res/res/layout/preference_dropdown_material.xml
+     except that icon space in this layout is always reserved -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <Spinner
+        android:id="@+id/spinner"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/preference_no_icon_padding_start"
+        android:visibility="invisible" />
+
+    <include layout="@layout/preference_material"/>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/styles_support_preference.xml b/packages/SettingsLib/res/values/styles_support_preference.xml
index d7032b8..cf9f3c6 100644
--- a/packages/SettingsLib/res/values/styles_support_preference.xml
+++ b/packages/SettingsLib/res/values/styles_support_preference.xml
@@ -20,14 +20,73 @@
 
     <dimen name="preference_no_icon_padding_start">72dp</dimen>
 
+    <!-- Fragment style -->
+    <style name="PreferenceFragmentStyle.SettingsBase" parent="@*android:style/PreferenceFragment.Material">
+        <item name="allowDividerAfterLastItem">false</item>
+    </style>
+
+    <!-- Preferences -->
+    <style name="Preference.SettingsBase" parent="@style/Preference.Material">
+        <item name="allowDividerAbove">false</item>
+        <item name="allowDividerBelow">true</item>
+        <item name="singleLineTitle">false</item>
+        <item name="iconSpaceReserved">true</item>
+    </style>
+
+    <!-- Preference category -->
+    <style name="Preference.Category.SettingsBase" parent="@style/Preference.Category.Material">
+        <item name="allowDividerAbove">true</item>
+        <item name="allowDividerBelow">true</item>
+        <item name="android:layout">@layout/preference_category_material_settings</item>
+    </style>
+
+    <!-- Preference screen -->
+    <style name="Preference.Screen.SettingsBase" parent="@style/Preference.PreferenceScreen.Material">
+        <item name="allowDividerAbove">false</item>
+        <item name="allowDividerBelow">true</item>
+        <item name="iconSpaceReserved">true</item>
+    </style>
+
     <!-- Footer Preferences -->
-    <style name="Preference.FooterPreference.SettingsBase" parent="@style/Preference.Material">
+    <style name="Preference.FooterPreference.SettingsBase" parent="Preference.SettingsBase">
         <item name="android:layout">@layout/preference_footer</item>
         <item name="allowDividerAbove">true</item>
     </style>
 
+    <!-- Dropdown Preferences -->
+    <style name="Preference.DropdownPreference.SettingsBase" parent="Preference.SettingsBase">
+        <item name="android:layout">@layout/preference_dropdown_material_settings</item>
+    </style>
+
+    <!-- Switch Preferences -->
+    <style name="Preference.SwitchPreference.SettingsBase" parent="@style/Preference.SwitchPreference.Material">
+        <item name="allowDividerAbove">false</item>
+        <item name="allowDividerBelow">true</item>
+        <item name="iconSpaceReserved">true</item>
+        <item name="singleLineTitle">false</item>
+    </style>
+
+    <!-- EditText Preferences -->
+    <style name="Preference.EditTextPreference.SettingsBase"
+           parent="@style/Preference.DialogPreference.EditTextPreference.Material">
+        <item name="allowDividerAbove">false</item>
+        <item name="allowDividerBelow">true</item>
+        <item name="iconSpaceReserved">true</item>
+        <item name="singleLineTitle">false</item>
+    </style>
+
     <style name="PreferenceThemeOverlay.SettingsBase" parent="@style/PreferenceThemeOverlay.v14.Material">
+        <!-- Parent path frameworks/support/v14/preference/res/values/themes.xml -->
+        <item name="android:scrollbars">vertical</item>
+        <item name="preferenceFragmentStyle">@style/PreferenceFragmentStyle.SettingsBase</item>
+        <item name="preferenceCategoryStyle">@style/Preference.Category.SettingsBase</item>
+        <item name="preferenceScreenStyle">@style/Preference.Screen.SettingsBase</item>
+        <item name="preferenceStyle">@style/Preference.SettingsBase</item>
+        <item name="dialogPreferenceStyle">@style/Preference.SettingsBase</item>
+        <item name="editTextPreferenceStyle">@style/Preference.EditTextPreference.SettingsBase</item>
         <item name="footerPreferenceStyle">@style/Preference.FooterPreference.SettingsBase</item>
+        <item name="switchPreferenceStyle">@style/Preference.SwitchPreference.SettingsBase</item>
+        <item name="dropdownPreferenceStyle">@style/Preference.DropdownPreference.SettingsBase</item>
     </style>
 
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/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/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 7fb6ede..f4ec936 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -174,13 +174,10 @@
             Settings.NameValueTable.VALUE
     };
 
-    public static final int SETTINGS_TYPE_GLOBAL = 0;
-    public static final int SETTINGS_TYPE_SYSTEM = 1;
-    public static final int SETTINGS_TYPE_SECURE = 2;
-    public static final int SETTINGS_TYPE_SSAID = 3;
-
-    public static final int SETTINGS_TYPE_MASK = 0xF0000000;
-    public static final int SETTINGS_TYPE_SHIFT = 28;
+    public static final int SETTINGS_TYPE_GLOBAL = SettingsState.SETTINGS_TYPE_GLOBAL;
+    public static final int SETTINGS_TYPE_SYSTEM = SettingsState.SETTINGS_TYPE_SYSTEM;
+    public static final int SETTINGS_TYPE_SECURE = SettingsState.SETTINGS_TYPE_SECURE;
+    public static final int SETTINGS_TYPE_SSAID = SettingsState.SETTINGS_TYPE_SSAID;
 
     private static final Bundle NULL_SETTING_BUNDLE = Bundle.forPair(
             Settings.NameValueTable.VALUE, null);
@@ -278,40 +275,23 @@
     private volatile IPackageManager mPackageManager;
 
     public static int makeKey(int type, int userId) {
-        return (type << SETTINGS_TYPE_SHIFT) | userId;
+        return SettingsState.makeKey(type, userId);
     }
 
     public static int getTypeFromKey(int key) {
-        return key >>> SETTINGS_TYPE_SHIFT;
+        return SettingsState.getTypeFromKey(key);
     }
 
     public static int getUserIdFromKey(int key) {
-        return key & ~SETTINGS_TYPE_MASK;
+        return SettingsState.getUserIdFromKey(key);
     }
 
     public static String settingTypeToString(int type) {
-        switch (type) {
-            case SETTINGS_TYPE_GLOBAL: {
-                return "SETTINGS_GLOBAL";
-            }
-            case SETTINGS_TYPE_SECURE: {
-                return "SETTINGS_SECURE";
-            }
-            case SETTINGS_TYPE_SYSTEM: {
-                return "SETTINGS_SYSTEM";
-            }
-            case SETTINGS_TYPE_SSAID: {
-                return "SETTINGS_SSAID";
-            }
-            default: {
-                return "UNKNOWN";
-            }
-        }
+        return SettingsState.settingTypeToString(type);
     }
 
     public static String keyToString(int key) {
-        return "Key[user=" + getUserIdFromKey(key) + ";type="
-                + settingTypeToString(getTypeFromKey(key)) + "]";
+        return SettingsState.keyToString(key);
     }
 
     @Override
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index f901bca..a8a67ab 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -33,6 +33,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.providers.settings.GlobalSettingsProto;
 import android.providers.settings.SettingsOperationProto;
 import android.text.TextUtils;
@@ -46,6 +47,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
 import com.android.server.LocalServices;
 
 import libcore.io.IoUtils;
@@ -195,6 +197,51 @@
     @GuardedBy("mLock")
     private int mNextHistoricalOpIdx;
 
+    public static final int SETTINGS_TYPE_GLOBAL = 0;
+    public static final int SETTINGS_TYPE_SYSTEM = 1;
+    public static final int SETTINGS_TYPE_SECURE = 2;
+    public static final int SETTINGS_TYPE_SSAID = 3;
+
+    public static final int SETTINGS_TYPE_MASK = 0xF0000000;
+    public static final int SETTINGS_TYPE_SHIFT = 28;
+
+    public static int makeKey(int type, int userId) {
+        return (type << SETTINGS_TYPE_SHIFT) | userId;
+    }
+
+    public static int getTypeFromKey(int key) {
+        return key >>> SETTINGS_TYPE_SHIFT;
+    }
+
+    public static int getUserIdFromKey(int key) {
+        return key & ~SETTINGS_TYPE_MASK;
+    }
+
+    public static String settingTypeToString(int type) {
+        switch (type) {
+            case SETTINGS_TYPE_GLOBAL: {
+                return "SETTINGS_GLOBAL";
+            }
+            case SETTINGS_TYPE_SECURE: {
+                return "SETTINGS_SECURE";
+            }
+            case SETTINGS_TYPE_SYSTEM: {
+                return "SETTINGS_SYSTEM";
+            }
+            case SETTINGS_TYPE_SSAID: {
+                return "SETTINGS_SSAID";
+            }
+            default: {
+                return "UNKNOWN";
+            }
+        }
+    }
+
+    public static String keyToString(int key) {
+        return "Key[user=" + getUserIdFromKey(key) + ";type="
+                + settingTypeToString(getTypeFromKey(key)) + "]";
+    }
+
     public SettingsState(Context context, Object lock, File file, int key,
             int maxBytesPerAppPackage, Looper looper) {
         // It is important that we use the same lock as the settings provider
@@ -604,6 +651,13 @@
                 for (int i = 0; i < settingCount; i++) {
                     Setting setting = settings.valueAt(i);
 
+                    if (setting.isTransient()) {
+                        if (DEBUG_PERSISTENCE) {
+                            Slog.i(LOG_TAG, "[SKIPPED PERSISTING]" + setting.getName());
+                        }
+                        continue;
+                    }
+
                     writeSingleSetting(mVersion, serializer, setting.getId(), setting.getName(),
                             setting.getValue(), setting.getDefaultValue(), setting.getPackageName(),
                             setting.getTag(), setting.isDefaultFromSystem());
@@ -914,6 +968,14 @@
             return update(this.defaultValue, false, packageName, null, true);
         }
 
+        public boolean isTransient() {
+            switch (getTypeFromKey(getKey())) {
+                case SETTINGS_TYPE_GLOBAL:
+                    return ArrayUtils.contains(Global.TRANSIENT_SETTINGS, getName());
+            }
+            return false;
+        }
+
         public boolean update(String value, boolean setDefault, String packageName, String tag,
                 boolean forceNonSystemPackage) {
             if (NULL_VALUE.equals(value)) {
diff --git a/packages/SettingsProvider/test/Android.mk b/packages/SettingsProvider/test/Android.mk
index a9707d4..902f1c7 100644
--- a/packages/SettingsProvider/test/Android.mk
+++ b/packages/SettingsProvider/test/Android.mk
@@ -12,7 +12,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 LOCAL_PACKAGE_NAME := SettingsProviderTest
 
diff --git a/packages/SystemUI/res/drawable/instant_icon.xml b/packages/SystemUI/res/drawable/instant_icon.xml
index 0039c81..8554daa 100644
--- a/packages/SystemUI/res/drawable/instant_icon.xml
+++ b/packages/SystemUI/res/drawable/instant_icon.xml
@@ -14,17 +14,13 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="40dp"
-        android:height="40dp"
-        android:viewportWidth="2.2"
-        android:viewportHeight="2.2">
+        android:height="24dp"
+        android:width="24dp"
+        android:viewportHeight="48"
+        android:viewportWidth="48">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M.1,1.1
-        c0,.55  .45,1   1,1
-        c.55,0  1,-.45  1,-1
-        c0,-.55 -.45,-1 -1,-1
-        c-.55,0 -1,.45  -1,1z
-        M1.15,.95 l.5,0 l-.7,1 l0.1,-.7 l-.5,0 l.7,-1 z"/>
-</vector>
+        android:pathData="M35.8,18.9l-9.8,0.1l0.2,-19l-14.1,29.3l9.9,0l0,18.7z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index 0219db3..12f75bb 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -307,9 +307,8 @@
 
     void sendAccessibilityEventTypeViewTextChanged(String beforeText, int fromIndex,
                                                    int removedCount, int addedCount) {
-        if (AccessibilityManager.getInstance(mContext).isObservedEventType(
-                    AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED)
-                && (isFocused() || isSelected() && isShown())) {
+        if (AccessibilityManager.getInstance(mContext).isEnabled() &&
+                (isFocused() || isSelected() && isShown())) {
             AccessibilityEvent event =
                     AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
             event.setFromIndex(fromIndex);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
index debda21..cc2244a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
@@ -30,8 +30,6 @@
 
 import com.android.systemui.R;
 
-import java.util.Arrays;
-
 /**
  * Class to store the policy for AOD, which comes from
  * {@link android.provider.Settings.Global}
@@ -102,20 +100,6 @@
         mSettingsObserver.observe();
     }
 
-    private int[] parseIntArray(final String key, final int[] defaultArray) {
-        final String value = mParser.getString(key, null);
-        if (value != null) {
-            try {
-                return Arrays.stream(value.split(":")).map(String::trim).mapToInt(
-                        Integer::parseInt).toArray();
-            } catch (NumberFormatException e) {
-                return defaultArray;
-            }
-        } else {
-            return defaultArray;
-        }
-    }
-
     private final class SettingsObserver extends ContentObserver {
         private final Uri ALWAYS_ON_DISPLAY_CONSTANTS_URI
                 = Settings.Global.getUriFor(Settings.Global.ALWAYS_ON_DISPLAY_CONSTANTS);
@@ -154,10 +138,10 @@
                         DEFAULT_PROX_COOLDOWN_TRIGGER_MS);
                 proxCooldownPeriodMs = mParser.getLong(KEY_PROX_COOLDOWN_PERIOD_MS,
                         DEFAULT_PROX_COOLDOWN_PERIOD_MS);
-                screenBrightnessArray = parseIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY,
+                screenBrightnessArray = mParser.getIntArray(KEY_SCREEN_BRIGHTNESS_ARRAY,
                         resources.getIntArray(
                                 R.array.config_doze_brightness_sensor_to_brightness));
-                dimmingScrimArray = parseIntArray(KEY_DIMMING_SCRIM_ARRAY,
+                dimmingScrimArray = mParser.getIntArray(KEY_DIMMING_SCRIM_ARRAY,
                         resources.getIntArray(
                                 R.array.config_doze_brightness_sensor_to_scrim_opacity));
             }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 51175d1..2b48e0f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -387,9 +387,7 @@
             }
             case MotionEvent.ACTION_HOVER_ENTER:
             case MotionEvent.ACTION_HOVER_MOVE: {
-                if (mAccessibilityManager.isObservedEventType(
-                                AccessibilityEvent.TYPE_VIEW_HOVER_ENTER)
-                        && !mSendingHoverAccessibilityEvents) {
+                if (mAccessibilityManager.isEnabled() && !mSendingHoverAccessibilityEvents) {
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
                     event.setImportantForAccessibility(true);
@@ -402,9 +400,7 @@
                 break;
             }
             case MotionEvent.ACTION_HOVER_EXIT: {
-                if (mAccessibilityManager.isObservedEventType(
-                                AccessibilityEvent.TYPE_VIEW_HOVER_EXIT)
-                        && mSendingHoverAccessibilityEvents) {
+                if (mAccessibilityManager.isEnabled() && mSendingHoverAccessibilityEvents) {
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
                     event.setImportantForAccessibility(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index b7a00eb..75321fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -82,9 +82,6 @@
     private boolean mNoAnimationsInThisFrame;
     private boolean mAnimationsEnabled = true;
     private boolean mShowNotificationShelf;
-    private boolean mVibrationOnAnimation;
-    private boolean mUserTouchingScreen;
-    private boolean mTouchActive;
     private float mFirstElementRoundness;
 
     public NotificationShelf(Context context, AttributeSet attrs) {
@@ -102,9 +99,6 @@
         setClipChildren(false);
         setClipToPadding(false);
         mShelfIcons.setShowAllIcons(false);
-        mVibrationOnAnimation = mContext.getResources().getBoolean(
-                R.bool.config_vibrateOnIconAnimation);
-        updateVibrationOnAnimation();
         mViewInvertHelper = new ViewInvertHelper(mShelfIcons,
                 NotificationPanelView.DOZE_ANIMATION_DURATION);
         mShelfState = new ShelfState();
@@ -112,15 +106,6 @@
         initDimens();
     }
 
-    private void updateVibrationOnAnimation() {
-        mShelfIcons.setVibrateOnAnimation(mVibrationOnAnimation && mTouchActive);
-    }
-
-    public void setTouchActive(boolean touchActive) {
-        mTouchActive = touchActive;
-        updateVibrationOnAnimation();
-    }
-
     public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout) {
         mAmbientState = ambientState;
         mHostLayout = hostLayout;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
index 492ab44..aea0127 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
@@ -16,7 +16,6 @@
  */
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
@@ -234,7 +233,7 @@
 
         final int defaultSnooze = mParser.getInt(KEY_DEFAULT_SNOOZE,
                 resources.getInteger(R.integer.config_notification_snooze_time_default));
-        final int[] snoozeTimes = parseIntArray(KEY_OPTIONS,
+        final int[] snoozeTimes = mParser.getIntArray(KEY_OPTIONS,
                 resources.getIntArray(R.array.config_notification_snooze_times));
 
         for (int i = 0; i < snoozeTimes.length && i < sAccessibilityActions.length; i++) {
@@ -248,21 +247,6 @@
         return options;
     }
 
-    @VisibleForTesting
-    int[] parseIntArray(final String key, final int[] defaultArray) {
-        final String value = mParser.getString(key, null);
-        if (value != null) {
-            try {
-                return Arrays.stream(value.split(":")).map(String::trim).mapToInt(
-                        Integer::parseInt).toArray();
-            } catch (NumberFormatException e) {
-                return defaultArray;
-            }
-        } else {
-            return defaultArray;
-        }
-    }
-
     private SnoozeOption createOption(int minutes, int accessibilityActionId) {
         Resources res = getResources();
         boolean showInHours = minutes >= 60;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index af393c9..7e2336c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -61,11 +61,6 @@
         return sLocationOffset[1] - sLocationBase[1];
     }
 
-    public static boolean isHapticFeedbackDisabled(Context context) {
-        return Settings.System.getIntForUser(context.getContentResolver(),
-                Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) == 0;
-    }
-
     /**
      * @param dimenId the dimen to look up
      * @return the font scaled dimen as if it were in sp but doesn't shrink sizes below dp
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index 9a4e616..a2b1013 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -266,7 +266,6 @@
     }
 
     private void showBouncer() {
-        mScrimController.transitionTo(ScrimState.BOUNCER);
         mStatusBarKeyguardViewManager.animateCollapsePanels(
                 FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
         mPendingShowBouncer = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 836efff..a1b49c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.statusbar.notification.NotificationUtils.isHapticFeedbackDisabled;
-
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Canvas;
@@ -120,8 +118,6 @@
     private float mVisualOverflowAdaption;
     private boolean mDisallowNextAnimation;
     private boolean mAnimationsEnabled = true;
-    private boolean mVibrateOnAnimation;
-    private Vibrator mVibrator;
     private ArrayMap<String, ArrayList<StatusBarIcon>> mReplacingIcons;
     private int mDarkOffsetX;
 
@@ -129,7 +125,6 @@
         super(context, attrs);
         initDimens();
         setWillNotDraw(!DEBUG);
-        mVibrator = mContext.getSystemService(Vibrator.class);
     }
 
     private void initDimens() {
@@ -497,10 +492,6 @@
         return width - (getWidth() - getActualPaddingStart() - getActualPaddingEnd()) > 0;
     }
 
-    public void setVibrateOnAnimation(boolean vibrateOnAnimation) {
-        mVibrateOnAnimation = vibrateOnAnimation;
-    }
-
     public int getIconSize() {
         return mIconSize;
     }
@@ -608,39 +599,14 @@
                 } else {
                     super.applyToView(view);
                 }
-                boolean wasInShelf = icon.isInShelf();
                 boolean inShelf = iconAppearAmount == 1.0f;
                 icon.setIsInShelf(inShelf);
-                if (shouldVibrateChange(wasInShelf != inShelf)) {
-                    AsyncTask.execute(
-                            () -> mVibrator.vibrate(VibrationEffect.get(
-                                    VibrationEffect.EFFECT_TICK)));
-                }
             }
             justAdded = false;
             justReplaced = false;
             needsCannedAnimation = false;
         }
 
-        private boolean shouldVibrateChange(boolean inShelfChanged) {
-            if (!mVibrateOnAnimation) {
-                return false;
-            }
-            if (justAdded) {
-                return false;
-            }
-            if (!mAnimationsEnabled) {
-                return false;
-            }
-            if (!inShelfChanged) {
-                return false;
-            }
-            if (isHapticFeedbackDisabled(mContext)) {
-                return false;
-            }
-            return true;
-        }
-
         public boolean hasCustomTransformHeight() {
             return isLastExpandIcon && customTransformHeight != NO_VALUE;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 83125c2..2fc22ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.statusbar.notification.NotificationUtils.isHapticFeedbackDisabled;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -25,10 +23,14 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.os.AsyncTask;
+import android.os.Handler;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
+import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.InputDevice;
@@ -63,6 +65,7 @@
     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
     private boolean mPanelUpdateWhenAnimatorEnds;
     private boolean mVibrateOnOpening;
+    private boolean mVibrationEnabled;
 
     private final void logf(String fmt, Object... args) {
         Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
@@ -105,6 +108,12 @@
     private FlingAnimationUtils mFlingAnimationUtilsDismissing;
     private FalsingManager mFalsingManager;
     private final Vibrator mVibrator;
+    final private ContentObserver mVibrationObserver = new ContentObserver(Handler.getMain()) {
+        @Override
+        public void onChange(boolean selfChange) {
+            updateHapticFeedBackEnabled();
+        }
+    };
 
     /**
      * Whether an instant expand request is currently pending and we are just waiting for layout.
@@ -209,6 +218,15 @@
         mVibrator = mContext.getSystemService(Vibrator.class);
         mVibrateOnOpening = mContext.getResources().getBoolean(
                 R.bool.config_vibrateOnIconAnimation);
+        mContext.getContentResolver().registerContentObserver(
+                Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_ENABLED), true,
+                mVibrationObserver);
+        mVibrationObserver.onChange(false /* selfChange */);
+    }
+
+    public void updateHapticFeedBackEnabled() {
+        mVibrationEnabled = Settings.System.getIntForUser(mContext.getContentResolver(),
+                Settings.System.HAPTIC_FEEDBACK_ENABLED, 0, UserHandle.USER_CURRENT) != 0;
     }
 
     protected void loadDimens() {
@@ -400,7 +418,7 @@
         runPeekAnimation(INITIAL_OPENING_PEEK_DURATION, getOpeningHeight(),
                 false /* collapseWhenFinished */);
         notifyBarPanelExpansionChanged();
-        if (mVibrateOnOpening && !isHapticFeedbackDisabled(mContext)) {
+        if (mVibrateOnOpening && mVibrationEnabled) {
             AsyncTask.execute(() ->
                     mVibrator.vibrate(VibrationEffect.get(VibrationEffect.EFFECT_TICK, false)));
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 7c33437..3e32446 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -5112,6 +5112,7 @@
 
     public void notifyFpAuthModeChanged() {
         updateDozing();
+        updateScrimController();
     }
 
     private void updateDozing() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index d7f11f7..4accd86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -248,7 +248,6 @@
 
     public void setTouchActive(boolean touchActive) {
         mTouchActive = touchActive;
-        mStackScrollLayout.setTouchActive(touchActive);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index a3d2423..0f2a2c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -4443,10 +4443,6 @@
                 mAmbientState.getScrollY()));
     }
 
-    public void setTouchActive(boolean touchActive) {
-        mShelf.setTouchActive(touchActive);
-    }
-
     /**
      * A listener that is notified when some child locations might have changed.
      */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java
index 6b31c96..756bb1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationSnoozeTest.java
@@ -62,57 +62,6 @@
     }
 
     @Test
-    public void testParseIntArrayNull() throws Exception {
-        when(mMockParser.getString(anyString(), isNull())).thenReturn(null);
-        mNotificationSnooze.setKeyValueListParser(mMockParser);
-
-        int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
-        assertEquals(RES_OPTIONS, result);
-    }
-
-    @Test
-    public void testParseIntArrayLeadingSep() throws Exception {
-        when(mMockParser.getString(anyString(), isNull())).thenReturn(":4:5:6");
-        mNotificationSnooze.setKeyValueListParser(mMockParser);
-
-        int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
-        assertEquals(RES_OPTIONS, result);
-    }
-
-    @Test
-    public void testParseIntArrayEmptyItem() throws Exception {
-        when(mMockParser.getString(anyString(), isNull())).thenReturn("4::6");
-        mNotificationSnooze.setKeyValueListParser(mMockParser);
-
-        int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
-        assertEquals(RES_OPTIONS, result);
-    }
-
-    @Test
-    public void testParseIntArrayTrailingSep() throws Exception {
-        when(mMockParser.getString(anyString(), isNull())).thenReturn("4:5:6:");
-        mNotificationSnooze.setKeyValueListParser(mMockParser);
-
-        int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
-        assertEquals(3, result.length);
-        assertEquals(4, result[0]);  // respect order
-        assertEquals(5, result[1]);
-        assertEquals(6, result[2]);
-    }
-
-    @Test
-    public void testParseIntArrayGoodData() throws Exception {
-        when(mMockParser.getString(anyString(), isNull())).thenReturn("4:5:6");
-        mNotificationSnooze.setKeyValueListParser(mMockParser);
-
-        int[] result = mNotificationSnooze.parseIntArray("foo", RES_OPTIONS);
-        assertEquals(3, result.length);
-        assertEquals(4, result[0]);  // respect order
-        assertEquals(5, result[1]);
-        assertEquals(6, result[2]);
-    }
-
-    @Test
     public void testGetOptionsWithNoConfig() throws Exception {
         ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
         assertEquals(3, result.size());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 713f7843..0aeb7b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -104,6 +104,7 @@
     PowerManager mPowerManager;
     SystemServicesProxy mSystemServicesProxy;
     NotificationPanelView mNotificationPanelView;
+    ScrimController mScrimController;
     IStatusBarService mBarService;
     ArrayList<Entry> mNotificationList;
     private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
@@ -131,6 +132,7 @@
         mNotificationPanelView = mock(NotificationPanelView.class);
         when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0));
         mNotificationList = mock(ArrayList.class);
+        mScrimController = mock(ScrimController.class);
         IPowerManager powerManagerService = mock(IPowerManager.class);
         HandlerThread handlerThread = new HandlerThread("TestThread");
         handlerThread.start();
@@ -143,7 +145,7 @@
         mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
                 mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
                 mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView,
-                mBarService);
+                mBarService, mScrimController);
         mStatusBar.mContext = mContext;
         mStatusBar.mComponents = mContext.getComponents();
         doAnswer(invocation -> {
@@ -532,12 +534,21 @@
         mStatusBar.updateKeyguardState(false, false);
     }
 
+    @Test
+    public void testFingerprintNotification_UpdatesScrims() {
+        mStatusBar.mStatusBarWindowManager = mock(StatusBarWindowManager.class);
+        mStatusBar.mFingerprintUnlockController = mock(FingerprintUnlockController.class);
+        mStatusBar.mDozeScrimController = mock(DozeScrimController.class);
+        mStatusBar.notifyFpAuthModeChanged();
+        verify(mScrimController).transitionTo(any(), any());
+    }
+
     static class TestableStatusBar extends StatusBar {
         public TestableStatusBar(StatusBarKeyguardViewManager man,
                 UnlockMethodCache unlock, KeyguardIndicationController key,
                 NotificationStackScrollLayout stack, HeadsUpManager hum, NotificationData nd,
                 PowerManager pm, SystemServicesProxy ssp, NotificationPanelView panelView,
-                IStatusBarService barService) {
+                IStatusBarService barService, ScrimController scrimController) {
             mStatusBarKeyguardViewManager = man;
             mUnlockMethodCache = unlock;
             mKeyguardIndicationController = key;
@@ -550,7 +561,7 @@
             mNotificationPanel = panelView;
             mBarService = barService;
             mWakefulnessLifecycle = createAwakeWakefulnessLifecycle();
-            mScrimController = mock(ScrimController.class);
+            mScrimController = scrimController;
         }
 
         private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 4f1f4d7..6faf1f9 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5023,8 +5023,38 @@
     // OS: P
     CONNECTION_DEVICE_ADVANCED = 1264;
 
-    // ---- End P Constants, all P constants go above this line ----
+    // OPEN: Settings > Security > Screen lock gear icon
+    // CATEGORY: SETTINGS
+    // OS: P
+    SCREEN_LOCK_SETTINGS = 1265;
 
+    // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Delete rule (trash can icon)
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_DELETE_RULE_DIALOG = 1266;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Select rule ("Event") > Rule name > OK
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_MODE_RULE_NAME_CHANGE_OK = 1267;
+
+    // OPEN: Settings > Sound > Do Not Disturb > TURN ON NOW/TURN OFF NOW
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_TOGGLE_DND_BUTTON = 1268;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Add rule > Event/Time
+    // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Select rule ("Event") > Rule name
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_RULE_NAME_DIALOG = 1269;
+
+    // OPEN: Settings > Sound > Do Not Disturb > Turn on automatically > Add rule
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_RULE_SELECTION_DIALOG = 1270;
+
+    // ---- End P Constants, all P constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/Android.bp b/services/Android.bp
new file mode 100644
index 0000000..84c45fe
--- /dev/null
+++ b/services/Android.bp
@@ -0,0 +1,8 @@
+// native library
+// =============================================================
+
+cc_library_shared {
+    name: "libandroid_servers",
+    defaults: ["libservices.core-libs"],
+    whole_static_libs: ["libservices.core"],
+}
diff --git a/services/Android.mk b/services/Android.mk
index ed2ba1f..81d8181 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -52,23 +52,6 @@
 
 include $(BUILD_JAVA_LIBRARY)
 
-# native library
-# =============================================================
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES :=
-LOCAL_SHARED_LIBRARIES :=
-
-# include all the jni subdirs to collect their sources
-include $(wildcard $(LOCAL_PATH)/*/jni/Android.mk)
-
-LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
-
-LOCAL_MODULE:= libandroid_servers
-
-include $(BUILD_SHARED_LIBRARY)
-
 # =============================================================
 
 ifeq (,$(ONE_SHOT_MAKEFILE))
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
index 22d922b..7e94d7b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
@@ -20,7 +20,9 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 
+import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.GestureDescription;
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.annotation.NonNull;
@@ -47,6 +49,7 @@
 import android.view.View;
 import android.view.accessibility.AccessibilityCache;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
@@ -62,7 +65,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -104,7 +106,7 @@
 
     int mFeedbackType;
 
-    final Set<String> mPackageNames = new HashSet<>();
+    Set<String> mPackageNames = new HashSet<>();
 
     boolean mIsDefault;
 
@@ -282,98 +284,40 @@
         return true;
     }
 
-    boolean setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
-        boolean somethingChanged = false;
-
-        if (mEventTypes != info.eventTypes) {
-            mEventTypes = info.eventTypes;
-            somethingChanged = true;
+    public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
+        mEventTypes = info.eventTypes;
+        mFeedbackType = info.feedbackType;
+        String[] packageNames = info.packageNames;
+        if (packageNames != null) {
+            mPackageNames.addAll(Arrays.asList(packageNames));
         }
-
-        if (mFeedbackType != info.feedbackType) {
-            mFeedbackType = info.feedbackType;
-            somethingChanged = true;
-        }
-
-        final String[] oldPackageNames = mPackageNames.toArray(new String[mPackageNames.size()]);
-        if (!Arrays.equals(oldPackageNames, info.packageNames)) {
-            mPackageNames.clear();
-            if (info.packageNames != null) {
-                Collections.addAll(mPackageNames, info.packageNames);
-            }
-            somethingChanged = true;
-        }
-
-        if (mNotificationTimeout != info.notificationTimeout) {
-            mNotificationTimeout = info.notificationTimeout;
-            somethingChanged = true;
-        }
-
-        final boolean newIsDefault = (info.flags & DEFAULT) != 0;
-        if (mIsDefault != newIsDefault) {
-            mIsDefault = newIsDefault;
-            somethingChanged = true;
-        }
+        mNotificationTimeout = info.notificationTimeout;
+        mIsDefault = (info.flags & DEFAULT) != 0;
 
         if (supportsFlagForNotImportantViews(info)) {
-            somethingChanged |= updateFetchFlag(info.flags,
-                    AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS);
-        }
-
-        somethingChanged |= updateFetchFlag(info.flags,
-                AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS);
-
-        final boolean newRequestTouchExplorationMode = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
-        if (mRequestTouchExplorationMode != newRequestTouchExplorationMode) {
-            mRequestTouchExplorationMode = newRequestTouchExplorationMode;
-            somethingChanged = true;
-        }
-
-        final boolean newRequestFilterKeyEvents = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
-        if (mRequestFilterKeyEvents != newRequestFilterKeyEvents) {
-            mRequestFilterKeyEvents = newRequestFilterKeyEvents;
-            somethingChanged = true;
-        }
-
-        final boolean newRetrieveInteractiveWindows = (info.flags
-                & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
-        if (mRetrieveInteractiveWindows != newRetrieveInteractiveWindows) {
-            mRetrieveInteractiveWindows = newRetrieveInteractiveWindows;
-            somethingChanged = true;
-        }
-
-        final boolean newCaptureFingerprintGestures = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES) != 0;
-        if (mCaptureFingerprintGestures != newCaptureFingerprintGestures) {
-            mCaptureFingerprintGestures = newCaptureFingerprintGestures;
-            somethingChanged = true;
-        }
-
-        final boolean newRequestAccessibilityButton = (info.flags
-                & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
-        if (mRequestAccessibilityButton != newRequestAccessibilityButton) {
-            mRequestAccessibilityButton = newRequestAccessibilityButton;
-            somethingChanged = true;
-        }
-
-        return somethingChanged;
-    }
-
-    private boolean updateFetchFlag(int allFlags, int flagToUpdate) {
-        if ((allFlags & flagToUpdate) != 0) {
-            if ((mFetchFlags & flagToUpdate) == 0) {
-                mFetchFlags |= flagToUpdate;
-                return true;
+            if ((info.flags & AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0) {
+                mFetchFlags |= AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+            } else {
+                mFetchFlags &= ~AccessibilityNodeInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
             }
+        }
+
+        if ((info.flags & AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS) != 0) {
+            mFetchFlags |= AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
         } else {
-            if ((mFetchFlags & flagToUpdate) != 0) {
-                mFetchFlags &= ~flagToUpdate;
-                return true;
-            }
+            mFetchFlags &= ~AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
         }
-        return false;
+
+        mRequestTouchExplorationMode = (info.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
+        mRequestFilterKeyEvents = (info.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
+        mRetrieveInteractiveWindows = (info.flags
+                & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
+        mCaptureFingerprintGestures = (info.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES) != 0;
+        mRequestAccessibilityButton = (info.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
     }
 
     protected boolean supportsFlagForNotImportantViews(AccessibilityServiceInfo info) {
@@ -405,15 +349,14 @@
                 // If the XML manifest had data to configure the service its info
                 // should be already set. In such a case update only the dynamically
                 // configurable properties.
-                final boolean serviceInfoChanged;
                 AccessibilityServiceInfo oldInfo = mAccessibilityServiceInfo;
                 if (oldInfo != null) {
                     oldInfo.updateDynamicallyConfigurableProperties(info);
-                    serviceInfoChanged = setDynamicallyConfigurableProperties(oldInfo);
+                    setDynamicallyConfigurableProperties(oldInfo);
                 } else {
-                    serviceInfoChanged = setDynamicallyConfigurableProperties(info);
+                    setDynamicallyConfigurableProperties(info);
                 }
-                mSystemSupport.onClientChange(serviceInfoChanged);
+                mSystemSupport.onClientChange(true);
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8b5c85a7..0a21b9e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -100,7 +100,8 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IntPair;
 import com.android.server.LocalServices;
-import com.android.server.policy.AccessibilityShortcutController;
+import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.accessibility.AccessibilityShortcutController.ToggleableFrameworkFeatureInfo;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -1897,8 +1898,11 @@
         if (userState.mServiceToEnableWithShortcut == null) {
             return;
         }
-        boolean shortcutServiceIsInstalled = false;
-        for (int i = 0; i < userState.mInstalledServices.size(); i++) {
+        boolean shortcutServiceIsInstalled =
+                AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
+                        .containsKey(userState.mServiceToEnableWithShortcut);
+        for (int i = 0; !shortcutServiceIsInstalled && (i < userState.mInstalledServices.size());
+                i++) {
             if (userState.mInstalledServices.get(i).getComponentName()
                     .equals(userState.mServiceToEnableWithShortcut)) {
                 shortcutServiceIsInstalled = true;
@@ -1909,7 +1913,8 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                        Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, null, userState.mUserId);
+                        Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, null,
+                        userState.mUserId);
 
                 Settings.Secure.putIntForUser(mContext.getContentResolver(),
                         Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED, 0, userState.mUserId);
@@ -2117,12 +2122,26 @@
             throw new SecurityException(
                     "performAccessibilityShortcut requires the WRITE_SECURE_SETTINGS permission");
         }
+        final Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureMap =
+                AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
         synchronized(mLock) {
-            UserState userState = getUserStateLocked(mCurrentUserId);
-            ComponentName serviceName = userState.mServiceToEnableWithShortcut;
+            final UserState userState = getUserStateLocked(mCurrentUserId);
+            final ComponentName serviceName = userState.mServiceToEnableWithShortcut;
             if (serviceName == null) {
                 return;
             }
+            if (frameworkFeatureMap.containsKey(serviceName)) {
+                // Toggle the requested framework feature
+                ToggleableFrameworkFeatureInfo featureInfo = frameworkFeatureMap.get(serviceName);
+                SettingStringHelper setting = new SettingStringHelper(mContext.getContentResolver(),
+                        featureInfo.getSettingKey(), mCurrentUserId);
+                // Assuming that the default state will be to have the feature off
+                if (!TextUtils.equals(featureInfo.getSettingOnValue(), setting.read())) {
+                    setting.write(featureInfo.getSettingOnValue());
+                } else {
+                    setting.write(featureInfo.getSettingOffValue());
+                }
+            }
             final long identity = Binder.clearCallingIdentity();
             try {
                 if (userState.mComponentNameToServiceMap.get(serviceName) == null) {
@@ -2400,8 +2419,7 @@
         private void announceNewUserIfNeeded() {
             synchronized (mLock) {
                 UserState userState = getCurrentUserStateLocked();
-                if (userState.isHandlingAccessibilityEvents()
-                        && userState.isObservedEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT)) {
+                if (userState.isHandlingAccessibilityEvents()) {
                     UserManager userManager = (UserManager) mContext.getSystemService(
                             Context.USER_SERVICE);
                     String message = mContext.getString(R.string.user_switched,
@@ -3158,21 +3176,13 @@
             if (mWindowsForAccessibilityCallback == null) {
                 return;
             }
-            final int userId;
-            synchronized (mLock) {
-                userId = mCurrentUserId;
-                final UserState userState = getUserStateLocked(userId);
-                if (!userState.isObservedEventType(AccessibilityEvent.TYPE_WINDOWS_CHANGED)) {
-                    return;
-                }
-            }
             final long identity = Binder.clearCallingIdentity();
             try {
                 // Let the client know the windows changed.
                 AccessibilityEvent event = AccessibilityEvent.obtain(
                         AccessibilityEvent.TYPE_WINDOWS_CHANGED);
                 event.setEventTime(SystemClock.uptimeMillis());
-                sendAccessibilityEvent(event, userId);
+                sendAccessibilityEvent(event, mCurrentUserId);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -3377,10 +3387,6 @@
             mUserId = userId;
         }
 
-        public boolean isObservedEventType(@AccessibilityEvent.EventType int type) {
-            return (mLastSentRelevantEventTypes & type) != 0;
-        }
-
         public int getClientState() {
             int clientState = 0;
             final boolean a11yEnabled = (mUiAutomationManager.isUiAutomationRunningLocked()
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index 62017e8..3419b80 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -791,7 +791,7 @@
      */
     private void sendAccessibilityEvent(int type) {
         AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
-        if (accessibilityManager.isObservedEventType(type)) {
+        if (accessibilityManager.isEnabled()) {
             AccessibilityEvent event = AccessibilityEvent.obtain(type);
             event.setWindowId(mAms.getActiveWindowId());
             accessibilityManager.sendAccessibilityEvent(event);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 6c15438..b446209 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -2506,6 +2506,8 @@
             info.widgetCategory = sa.getInt(
                     com.android.internal.R.styleable.AppWidgetProviderInfo_widgetCategory,
                     AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN);
+            info.widgetFeatures = sa.getInt(
+                    com.android.internal.R.styleable.AppWidgetProviderInfo_widgetFeatures, 0);
 
             sa.recycle();
         } catch (IOException | PackageManager.NameNotFoundException | XmlPullParserException e) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 0291276..690c45b 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -610,6 +610,21 @@
         }
 
         @Override
+        public boolean isFieldClassificationEnabled() throws RemoteException {
+            UserHandle user = getCallingUserHandle();
+            int uid = getCallingUid();
+
+            synchronized (mLock) {
+                AutofillManagerServiceImpl service = peekServiceForUserLocked(user.getIdentifier());
+                if (service != null) {
+                    return service.isFieldClassificationEnabled();
+                }
+            }
+
+            return false;
+        }
+
+        @Override
         public boolean restoreSession(int sessionId, IBinder activityToken, IBinder appCallback)
                 throws RemoteException {
             activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 8b6dc20..3ef450a 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -47,6 +47,7 @@
 import android.provider.Settings;
 import android.service.autofill.AutofillService;
 import android.service.autofill.AutofillServiceInfo;
+import android.service.autofill.FieldClassification.Match;
 import android.service.autofill.FillEventHistory;
 import android.service.autofill.FillEventHistory.Event;
 import android.service.autofill.FillResponse;
@@ -74,6 +75,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Random;
 
 /**
@@ -626,7 +628,7 @@
             if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
                 mEventHistory.addEvent(
                         new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null,
-                                null, null, null, null, null, -1));
+                                null, null, null, null, null, null));
             }
         }
     }
@@ -640,7 +642,7 @@
             if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) {
                 mEventHistory.addEvent(
                         new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
-                                clientState, null, null, null, null, null, null, null, -1));
+                                clientState, null, null, null, null, null, null, null, null));
             }
         }
     }
@@ -652,7 +654,7 @@
         synchronized (mLock) {
             if (isValidEventLocked("logSaveShown()", sessionId)) {
                 mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null,
-                        null, null, null, null, null, null, -1));
+                        null, null, null, null, null, null, null));
             }
         }
     }
@@ -666,7 +668,7 @@
             if (isValidEventLocked("logDatasetSelected()", sessionId)) {
                 mEventHistory.addEvent(
                         new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
-                                null, null, null, null, null, null, -1));
+                                null, null, null, null, null, null, null));
             }
         }
     }
@@ -681,14 +683,24 @@
             @Nullable ArrayList<String> changedDatasetIds,
             @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
             @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
-            @Nullable String detectedRemoteId, int detectedFieldScore) {
+            @NonNull ArrayList<AutofillId> detectedFieldIdsList,
+            @NonNull ArrayList<Match> detectedMatchesList) {
+
         synchronized (mLock) {
             if (isValidEventLocked("logDatasetNotSelected()", sessionId)) {
+                AutofillId[] detectedFieldsIds = null;
+                Match[] detectedMatches = null;
+                if (!detectedFieldIdsList.isEmpty()) {
+                    detectedFieldsIds = new AutofillId[detectedFieldIdsList.size()];
+                    detectedFieldIdsList.toArray(detectedFieldsIds);
+                    detectedMatches = new Match[detectedMatchesList.size()];
+                    detectedMatchesList.toArray(detectedMatches);
+                }
                 mEventHistory.addEvent(new Event(Event.TYPE_CONTEXT_COMMITTED, null,
                         clientState, selectedDatasets, ignoredDatasets,
                         changedFieldIds, changedDatasetIds,
                         manuallyFilledFieldIds, manuallyFilledDatasetIds,
-                        detectedRemoteId, detectedFieldScore));
+                        detectedFieldsIds, detectedMatches));
             }
         }
     }
@@ -757,7 +769,8 @@
         pw.print(prefix); pw.print("Default component: ");
             pw.println(mContext.getString(R.string.config_defaultAutofillService));
         pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
-        pw.print(prefix); pw.print("Field detection: "); pw.println(isFieldDetectionEnabled());
+        pw.print(prefix); pw.print("Field classification enabled: ");
+            pw.println(isFieldClassificationEnabled());
         pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
         pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
 
@@ -1014,10 +1027,10 @@
         return false;
     }
 
-    // TODO(b/67867469): remove once feature is finished
-    boolean isFieldDetectionEnabled() {
+    boolean isFieldClassificationEnabled() {
         return Settings.Secure.getIntForUser(
-                mContext.getContentResolver(), Settings.Secure.AUTOFILL_FEATURE_FIELD_DETECTION, 0,
+                mContext.getContentResolver(),
+                Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION, 0,
                 mUserId) == 1;
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3615bca..dea6669 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -55,6 +55,7 @@
 import android.os.SystemClock;
 import android.service.autofill.AutofillService;
 import android.service.autofill.Dataset;
+import android.service.autofill.FieldClassification.Match;
 import android.service.autofill.FillContext;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
@@ -64,6 +65,7 @@
 import android.service.autofill.SaveRequest;
 import android.service.autofill.UserData;
 import android.service.autofill.ValueFinder;
+import android.service.autofill.EditDistanceScorer;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LocalLog;
@@ -499,7 +501,7 @@
         }
 
         // TODO(b/67867469): remove once feature is finished
-        if (response.getFieldClassificationIds() != null && !mService.isFieldDetectionEnabled()) {
+        if (response.getFieldClassificationIds() != null && !mService.isFieldClassificationEnabled()) {
             Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
             processNullResponseLocked(requestFlags);
             return;
@@ -942,19 +944,18 @@
         }
 
         final UserData userData = mService.getUserData();
-        final AutofillId detectableFieldId;
-        final String detectableRemoteId;
-        String detectedRemoteId = null;
-        if (userData == null) {
-            detectableFieldId = null;
-            detectableRemoteId = null;
-        } else {
-            // TODO(b/67867469): hardcoded to just first entry on initial refactoring.
-            detectableFieldId = fieldClassificationIds[0];
-            detectableRemoteId = userData.getRemoteIds()[0];
-        }
 
-        int detectedFieldScore = -1;
+        final ArrayList<AutofillId> detectedFieldIds;
+        final ArrayList<Match> detectedMatches;
+
+        if (userData != null) {
+            final int maxFieldsSize = UserData.getMaxFieldClassificationIdsSize();
+            detectedFieldIds = new ArrayList<>(maxFieldsSize);
+            detectedMatches = new ArrayList<>(maxFieldsSize);
+        } else {
+            detectedFieldIds = null;
+            detectedMatches = null;
+        }
 
         for (int i = 0; i < mViewStates.size(); i++) {
             final ViewState viewState = mViewStates.valueAt(i);
@@ -998,8 +999,8 @@
                     final AutofillValue currentValue = viewState.getCurrentValue();
                     if (currentValue == null) {
                         if (sDebug) {
-                            Slog.d(TAG, "logContextCommitted(): skipping view witout current value "
-                                    + "( " + viewState + ")");
+                            Slog.d(TAG, "logContextCommitted(): skipping view without current "
+                                    + "value ( " + viewState + ")");
                         }
                         continue;
                     }
@@ -1060,19 +1061,10 @@
                         } // for j
                     }
 
-                    // Check if detectable field changed.
-                    if (detectableFieldId != null && detectableFieldId.equals(viewState.id)
-                            && currentValue.isText() && currentValue.getTextValue() != null) {
-                        final String actualValue = currentValue.getTextValue().toString();
-                        // TODO(b/67867469): hardcoded to just first entry on initial refactoring.
-                        final String expectedValue = userData.getValues()[0];
-                        if (actualValue.equalsIgnoreCase(expectedValue)) {
-                            detectedRemoteId = detectableRemoteId;
-                            detectedFieldScore = 0;
-                        } else if (sVerbose) {
-                            Slog.v(TAG, "Detection mismatch for field " + detectableFieldId);
-                        }
-                        // TODO(b/67867469): set score on partial hits
+                    // Sets field classification score for field
+                    if (userData!= null) {
+                        setScore(detectedFieldIds, detectedMatches, userData, viewState.id,
+                                currentValue);
                     }
                 } // else
             } // else
@@ -1085,8 +1077,8 @@
                     + ", changedAutofillIds=" + changedFieldIds
                     + ", changedDatasetIds=" + changedDatasetIds
                     + ", manuallyFilledIds=" + manuallyFilledIds
-                    + ", detectableFieldId=" + detectableFieldId
-                    + ", detectedFieldScore=" + detectedFieldScore
+                    + ", detectedFieldIds=" + detectedFieldIds
+                    + ", detectedMatches=" + detectedMatches
                     );
         }
 
@@ -1109,7 +1101,46 @@
         mService.logContextCommitted(id, mClientState, mSelectedDatasetIds, ignoredDatasets,
                 changedFieldIds, changedDatasetIds,
                 manuallyFilledFieldIds, manuallyFilledDatasetIds,
-                detectedRemoteId, detectedFieldScore);
+                detectedFieldIds, detectedMatches);
+    }
+
+    /**
+     * Adds the top score match to {@code detectedFieldsIds} and {@code detectedMatches} for
+     * {@code fieldId} based on its {@code currentValue} and {@code userData}.
+     */
+    private static void setScore(@NonNull ArrayList<AutofillId> detectedFieldIds,
+            @NonNull ArrayList<Match> detectedMatches, @NonNull UserData userData,
+            @NonNull AutofillId fieldId, @NonNull AutofillValue currentValue) {
+
+        final String[] userValues = userData.getValues();
+        final String[] remoteIds = userData.getRemoteIds();
+
+        // Sanity check
+        if (userValues == null || remoteIds == null || userValues.length != remoteIds.length) {
+            final int valuesLength = userValues == null ? -1 : userValues.length;
+            final int idsLength = remoteIds == null ? -1 : remoteIds.length;
+            Slog.w(TAG, "setScores(): user data mismatch: values.length = "
+                    + valuesLength + ", ids.length = " + idsLength);
+            return;
+        }
+        String remoteId = null;
+        float topScore = 0;
+        for (int i = 0; i < userValues.length; i++) {
+            final String value = userValues[i];
+            final float score = userData.getScorer().getScore(currentValue, value);
+            if (score > topScore) {
+                topScore = score;
+                remoteId = remoteIds[i];
+            }
+        }
+
+        if (remoteId != null && topScore > 0) {
+            if (sVerbose) Slog.v(TAG, "setScores(): top score for #" + fieldId + " is " + topScore);
+            detectedFieldIds.add(fieldId);
+            detectedMatches.add(new Match(remoteId, topScore));
+        } else if (sVerbose) {
+            Slog.v(TAG, "setScores(): no top score for #" + fieldId + ": " + topScore);
+        }
     }
 
     /**
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index 3eaee9a..bd0d853 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -2389,19 +2389,24 @@
             if (MORE_DEBUG) Slog.v(TAG, "Found the app - running clear process");
             mBackupHandler.removeMessages(MSG_RETRY_CLEAR);
             synchronized (mQueueLock) {
-                final IBackupTransport transport =
-                        mTransportManager.getTransportBinder(transportName);
-                if (transport == null) {
-                    // transport is currently unavailable -- make sure to retry
+                TransportClient transportClient =
+                        mTransportManager
+                                .getTransportClient(transportName, "BMS.clearBackupData()");
+                if (transportClient == null) {
+                    // transport is currently unregistered -- make sure to retry
                     Message msg = mBackupHandler.obtainMessage(MSG_RETRY_CLEAR,
                             new ClearRetryParams(transportName, packageName));
                     mBackupHandler.sendMessageDelayed(msg, TRANSPORT_RETRY_INTERVAL);
                     return;
                 }
                 long oldId = Binder.clearCallingIdentity();
+                OnTaskFinishedListener listener =
+                        caller ->
+                                mTransportManager.disposeOfTransportClient(transportClient, caller);
                 mWakelock.acquire();
-                Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
-                        new ClearParams(transport, info));
+                Message msg = mBackupHandler.obtainMessage(
+                        MSG_RUN_CLEAR,
+                        new ClearParams(transportClient, info, listener));
                 mBackupHandler.sendMessage(msg);
                 Binder.restoreCallingIdentity(oldId);
             }
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 9011b95..477724d 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -268,8 +268,13 @@
 
             case MSG_RUN_CLEAR: {
                 ClearParams params = (ClearParams) msg.obj;
-                (new PerformClearTask(backupManagerService, params.transport,
-                        params.packageInfo)).run();
+                Runnable task =
+                        new PerformClearTask(
+                                backupManagerService,
+                                params.transportClient,
+                                params.packageInfo,
+                                params.listener);
+                task.run();
                 break;
             }
 
diff --git a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
index 7af01ea..84ca59b 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformClearTask.java
@@ -23,46 +23,54 @@
 
 import com.android.internal.backup.IBackupTransport;
 import com.android.server.backup.RefactoredBackupManagerService;
+import com.android.server.backup.transport.TransportClient;
 
 import java.io.File;
 
 public class PerformClearTask implements Runnable {
-
-    private RefactoredBackupManagerService backupManagerService;
-    IBackupTransport mTransport;
-    PackageInfo mPackage;
+    private final RefactoredBackupManagerService mBackupManagerService;
+    private final TransportClient mTransportClient;
+    private final PackageInfo mPackage;
+    private final OnTaskFinishedListener mListener;
 
     PerformClearTask(RefactoredBackupManagerService backupManagerService,
-            IBackupTransport transport, PackageInfo packageInfo) {
-        this.backupManagerService = backupManagerService;
-        mTransport = transport;
+            TransportClient transportClient, PackageInfo packageInfo,
+            OnTaskFinishedListener listener) {
+        mBackupManagerService = backupManagerService;
+        mTransportClient = transportClient;
         mPackage = packageInfo;
+        mListener = listener;
     }
 
     public void run() {
+        String callerLogString = "PerformClearTask.run()";
+        IBackupTransport transport = null;
         try {
             // Clear the on-device backup state to ensure a full backup next time
-            File stateDir = new File(backupManagerService.getBaseStateDir(),
-                    mTransport.transportDirName());
+            File stateDir = new File(mBackupManagerService.getBaseStateDir(),
+                    mTransportClient.getTransportDirName());
             File stateFile = new File(stateDir, mPackage.packageName);
             stateFile.delete();
 
+            transport = mTransportClient.connectOrThrow(callerLogString);
             // Tell the transport to remove all the persistent storage for the app
             // TODO - need to handle failures
-            mTransport.clearBackupData(mPackage);
+            transport.clearBackupData(mPackage);
         } catch (Exception e) {
             Slog.e(TAG, "Transport threw clearing data for " + mPackage + ": " + e.getMessage());
         } finally {
-            try {
-                // TODO - need to handle failures
-                mTransport.finishBackup();
-            } catch (Exception e) {
-                // Nothing we can do here, alas
-                Slog.e(TAG, "Unable to mark clear operation finished: " + e.getMessage());
+            if (transport != null) {
+                try {
+                    // TODO - need to handle failures
+                    transport.finishBackup();
+                } catch (Exception e) {
+                    // Nothing we can do here, alas
+                    Slog.e(TAG, "Unable to mark clear operation finished: " + e.getMessage());
+                }
             }
-
+            mListener.onFinished(callerLogString);
             // Last but not least, release the cpu
-            backupManagerService.getWakelock().release();
+            mBackupManagerService.getWakelock().release();
         }
     }
 }
diff --git a/services/backup/java/com/android/server/backup/params/ClearParams.java b/services/backup/java/com/android/server/backup/params/ClearParams.java
index d744efc..dc3bba0 100644
--- a/services/backup/java/com/android/server/backup/params/ClearParams.java
+++ b/services/backup/java/com/android/server/backup/params/ClearParams.java
@@ -18,15 +18,20 @@
 
 import android.content.pm.PackageInfo;
 
-import com.android.internal.backup.IBackupTransport;
+import com.android.server.backup.internal.OnTaskFinishedListener;
+import com.android.server.backup.transport.TransportClient;
 
 public class ClearParams {
-
-    public IBackupTransport transport;
+    public TransportClient transportClient;
     public PackageInfo packageInfo;
+    public OnTaskFinishedListener listener;
 
-    public ClearParams(IBackupTransport _transport, PackageInfo _info) {
-        transport = _transport;
-        packageInfo = _info;
+    public ClearParams(
+            TransportClient transportClient,
+            PackageInfo packageInfo,
+            OnTaskFinishedListener listener) {
+        this.transportClient = transportClient;
+        this.packageInfo = packageInfo;
+        this.listener = listener;
     }
 }
diff --git a/services/backup/java/com/android/server/backup/params/ClearRetryParams.java b/services/backup/java/com/android/server/backup/params/ClearRetryParams.java
index fcf66e4..41b5641 100644
--- a/services/backup/java/com/android/server/backup/params/ClearRetryParams.java
+++ b/services/backup/java/com/android/server/backup/params/ClearRetryParams.java
@@ -17,12 +17,11 @@
 package com.android.server.backup.params;
 
 public class ClearRetryParams {
-
     public String transportName;
     public String packageName;
 
-    public ClearRetryParams(String transport, String pkg) {
-        transportName = transport;
-        packageName = pkg;
+    public ClearRetryParams(String transportName, String packageName) {
+        this.transportName = transportName;
+        this.packageName = packageName;
     }
 }
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index f2f01cf..d44fe4d 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -21,6 +21,7 @@
 import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.Preconditions.checkState;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
 
 import android.Manifest;
 import android.annotation.CheckResult;
@@ -69,6 +70,7 @@
 import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
 
@@ -440,32 +442,35 @@
             return;
         }
 
-        Binder.withCleanCallingIdentity(() -> {
-            try {
-                if (containsEither(packageInfo.requestedPermissions,
-                        Manifest.permission.RUN_IN_BACKGROUND,
-                        Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) {
-                    mIdleController.addPowerSaveWhitelistApp(packageInfo.packageName);
-                } else {
-                    mIdleController.removePowerSaveWhitelistApp(packageInfo.packageName);
-                }
-            } catch (RemoteException e) {
-                /* ignore - local call */
-            }
+        Binder.withCleanCallingIdentity(obtainRunnable(CompanionDeviceManagerService::
+                updateSpecialAccessPermissionAsSystem, this, packageInfo).recycleOnUse());
+    }
 
-            NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(getContext());
+    private void updateSpecialAccessPermissionAsSystem(PackageInfo packageInfo) {
+        try {
             if (containsEither(packageInfo.requestedPermissions,
-                    Manifest.permission.USE_DATA_IN_BACKGROUND,
-                    Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)) {
-                networkPolicyManager.addUidPolicy(
-                        packageInfo.applicationInfo.uid,
-                        NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
+                    android.Manifest.permission.RUN_IN_BACKGROUND,
+                    android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND)) {
+                mIdleController.addPowerSaveWhitelistApp(packageInfo.packageName);
             } else {
-                networkPolicyManager.removeUidPolicy(
-                        packageInfo.applicationInfo.uid,
-                        NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
+                mIdleController.removePowerSaveWhitelistApp(packageInfo.packageName);
             }
-        });
+        } catch (RemoteException e) {
+            /* ignore - local call */
+        }
+
+        NetworkPolicyManager networkPolicyManager = NetworkPolicyManager.from(getContext());
+        if (containsEither(packageInfo.requestedPermissions,
+                android.Manifest.permission.USE_DATA_IN_BACKGROUND,
+                android.Manifest.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND)) {
+            networkPolicyManager.addUidPolicy(
+                    packageInfo.applicationInfo.uid,
+                    NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
+        } else {
+            networkPolicyManager.removeUidPolicy(
+                    packageInfo.applicationInfo.uid,
+                    NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND);
+        }
     }
 
     private static <T> boolean containsEither(T[] array, T a, T b) {
@@ -474,17 +479,17 @@
 
     @Nullable
     private PackageInfo getPackageInfo(String packageName, int userId) {
-        return Binder.withCleanCallingIdentity(() -> {
+        return Binder.withCleanCallingIdentity(PooledLambda.obtainSupplier((context, pkg, id) -> {
             try {
-                return getContext().getPackageManager().getPackageInfoAsUser(
-                        packageName,
+                return context.getPackageManager().getPackageInfoAsUser(
+                        pkg,
                         PackageManager.GET_PERMISSIONS | PackageManager.GET_CONFIGURATIONS,
-                        userId);
+                        id);
             } catch (PackageManager.NameNotFoundException e) {
-                Slog.e(LOG_TAG, "Failed to get PackageInfo for package " + packageName, e);
+                Slog.e(LOG_TAG, "Failed to get PackageInfo for package " + pkg, e);
                 return null;
             }
-        });
+        }, getContext(), packageName, userId).recycleOnUse());
     }
 
     private void recordAssociation(String priviledgedPackage, String deviceAddress) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 04279a3..763a4e4 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -67,6 +67,7 @@
 import java.io.PrintWriter;
 import java.util.HashMap;
 import java.util.LinkedList;
+import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -79,15 +80,14 @@
     private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
     private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
 
-    private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID="bluetooth_addr_valid";
-    private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address";
-    private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name";
+    private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID = "bluetooth_addr_valid";
+    private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS = "bluetooth_address";
+    private static final String SECURE_SETTINGS_BLUETOOTH_NAME = "bluetooth_name";
 
     private static final int ACTIVE_LOG_MAX_SIZE = 20;
     private static final int CRASH_LOG_MAX_SIZE = 100;
     private static final String REASON_AIRPLANE_MODE = "airplane mode";
     private static final String REASON_DISALLOWED = "disallowed by system";
-    private static final String REASON_SHARING_DISALLOWED = "sharing disallowed by system";
     private static final String REASON_RESTARTED = "automatic restart";
     private static final String REASON_START_CRASH = "turn-on crash";
     private static final String REASON_SYSTEM_BOOT = "system boot";
@@ -130,14 +130,14 @@
     private static final int MAX_ERROR_RESTART_RETRIES = 6;
 
     // Bluetooth persisted setting is off
-    private static final int BLUETOOTH_OFF=0;
+    private static final int BLUETOOTH_OFF = 0;
     // Bluetooth persisted setting is on
     // and Airplane mode won't affect Bluetooth state at start up
-    private static final int BLUETOOTH_ON_BLUETOOTH=1;
+    private static final int BLUETOOTH_ON_BLUETOOTH = 1;
     // Bluetooth persisted setting is on
     // but Airplane mode will affect Bluetooth state at start up
     // and Airplane mode will have higher priority.
-    private static final int BLUETOOTH_ON_AIRPLANE=2;
+    private static final int BLUETOOTH_ON_AIRPLANE = 2;
 
     private static final int SERVICE_IBLUETOOTH = 1;
     private static final int SERVICE_IBLUETOOTHGATT = 2;
@@ -154,8 +154,7 @@
     private IBinder mBluetoothBinder;
     private IBluetooth mBluetooth;
     private IBluetoothGatt mBluetoothGatt;
-    private final ReentrantReadWriteLock mBluetoothLock =
-        new ReentrantReadWriteLock();
+    private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock();
     private boolean mBinding;
     private boolean mUnbinding;
 
@@ -175,7 +174,7 @@
         private boolean mEnable;
         private long mTimestamp;
 
-        public ActiveLog(String packageName, boolean enable, long timestamp) {
+        ActiveLog(String packageName, boolean enable, long timestamp) {
             mPackageName = packageName;
             mEnable = enable;
             mTimestamp = timestamp;
@@ -186,8 +185,8 @@
         }
 
         public String toString() {
-            return  timeToLog(mTimestamp) + (mEnable ? "  Enabled " : " Disabled ") + " by "
-                + mPackageName;
+            return timeToLog(mTimestamp) + (mEnable ? "  Enabled " : " Disabled ") + " by "
+                    + mPackageName;
         }
 
     }
@@ -203,7 +202,8 @@
     private boolean mEnableExternal;
 
     // Map of apps registered to keep BLE scanning on.
-    private Map<IBinder, ClientDeathRecipient> mBleApps = new ConcurrentHashMap<IBinder, ClientDeathRecipient>();
+    private Map<IBinder, ClientDeathRecipient> mBleApps =
+            new ConcurrentHashMap<IBinder, ClientDeathRecipient>();
 
     private int mState;
     private final BluetoothHandler mHandler;
@@ -212,51 +212,52 @@
 
     // Save a ProfileServiceConnections object for each of the bound
     // bluetooth profile services
-    private final Map <Integer, ProfileServiceConnections> mProfileServices =
-            new HashMap <Integer, ProfileServiceConnections>();
+    private final Map<Integer, ProfileServiceConnections> mProfileServices =
+            new HashMap<Integer, ProfileServiceConnections>();
 
     private final boolean mPermissionReviewRequired;
 
     private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() {
         @Override
-        public void onBluetoothStateChange(int prevState, int newState) throws RemoteException  {
-            Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE,prevState,newState);
+        public void onBluetoothStateChange(int prevState, int newState) throws RemoteException {
+            Message msg =
+                    mHandler.obtainMessage(MESSAGE_BLUETOOTH_STATE_CHANGE, prevState, newState);
             mHandler.sendMessage(msg);
         }
     };
 
     private final UserRestrictionsListener mUserRestrictionsListener =
             new UserRestrictionsListener() {
-        @Override
-        public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
-                Bundle prevRestrictions) {
+                @Override
+                public void onUserRestrictionsChanged(int userId, Bundle newRestrictions,
+                        Bundle prevRestrictions) {
 
-            if (UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions,
-                    UserManager.DISALLOW_BLUETOOTH_SHARING)) {
-                updateOppLauncherComponentState(userId, newRestrictions.getBoolean(
-                        UserManager.DISALLOW_BLUETOOTH_SHARING));
-            }
+                    if (UserRestrictionsUtils.restrictionsChanged(prevRestrictions, newRestrictions,
+                            UserManager.DISALLOW_BLUETOOTH_SHARING)) {
+                        updateOppLauncherComponentState(userId,
+                                newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH_SHARING));
+                    }
 
-            // DISALLOW_BLUETOOTH can only be set by DO or PO on the system user.
-            if (userId == UserHandle.USER_SYSTEM &&
-                UserRestrictionsUtils.restrictionsChanged(
-                    prevRestrictions, newRestrictions, UserManager.DISALLOW_BLUETOOTH)) {
-                if (userId == UserHandle.USER_SYSTEM && newRestrictions.getBoolean(
-                        UserManager.DISALLOW_BLUETOOTH)) {
-                    updateOppLauncherComponentState(userId, true); // Sharing disallowed
-                    sendDisableMsg(REASON_DISALLOWED);
-                } else {
-                    updateOppLauncherComponentState(userId, newRestrictions.getBoolean(
-                            UserManager.DISALLOW_BLUETOOTH_SHARING));
+                    // DISALLOW_BLUETOOTH can only be set by DO or PO on the system user.
+                    if (userId == UserHandle.USER_SYSTEM
+                            && UserRestrictionsUtils.restrictionsChanged(prevRestrictions,
+                            newRestrictions, UserManager.DISALLOW_BLUETOOTH)) {
+                        if (userId == UserHandle.USER_SYSTEM && newRestrictions.getBoolean(
+                                UserManager.DISALLOW_BLUETOOTH)) {
+                            updateOppLauncherComponentState(userId, true); // Sharing disallowed
+                            sendDisableMsg(REASON_DISALLOWED);
+                        } else {
+                            updateOppLauncherComponentState(userId, newRestrictions.getBoolean(
+                                    UserManager.DISALLOW_BLUETOOTH_SHARING));
+                        }
+                    }
                 }
-            }
-        }
-    };
+            };
 
     private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) {
         @Override
         public void onChange(boolean unused) {
-            synchronized(this) {
+            synchronized (this) {
                 if (isBluetoothPersistedStateOn()) {
                     if (isAirplaneModeOn()) {
                         persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE);
@@ -278,8 +279,9 @@
                     mBluetoothLock.readLock().unlock();
                 }
 
-                Slog.d(TAG, "Airplane Mode change - current state:  " +
-                          BluetoothAdapter.nameForState(st));
+                Slog.d(TAG,
+                        "Airplane Mode change - current state:  " + BluetoothAdapter.nameForState(
+                                st));
 
                 if (isAirplaneModeOn()) {
                     // Clear registered LE apps to force shut-off
@@ -295,11 +297,11 @@
                                 mEnableExternal = false;
                             }
                         } catch (RemoteException e) {
-                            Slog.e(TAG,"Unable to call onBrEdrDown", e);
+                            Slog.e(TAG, "Unable to call onBrEdrDown", e);
                         } finally {
                             mBluetoothLock.readLock().unlock();
                         }
-                    } else if (st == BluetoothAdapter.STATE_ON){
+                    } else if (st == BluetoothAdapter.STATE_ON) {
                         sendDisableMsg(REASON_AIRPLANE_MODE);
                     }
                 } else if (mEnableExternal) {
@@ -315,35 +317,42 @@
             String action = intent.getAction();
             if (BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED.equals(action)) {
                 String newName = intent.getStringExtra(BluetoothAdapter.EXTRA_LOCAL_NAME);
-                if (DBG) Slog.d(TAG, "Bluetooth Adapter name changed to " + newName);
+                if (DBG) {
+                    Slog.d(TAG, "Bluetooth Adapter name changed to " + newName);
+                }
                 if (newName != null) {
                     storeNameAndAddress(newName, null);
                 }
             } else if (BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED.equals(action)) {
                 String newAddress = intent.getStringExtra(BluetoothAdapter.EXTRA_BLUETOOTH_ADDRESS);
                 if (newAddress != null) {
-                    if (DBG) Slog.d(TAG, "Bluetooth Adapter address changed to " + newAddress);
+                    if (DBG) {
+                        Slog.d(TAG, "Bluetooth Adapter address changed to " + newAddress);
+                    }
                     storeNameAndAddress(null, newAddress);
                 } else {
-                    if (DBG) Slog.e(TAG, "No Bluetooth Adapter address parameter found");
+                    if (DBG) {
+                        Slog.e(TAG, "No Bluetooth Adapter address parameter found");
+                    }
                 }
             } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
                 final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
                 if (Settings.Global.BLUETOOTH_ON.equals(name)) {
                     // The Bluetooth On state may be changed during system restore.
-                    final String prevValue = intent.getStringExtra(
-                            Intent.EXTRA_SETTING_PREVIOUS_VALUE);
-                    final String newValue = intent.getStringExtra(
-                            Intent.EXTRA_SETTING_NEW_VALUE);
+                    final String prevValue =
+                            intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE);
+                    final String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
 
-                    if (DBG) Slog.d(TAG, "ACTION_SETTING_RESTORED with BLUETOOTH_ON, prevValue=" +
-                                    prevValue + ", newValue=" + newValue);
+                    if (DBG) {
+                        Slog.d(TAG,
+                                "ACTION_SETTING_RESTORED with BLUETOOTH_ON, prevValue=" + prevValue
+                                        + ", newValue=" + newValue);
+                    }
 
                     if ((newValue != null) && (prevValue != null) && !prevValue.equals(newValue)) {
                         Message msg = mHandler.obtainMessage(MESSAGE_RESTORE_USER_SETTING,
-                                                             newValue.equals("0") ?
-                                                             RESTORE_SETTING_TO_OFF :
-                                                             RESTORE_SETTING_TO_ON, 0);
+                                newValue.equals("0") ? RESTORE_SETTING_TO_OFF
+                                        : RESTORE_SETTING_TO_ON, 0);
                         mHandler.sendMessage(msg);
                     }
                 }
@@ -356,8 +365,8 @@
 
         mContext = context;
 
-        mPermissionReviewRequired = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_permissionReviewRequired);
+        mPermissionReviewRequired = context.getResources()
+                .getBoolean(com.android.internal.R.bool.config_permissionReviewRequired);
 
         mActiveLogs = new LinkedList<ActiveLog>();
         mCrashTimestamps = new LinkedList<Long>();
@@ -389,23 +398,26 @@
 
         loadStoredNameAndAddress();
         if (isBluetoothPersistedStateOn()) {
-            if (DBG) Slog.d(TAG, "Startup: Bluetooth persisted state is ON.");
+            if (DBG) {
+                Slog.d(TAG, "Startup: Bluetooth persisted state is ON.");
+            }
             mEnableExternal = true;
         }
 
-        String airplaneModeRadios = Settings.Global.getString(mContentResolver,
-            Settings.Global.AIRPLANE_MODE_RADIOS);
-        if (airplaneModeRadios == null ||
-            airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH)) {
+        String airplaneModeRadios =
+                Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS);
+        if (airplaneModeRadios == null || airplaneModeRadios.contains(
+                Settings.Global.RADIO_BLUETOOTH)) {
             mContentResolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
-                true, mAirplaneModeObserver);
+                    Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
+                    mAirplaneModeObserver);
         }
 
         int systemUiUid = -1;
         try {
-            systemUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui",
-                    PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
+            systemUiUid = mContext.getPackageManager()
+                    .getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY,
+                            UserHandle.USER_SYSTEM);
         } catch (PackageManager.NameNotFoundException e) {
             // Some platforms, such as wearables do not have a system ui.
             Slog.w(TAG, "Unable to resolve SystemUI's UID.", e);
@@ -416,7 +428,7 @@
     /**
      *  Returns true if airplane mode is currently on
      */
-    private final boolean isAirplaneModeOn() {
+    private boolean isAirplaneModeOn() {
         return Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
     }
@@ -424,31 +436,32 @@
     /**
      *  Returns true if the Bluetooth saved state is "on"
      */
-    private final boolean isBluetoothPersistedStateOn() {
-        int state = Settings.Global.getInt(mContentResolver,
-                                           Settings.Global.BLUETOOTH_ON, -1);
-        if (DBG) Slog.d(TAG, "Bluetooth persisted state: " + state);
+    private boolean isBluetoothPersistedStateOn() {
+        int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1);
+        if (DBG) {
+            Slog.d(TAG, "Bluetooth persisted state: " + state);
+        }
         return state != BLUETOOTH_OFF;
     }
 
     /**
      *  Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH
      */
-    private final boolean isBluetoothPersistedStateOnBluetooth() {
-        return Settings.Global.getInt(mContentResolver,
-                Settings.Global.BLUETOOTH_ON, BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH;
+    private boolean isBluetoothPersistedStateOnBluetooth() {
+        return Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON,
+                BLUETOOTH_ON_BLUETOOTH) == BLUETOOTH_ON_BLUETOOTH;
     }
 
     /**
      *  Save the Bluetooth on/off state
      */
     private void persistBluetoothSetting(int value) {
-        if (DBG) Slog.d(TAG, "Persisting Bluetooth Setting: " + value);
+        if (DBG) {
+            Slog.d(TAG, "Persisting Bluetooth Setting: " + value);
+        }
         // waive WRITE_SECURE_SETTINGS permission check
         long callingIdentity = Binder.clearCallingIdentity();
-        Settings.Global.putInt(mContext.getContentResolver(),
-                               Settings.Global.BLUETOOTH_ON,
-                               value);
+        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.BLUETOOTH_ON, value);
         Binder.restoreCallingIdentity(callingIdentity);
     }
 
@@ -458,7 +471,7 @@
      * @return
      */
     private boolean isNameAndAddressSet() {
-        return mName !=null && mAddress!= null && mName.length()>0 && mAddress.length()>0;
+        return mName != null && mAddress != null && mName.length() > 0 && mAddress.length() > 0;
     }
 
     /**
@@ -466,17 +479,24 @@
      * in the local cache
      */
     private void loadStoredNameAndAddress() {
-        if (DBG) Slog.d(TAG, "Loading stored name and address");
-        if (mContext.getResources().getBoolean
-            (com.android.internal.R.bool.config_bluetooth_address_validation) &&
-             Settings.Secure.getInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0) == 0) {
+        if (DBG) {
+            Slog.d(TAG, "Loading stored name and address");
+        }
+        if (mContext.getResources()
+                .getBoolean(com.android.internal.R.bool.config_bluetooth_address_validation)
+                && Settings.Secure.getInt(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDR_VALID, 0)
+                == 0) {
             // if the valid flag is not set, don't load the address and name
-            if (DBG) Slog.d(TAG, "invalid bluetooth name and address stored");
+            if (DBG) {
+                Slog.d(TAG, "invalid bluetooth name and address stored");
+            }
             return;
         }
         mName = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME);
         mAddress = Settings.Secure.getString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS);
-        if (DBG) Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress);
+        if (DBG) {
+            Slog.d(TAG, "Stored bluetooth Name=" + mName + ",Address=" + mAddress);
+        }
     }
 
     /**
@@ -489,15 +509,20 @@
         if (name != null) {
             Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_NAME, name);
             mName = name;
-            if (DBG) Slog.d(TAG,"Stored Bluetooth name: " +
-                Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_NAME));
+            if (DBG) {
+                Slog.d(TAG, "Stored Bluetooth name: " + Settings.Secure.getString(mContentResolver,
+                        SECURE_SETTINGS_BLUETOOTH_NAME));
+            }
         }
 
         if (address != null) {
             Settings.Secure.putString(mContentResolver, SECURE_SETTINGS_BLUETOOTH_ADDRESS, address);
-            mAddress=address;
-            if (DBG)  Slog.d(TAG,"Stored Bluetoothaddress: " +
-                Settings.Secure.getString(mContentResolver,SECURE_SETTINGS_BLUETOOTH_ADDRESS));
+            mAddress = address;
+            if (DBG) {
+                Slog.d(TAG,
+                        "Stored Bluetoothaddress: " + Settings.Secure.getString(mContentResolver,
+                                SECURE_SETTINGS_BLUETOOTH_ADDRESS));
+            }
         }
 
         if ((name != null) && (address != null)) {
@@ -505,7 +530,7 @@
         }
     }
 
-    public IBluetooth registerAdapter(IBluetoothManagerCallback callback){
+    public IBluetooth registerAdapter(IBluetoothManagerCallback callback) {
         if (callback == null) {
             Slog.w(TAG, "Callback is null in registerAdapter");
             return null;
@@ -522,19 +547,17 @@
             Slog.w(TAG, "Callback is null in unregisterAdapter");
             return;
         }
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
-                                                "Need BLUETOOTH permission");
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_ADAPTER);
         msg.obj = callback;
         mHandler.sendMessage(msg);
     }
 
     public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
-                                                "Need BLUETOOTH permission");
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         if (callback == null) {
-          Slog.w(TAG, "registerStateChangeCallback: Callback is null!");
-          return;
+            Slog.w(TAG, "registerStateChangeCallback: Callback is null!");
+            return;
         }
         Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_STATE_CHANGE_CALLBACK);
         msg.obj = callback;
@@ -542,11 +565,10 @@
     }
 
     public void unregisterStateChangeCallback(IBluetoothStateChangeCallback callback) {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
-                                                "Need BLUETOOTH permission");
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
         if (callback == null) {
-          Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!");
-          return;
+            Slog.w(TAG, "unregisterStateChangeCallback: Callback is null!");
+            return;
         }
         Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK);
         msg.obj = callback;
@@ -554,15 +576,16 @@
     }
 
     public boolean isEnabled() {
-        if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
-            (!checkIfCallerIsForegroundUser())) {
-            Slog.w(TAG,"isEnabled(): not allowed for non-active and non system user");
+        if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
+            Slog.w(TAG, "isEnabled(): not allowed for non-active and non system user");
             return false;
         }
 
         try {
             mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) return mBluetooth.isEnabled();
+            if (mBluetooth != null) {
+                return mBluetooth.isEnabled();
+            }
         } catch (RemoteException e) {
             Slog.e(TAG, "isEnabled()", e);
         } finally {
@@ -572,15 +595,16 @@
     }
 
     public int getState() {
-        if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
-                (!checkIfCallerIsForegroundUser())) {
+        if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
             Slog.w(TAG, "getState(): report OFF for non-active and non system user");
             return BluetoothAdapter.STATE_OFF;
         }
 
         try {
             mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) return mBluetooth.getState();
+            if (mBluetooth != null) {
+                return mBluetooth.getState();
+            }
         } catch (RemoteException e) {
             Slog.e(TAG, "getState()", e);
         } finally {
@@ -592,26 +616,29 @@
     class ClientDeathRecipient implements IBinder.DeathRecipient {
         private String mPackageName;
 
-        public ClientDeathRecipient(String packageName) {
+        ClientDeathRecipient(String packageName) {
             mPackageName = packageName;
         }
 
         public void binderDied() {
-            if (DBG) Slog.d(TAG, "Binder is dead - unregister " + mPackageName);
-            if (isBleAppPresent()) {
-              // Nothing to do, another app is here.
-              return;
+            if (DBG) {
+                Slog.d(TAG, "Binder is dead - unregister " + mPackageName);
             }
-            if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash");
+            if (isBleAppPresent()) {
+                // Nothing to do, another app is here.
+                return;
+            }
+            if (DBG) {
+                Slog.d(TAG, "Disabling LE only mode after application crash");
+            }
             try {
                 mBluetoothLock.readLock().lock();
-                if (mBluetooth != null &&
-                    mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
+                if (mBluetooth != null && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
                     mEnable = false;
                     mBluetooth.onBrEdrDown();
                 }
             } catch (RemoteException e) {
-                 Slog.e(TAG,"Unable to call onBrEdrDown", e);
+                Slog.e(TAG, "Unable to call onBrEdrDown", e);
             } finally {
                 mBluetoothLock.readLock().unlock();
             }
@@ -641,15 +668,17 @@
             @Override
             public void onChange(boolean selfChange) {
                 if (isBleScanAlwaysAvailable()) {
-                  // Nothing to do
-                  return;
+                    // Nothing to do
+                    return;
                 }
                 // BLE scan is not available.
                 disableBleScanMode();
                 clearBleApps();
                 try {
                     mBluetoothLock.readLock().lock();
-                    if (mBluetooth != null) mBluetooth.onBrEdrDown();
+                    if (mBluetooth != null) {
+                        mBluetooth.onBrEdrDown();
+                    }
                 } catch (RemoteException e) {
                     Slog.e(TAG, "error when disabling bluetooth", e);
                 } finally {
@@ -659,8 +688,8 @@
         };
 
         mContentResolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE),
-                false, contentObserver);
+                Settings.Global.getUriFor(Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE), false,
+                contentObserver);
     }
 
     // Disable ble scan only mode.
@@ -668,7 +697,9 @@
         try {
             mBluetoothLock.writeLock().lock();
             if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) {
-                if (DBG) Slog.d(TAG, "Reseting the mEnable flag for clean disable");
+                if (DBG) {
+                    Slog.d(TAG, "Reseting the mEnable flag for clean disable");
+                }
                 mEnable = false;
             }
         } catch (RemoteException e) {
@@ -688,15 +719,21 @@
                 throw new IllegalArgumentException("BLE app (" + packageName + ") already dead!");
             }
             mBleApps.put(token, deathRec);
-            if (DBG) Slog.d(TAG, "Registered for death of " + packageName);
+            if (DBG) {
+                Slog.d(TAG, "Registered for death of " + packageName);
+            }
         } else if (!enable && r != null) {
             // Unregister death recipient as the app goes away.
             token.unlinkToDeath(r, 0);
             mBleApps.remove(token);
-            if (DBG) Slog.d(TAG, "Unregistered for death of " + packageName);
+            if (DBG) {
+                Slog.d(TAG, "Unregistered for death of " + packageName);
+            }
         }
         int appCount = mBleApps.size();
-        if (DBG) Slog.d(TAG, appCount + " registered Ble Apps");
+        if (DBG) {
+            Slog.d(TAG, appCount + " registered Ble Apps");
+        }
         if (appCount == 0 && mEnable) {
             disableBleScanMode();
         }
@@ -713,7 +750,9 @@
 
     /** @hide */
     public boolean isBleAppPresent() {
-        if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size());
+        if (DBG) {
+            Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size());
+        }
         return mBleApps.size() > 0;
     }
 
@@ -721,17 +760,23 @@
      * Action taken when GattService is turned on
      */
     private void onBluetoothGattServiceUp() {
-        if (DBG) Slog.d(TAG,"BluetoothGatt Service is Up");
+        if (DBG) {
+            Slog.d(TAG, "BluetoothGatt Service is Up");
+        }
         try {
             mBluetoothLock.readLock().lock();
             if (mBluetooth == null) {
-                if (DBG) Slog.w(TAG, "onBluetoothServiceUp: mBluetooth is null!");
+                if (DBG) {
+                    Slog.w(TAG, "onBluetoothServiceUp: mBluetooth is null!");
+                }
                 return;
             }
             int st = mBluetooth.getState();
             if (st != BluetoothAdapter.STATE_BLE_ON) {
-                if (DBG) Slog.v(TAG, "onBluetoothServiceUp: state isn't BLE_ON: " +
-                        BluetoothAdapter.nameForState(st));
+                if (DBG) {
+                    Slog.v(TAG, "onBluetoothServiceUp: state isn't BLE_ON: "
+                            + BluetoothAdapter.nameForState(st));
+                }
                 return;
             }
             if (isBluetoothPersistedStateOnBluetooth() || !isBleAppPresent()) {
@@ -740,7 +785,7 @@
                 persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
             }
         } catch (RemoteException e) {
-            Slog.e(TAG,"Unable to call onServiceUp", e);
+            Slog.e(TAG, "Unable to call onServiceUp", e);
         } finally {
             mBluetoothLock.readLock().unlock();
         }
@@ -751,7 +796,9 @@
      * and turn off all service and stack if no LE app needs it
      */
     private void sendBrEdrDownCallback() {
-        if (DBG) Slog.d(TAG,"Calling sendBrEdrDownCallback callbacks");
+        if (DBG) {
+            Slog.d(TAG, "Calling sendBrEdrDownCallback callbacks");
+        }
 
         if (mBluetooth == null) {
             Slog.w(TAG, "Bluetooth handle is null");
@@ -768,7 +815,9 @@
         } else {
             try {
                 mBluetoothLock.readLock().lock();
-                if (mBluetooth != null) mBluetooth.onBrEdrDown();
+                if (mBluetooth != null) {
+                    mBluetooth.onBrEdrDown();
+                }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Call to onBrEdrDown() failed.", e);
             } finally {
@@ -778,8 +827,7 @@
 
     }
 
-    public boolean enableNoAutoConnect(String packageName)
-    {
+    public boolean enableNoAutoConnect(String packageName) {
         if (isBluetoothDisallowed()) {
             if (DBG) {
                 Slog.d(TAG, "enableNoAutoConnect(): not enabling - bluetooth disallowed");
@@ -788,11 +836,11 @@
         }
 
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
-                                                "Need BLUETOOTH ADMIN permission");
+                "Need BLUETOOTH ADMIN permission");
 
         if (DBG) {
-            Slog.d(TAG,"enableNoAutoConnect():  mBluetooth =" + mBluetooth +
-                    " mBinding = " + mBinding);
+            Slog.d(TAG, "enableNoAutoConnect():  mBluetooth =" + mBluetooth + " mBinding = "
+                    + mBinding);
         }
         int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
 
@@ -800,7 +848,7 @@
             throw new SecurityException("no permission to enable Bluetooth quietly");
         }
 
-        synchronized(mReceiver) {
+        synchronized (mReceiver) {
             mQuietEnableExternal = true;
             mEnableExternal = true;
             sendEnableMsg(true, packageName);
@@ -814,7 +862,7 @@
 
         if (isBluetoothDisallowed()) {
             if (DBG) {
-                Slog.d(TAG,"enable(): not enabling - bluetooth disallowed");
+                Slog.d(TAG, "enable(): not enabling - bluetooth disallowed");
             }
             return false;
         }
@@ -828,26 +876,26 @@
             mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                     "Need BLUETOOTH ADMIN permission");
 
-            if (!isEnabled() && mPermissionReviewRequired
-                    && startConsentUiIfNeeded(packageName, callingUid,
-                            BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
+            if (!isEnabled() && mPermissionReviewRequired && startConsentUiIfNeeded(packageName,
+                    callingUid, BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
                 return false;
             }
         }
 
         if (DBG) {
-            Slog.d(TAG,"enable(" + packageName + "):  mBluetooth =" + mBluetooth +
-                    " mBinding = " + mBinding + " mState = " +
-                    BluetoothAdapter.nameForState(mState));
+            Slog.d(TAG, "enable(" + packageName + "):  mBluetooth =" + mBluetooth + " mBinding = "
+                    + mBinding + " mState = " + BluetoothAdapter.nameForState(mState));
         }
 
-        synchronized(mReceiver) {
+        synchronized (mReceiver) {
             mQuietEnableExternal = false;
             mEnableExternal = true;
             // waive WRITE_SECURE_SETTINGS permission check
             sendEnableMsg(false, packageName);
         }
-        if (DBG) Slog.d(TAG, "enable returning");
+        if (DBG) {
+            Slog.d(TAG, "enable returning");
+        }
         return true;
     }
 
@@ -864,19 +912,17 @@
             mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                     "Need BLUETOOTH ADMIN permission");
 
-            if (isEnabled() && mPermissionReviewRequired
-                    && startConsentUiIfNeeded(packageName, callingUid,
-                            BluetoothAdapter.ACTION_REQUEST_DISABLE)) {
+            if (isEnabled() && mPermissionReviewRequired && startConsentUiIfNeeded(packageName,
+                    callingUid, BluetoothAdapter.ACTION_REQUEST_DISABLE)) {
                 return false;
             }
         }
 
         if (DBG) {
-            Slog.d(TAG,"disable(): mBluetooth = " + mBluetooth +
-                " mBinding = " + mBinding);
+            Slog.d(TAG, "disable(): mBluetooth = " + mBluetooth + " mBinding = " + mBinding);
         }
 
-        synchronized(mReceiver) {
+        synchronized (mReceiver) {
             if (persist) {
                 persistBluetoothSetting(BLUETOOTH_OFF);
             }
@@ -886,8 +932,8 @@
         return true;
     }
 
-    private boolean startConsentUiIfNeeded(String packageName,
-            int callingUid, String intentAction) throws RemoteException {
+    private boolean startConsentUiIfNeeded(String packageName, int callingUid, String intentAction)
+            throws RemoteException {
         try {
             // Validate the package only if we are going to use it
             ApplicationInfo applicationInfo = mContext.getPackageManager()
@@ -895,14 +941,13 @@
                             PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                             UserHandle.getUserId(callingUid));
             if (applicationInfo.uid != callingUid) {
-                throw new SecurityException("Package " + callingUid
-                        + " not in uid " + callingUid);
+                throw new SecurityException("Package " + callingUid + " not in uid " + callingUid);
             }
 
             Intent intent = new Intent(intentAction);
             intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            intent.setFlags(
+                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
             try {
                 mContext.startActivity(intent);
             } catch (ActivityNotFoundException e) {
@@ -918,13 +963,15 @@
 
     public void unbindAndFinish() {
         if (DBG) {
-            Slog.d(TAG,"unbindAndFinish(): " + mBluetooth +
-                " mBinding = " + mBinding + " mUnbinding = " + mUnbinding);
+            Slog.d(TAG, "unbindAndFinish(): " + mBluetooth + " mBinding = " + mBinding
+                    + " mUnbinding = " + mUnbinding);
         }
 
         try {
             mBluetoothLock.writeLock().lock();
-            if (mUnbinding) return;
+            if (mUnbinding) {
+                return;
+            }
             mUnbinding = true;
             mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
             mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE);
@@ -933,7 +980,7 @@
                 try {
                     mBluetooth.unregisterCallback(mBluetoothCallback);
                 } catch (RemoteException re) {
-                    Slog.e(TAG, "Unable to unregister BluetoothCallback",re);
+                    Slog.e(TAG, "Unable to unregister BluetoothCallback", re);
                 }
                 mBluetoothBinder = null;
                 mBluetooth = null;
@@ -959,8 +1006,8 @@
             IBluetoothProfileServiceConnection proxy) {
         if (!mEnable) {
             if (DBG) {
-                Slog.d(TAG, "Trying to bind to profile: " + bluetoothProfile +
-                        ", while Bluetooth was disabled");
+                Slog.d(TAG, "Trying to bind to profile: " + bluetoothProfile
+                        + ", while Bluetooth was disabled");
             }
             return false;
         }
@@ -968,15 +1015,19 @@
             ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile));
             if (psc == null) {
                 if (DBG) {
-                    Slog.d(TAG, "Creating new ProfileServiceConnections object for"
-                            + " profile: " + bluetoothProfile);
+                    Slog.d(TAG, "Creating new ProfileServiceConnections object for" + " profile: "
+                            + bluetoothProfile);
                 }
 
-                if (bluetoothProfile != BluetoothProfile.HEADSET) return false;
+                if (bluetoothProfile != BluetoothProfile.HEADSET) {
+                    return false;
+                }
 
                 Intent intent = new Intent(IBluetoothHeadset.class.getName());
                 psc = new ProfileServiceConnections(intent);
-                if (!psc.bindService()) return false;
+                if (!psc.bindService()) {
+                    return false;
+                }
 
                 mProfileServices.put(new Integer(bluetoothProfile), psc);
             }
@@ -1022,7 +1073,9 @@
      * PHASE_SYSTEM_SERVICES_READY.
      */
     public void handleOnBootPhase() {
-        if (DBG) Slog.d(TAG, "Bluetooth boot completed");
+        if (DBG) {
+            Slog.d(TAG, "Bluetooth boot completed");
+        }
         UserManagerInternal userManagerInternal =
                 LocalServices.getService(UserManagerInternal.class);
         userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener);
@@ -1031,10 +1084,14 @@
             return;
         }
         if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) {
-            if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth.");
+            if (DBG) {
+                Slog.d(TAG, "Auto-enabling Bluetooth.");
+            }
             sendEnableMsg(mQuietEnableExternal, REASON_SYSTEM_BOOT);
         } else if (!isNameAndAddressSet()) {
-            if (DBG) Slog.d(TAG, "Getting adapter name and address");
+            if (DBG) {
+                Slog.d(TAG, "Getting adapter name and address");
+            }
             Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
             mHandler.sendMessage(getMsg);
         }
@@ -1044,7 +1101,9 @@
      * Called when switching to a different foreground user.
      */
     public void handleOnSwitchUser(int userHandle) {
-        if (DBG) Slog.d(TAG, "User " + userHandle + " switched");
+        if (DBG) {
+            Slog.d(TAG, "User " + userHandle + " switched");
+        }
         mHandler.obtainMessage(MESSAGE_USER_SWITCHED, userHandle, 0).sendToTarget();
     }
 
@@ -1052,7 +1111,9 @@
      * Called when user is unlocked.
      */
     public void handleOnUnlockUser(int userHandle) {
-        if (DBG) Slog.d(TAG, "User " + userHandle + " unlocked");
+        if (DBG) {
+            Slog.d(TAG, "User " + userHandle + " unlocked");
+        }
         mHandler.obtainMessage(MESSAGE_USER_UNLOCKED, userHandle, 0).sendToTarget();
     }
 
@@ -1060,10 +1121,10 @@
      * This class manages the clients connected to a given ProfileService
      * and maintains the connection with that service.
      */
-    final private class ProfileServiceConnections implements ServiceConnection,
-            IBinder.DeathRecipient {
+    private final class ProfileServiceConnections
+            implements ServiceConnection, IBinder.DeathRecipient {
         final RemoteCallbackList<IBluetoothProfileServiceConnection> mProxies =
-                new RemoteCallbackList <IBluetoothProfileServiceConnection>();
+                new RemoteCallbackList<IBluetoothProfileServiceConnection>();
         IBinder mService;
         ComponentName mClassName;
         Intent mIntent;
@@ -1076,8 +1137,8 @@
         }
 
         private boolean bindService() {
-            if (mIntent != null && mService == null &&
-                    doBind(mIntent, this, 0, UserHandle.CURRENT_OR_SELF)) {
+            if (mIntent != null && mService == null && doBind(mIntent, this, 0,
+                    UserHandle.CURRENT_OR_SELF)) {
                 Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
                 msg.obj = this;
                 mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS);
@@ -1090,7 +1151,7 @@
         private void addProxy(IBluetoothProfileServiceConnection proxy) {
             mProxies.register(proxy);
             if (mService != null) {
-                try{
+                try {
                     proxy.onServiceConnected(mClassName, mService);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Unable to connect to proxy", e);
@@ -1158,7 +1219,9 @@
 
         @Override
         public void onServiceDisconnected(ComponentName className) {
-            if (mService == null) return;
+            if (mService == null) {
+                return;
+            }
             mService.unlinkToDeath(this, 0);
             mService = null;
             mClassName = null;
@@ -1187,8 +1250,7 @@
         @Override
         public void binderDied() {
             if (DBG) {
-                Slog.w(TAG, "Profile service for profile: " + mClassName
-                        + " died.");
+                Slog.w(TAG, "Profile service for profile: " + mClassName + " died.");
             }
             onServiceDisconnected(mClassName);
             // Trigger rebind
@@ -1201,12 +1263,15 @@
     private void sendBluetoothStateCallback(boolean isUp) {
         try {
             int n = mStateChangeCallbacks.beginBroadcast();
-            if (DBG) Slog.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers.");
-            for (int i=0; i <n;i++) {
+            if (DBG) {
+                Slog.d(TAG, "Broadcasting onBluetoothStateChange(" + isUp + ") to " + n
+                        + " receivers.");
+            }
+            for (int i = 0; i < n; i++) {
                 try {
                     mStateChangeCallbacks.getBroadcastItem(i).onBluetoothStateChange(isUp);
                 } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to call onBluetoothStateChange() on callback #" + i , e);
+                    Slog.e(TAG, "Unable to call onBluetoothStateChange() on callback #" + i, e);
                 }
             }
         } finally {
@@ -1220,11 +1285,11 @@
     private void sendBluetoothServiceUpCallback() {
         try {
             int n = mCallbacks.beginBroadcast();
-            Slog.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
-            for (int i=0; i <n;i++) {
+            Slog.d(TAG, "Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
+            for (int i = 0; i < n; i++) {
                 try {
                     mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
-                }  catch (RemoteException e) {
+                } catch (RemoteException e) {
                     Slog.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
                 }
             }
@@ -1232,17 +1297,18 @@
             mCallbacks.finishBroadcast();
         }
     }
+
     /**
      * Inform BluetoothAdapter instances that Adapter service is down
      */
     private void sendBluetoothServiceDownCallback() {
         try {
             int n = mCallbacks.beginBroadcast();
-            Slog.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
-            for (int i=0; i <n;i++) {
+            Slog.d(TAG, "Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
+            for (int i = 0; i < n; i++) {
                 try {
                     mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
-                }  catch (RemoteException e) {
+                } catch (RemoteException e) {
                     Slog.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
                 }
             }
@@ -1252,12 +1318,10 @@
     }
 
     public String getAddress() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
-                "Need BLUETOOTH permission");
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
 
-        if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
-                (!checkIfCallerIsForegroundUser())) {
-            Slog.w(TAG,"getAddress(): not allowed for non-active and non system user");
+        if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
+            Slog.w(TAG, "getAddress(): not allowed for non-active and non system user");
             return null;
         }
 
@@ -1268,9 +1332,13 @@
 
         try {
             mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) return mBluetooth.getAddress();
+            if (mBluetooth != null) {
+                return mBluetooth.getAddress();
+            }
         } catch (RemoteException e) {
-            Slog.e(TAG, "getAddress(): Unable to retrieve address remotely. Returning cached address", e);
+            Slog.e(TAG,
+                    "getAddress(): Unable to retrieve address remotely. Returning cached address",
+                    e);
         } finally {
             mBluetoothLock.readLock().unlock();
         }
@@ -1282,18 +1350,18 @@
     }
 
     public String getName() {
-        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
-                                                "Need BLUETOOTH permission");
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
 
-        if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
-            (!checkIfCallerIsForegroundUser())) {
-            Slog.w(TAG,"getName(): not allowed for non-active and non system user");
+        if ((Binder.getCallingUid() != Process.SYSTEM_UID) && (!checkIfCallerIsForegroundUser())) {
+            Slog.w(TAG, "getName(): not allowed for non-active and non system user");
             return null;
         }
 
         try {
             mBluetoothLock.readLock().lock();
-            if (mBluetooth != null) return mBluetooth.getName();
+            if (mBluetooth != null) {
+                return mBluetooth.getName();
+            }
         } catch (RemoteException e) {
             Slog.e(TAG, "getName(): Unable to retrieve name remotely. Returning cached name", e);
         } finally {
@@ -1309,7 +1377,9 @@
     private class BluetoothServiceConnection implements ServiceConnection {
         public void onServiceConnected(ComponentName componentName, IBinder service) {
             String name = componentName.getClassName();
-            if (DBG) Slog.d(TAG, "BluetoothServiceConnection: " + name);
+            if (DBG) {
+                Slog.d(TAG, "BluetoothServiceConnection: " + name);
+            }
             Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
             if (name.equals("com.android.bluetooth.btservice.AdapterService")) {
                 msg.arg1 = SERVICE_IBLUETOOTH;
@@ -1326,7 +1396,9 @@
         public void onServiceDisconnected(ComponentName componentName) {
             // Called if we unexpectedly disconnect.
             String name = componentName.getClassName();
-            if (DBG) Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + name);
+            if (DBG) {
+                Slog.d(TAG, "BluetoothServiceConnection, disconnected: " + name);
+            }
             Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
             if (name.equals("com.android.bluetooth.btservice.AdapterService")) {
                 msg.arg1 = SERVICE_IBLUETOOTH;
@@ -1345,7 +1417,7 @@
     private class BluetoothHandler extends Handler {
         boolean mGetNameAddressOnly = false;
 
-        public BluetoothHandler(Looper looper) {
+        BluetoothHandler(Looper looper) {
             super(looper);
         }
 
@@ -1353,26 +1425,29 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MESSAGE_GET_NAME_AND_ADDRESS:
-                    if (DBG) Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS");
+                    if (DBG) {
+                        Slog.d(TAG, "MESSAGE_GET_NAME_AND_ADDRESS");
+                    }
                     try {
                         mBluetoothLock.writeLock().lock();
                         if ((mBluetooth == null) && (!mBinding)) {
-                            if (DBG) Slog.d(TAG, "Binding to service to get name and address");
+                            if (DBG) {
+                                Slog.d(TAG, "Binding to service to get name and address");
+                            }
                             mGetNameAddressOnly = true;
                             Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
                             mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS);
                             Intent i = new Intent(IBluetooth.class.getName());
                             if (!doBind(i, mConnection,
-                                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
-                                UserHandle.CURRENT)) {
+                                    Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
+                                    UserHandle.CURRENT)) {
                                 mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
                             } else {
                                 mBinding = true;
                             }
                         } else if (mBluetooth != null) {
                             try {
-                                storeNameAndAddress(mBluetooth.getName(),
-                                                    mBluetooth.getAddress());
+                                storeNameAndAddress(mBluetooth.getName(), mBluetooth.getAddress());
                             } catch (RemoteException re) {
                                 Slog.e(TAG, "Unable to grab names", re);
                             }
@@ -1431,15 +1506,16 @@
                         // on the order of (2 * SERVICE_RESTART_TIME_MS).
                         //
                         waitForOnOff(false, true);
-                        Message restartMsg = mHandler.obtainMessage(
-                                MESSAGE_RESTART_BLUETOOTH_SERVICE);
-                        mHandler.sendMessageDelayed(restartMsg,
-                                2 * SERVICE_RESTART_TIME_MS);
+                        Message restartMsg =
+                                mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
+                        mHandler.sendMessageDelayed(restartMsg, 2 * SERVICE_RESTART_TIME_MS);
                     }
                     break;
 
                 case MESSAGE_DISABLE:
-                    if (DBG) Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth);
+                    if (DBG) {
+                        Slog.d(TAG, "MESSAGE_DISABLE: mBluetooth = " + mBluetooth);
+                    }
                     mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                     if (mEnable && mBluetooth != null) {
                         waitForOnOff(true, false);
@@ -1455,45 +1531,45 @@
                 case MESSAGE_RESTORE_USER_SETTING:
                     try {
                         if ((msg.arg1 == RESTORE_SETTING_TO_OFF) && mEnable) {
-                            if (DBG) Slog.d(TAG, "Restore Bluetooth state to disabled");
+                            if (DBG) {
+                                Slog.d(TAG, "Restore Bluetooth state to disabled");
+                            }
                             disable(REASON_RESTORE_USER_SETTING, true);
                         } else if ((msg.arg1 == RESTORE_SETTING_TO_ON) && !mEnable) {
-                            if (DBG) Slog.d(TAG, "Restore Bluetooth state to enabled");
+                            if (DBG) {
+                                Slog.d(TAG, "Restore Bluetooth state to enabled");
+                            }
                             enable(REASON_RESTORE_USER_SETTING);
                         }
                     } catch (RemoteException e) {
-                        Slog.e(TAG,"Unable to change Bluetooth On setting", e);
+                        Slog.e(TAG, "Unable to change Bluetooth On setting", e);
                     }
                     break;
 
-                case MESSAGE_REGISTER_ADAPTER:
-                {
+                case MESSAGE_REGISTER_ADAPTER: {
                     IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
                     mCallbacks.register(callback);
                     break;
                 }
-                case MESSAGE_UNREGISTER_ADAPTER:
-                {
+                case MESSAGE_UNREGISTER_ADAPTER: {
                     IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
                     mCallbacks.unregister(callback);
                     break;
                 }
-                case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK:
-                {
-                    IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
+                case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK: {
+                    IBluetoothStateChangeCallback callback =
+                            (IBluetoothStateChangeCallback) msg.obj;
                     mStateChangeCallbacks.register(callback);
                     break;
                 }
-                case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK:
-                {
-                    IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
+                case MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK: {
+                    IBluetoothStateChangeCallback callback =
+                            (IBluetoothStateChangeCallback) msg.obj;
                     mStateChangeCallbacks.unregister(callback);
                     break;
                 }
-                case MESSAGE_ADD_PROXY_DELAYED:
-                {
-                    ProfileServiceConnections psc = mProfileServices.get(
-                            new Integer(msg.arg1));
+                case MESSAGE_ADD_PROXY_DELAYED: {
+                    ProfileServiceConnections psc = mProfileServices.get(new Integer(msg.arg1));
                     if (psc == null) {
                         break;
                     }
@@ -1502,8 +1578,7 @@
                     psc.addProxy(proxy);
                     break;
                 }
-                case MESSAGE_BIND_PROFILE_SERVICE:
-                {
+                case MESSAGE_BIND_PROFILE_SERVICE: {
                     ProfileServiceConnections psc = (ProfileServiceConnections) msg.obj;
                     removeMessages(MESSAGE_BIND_PROFILE_SERVICE, msg.obj);
                     if (psc == null) {
@@ -1512,16 +1587,17 @@
                     psc.bindService();
                     break;
                 }
-                case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
-                {
-                    if (DBG) Slog.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
+                case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: {
+                    if (DBG) {
+                        Slog.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
+                    }
 
                     IBinder service = (IBinder) msg.obj;
                     try {
                         mBluetoothLock.writeLock().lock();
                         if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
-                            mBluetoothGatt = IBluetoothGatt.Stub
-                                    .asInterface(Binder.allowBlocking(service));
+                            mBluetoothGatt =
+                                    IBluetoothGatt.Stub.asInterface(Binder.allowBlocking(service));
                             onBluetoothGattServiceUp();
                             break;
                         } // else must be SERVICE_IBLUETOOTH
@@ -1536,31 +1612,33 @@
                         if (!isNameAndAddressSet()) {
                             Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
                             mHandler.sendMessage(getMsg);
-                            if (mGetNameAddressOnly) return;
+                            if (mGetNameAddressOnly) {
+                                return;
+                            }
                         }
 
                         //Register callback object
                         try {
                             mBluetooth.registerCallback(mBluetoothCallback);
                         } catch (RemoteException re) {
-                            Slog.e(TAG, "Unable to register BluetoothCallback",re);
+                            Slog.e(TAG, "Unable to register BluetoothCallback", re);
                         }
                         //Inform BluetoothAdapter instances that service is up
                         sendBluetoothServiceUpCallback();
 
                         //Do enable request
                         try {
-                            if (mQuietEnable == false) {
+                            if (mQuietEnable) {
                                 if (!mBluetooth.enable()) {
-                                    Slog.e(TAG,"IBluetooth.enable() returned false");
+                                    Slog.e(TAG, "IBluetooth.enable() returned false");
                                 }
                             } else {
                                 if (!mBluetooth.enableNoAutoConnect()) {
-                                    Slog.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
+                                    Slog.e(TAG, "IBluetooth.enableNoAutoConnect() returned false");
                                 }
                             }
                         } catch (RemoteException e) {
-                            Slog.e(TAG,"Unable to call enable()",e);
+                            Slog.e(TAG, "Unable to call enable()", e);
                         }
                     } finally {
                         mBluetoothLock.writeLock().unlock();
@@ -1573,43 +1651,42 @@
                     }
                     break;
                 }
-                case MESSAGE_BLUETOOTH_STATE_CHANGE:
-                {
+                case MESSAGE_BLUETOOTH_STATE_CHANGE: {
                     int prevState = msg.arg1;
                     int newState = msg.arg2;
                     if (DBG) {
-                      Slog.d(TAG, "MESSAGE_BLUETOOTH_STATE_CHANGE: " + BluetoothAdapter.nameForState(prevState) + " > " +
-                        BluetoothAdapter.nameForState(newState));
+                        Slog.d(TAG,
+                                "MESSAGE_BLUETOOTH_STATE_CHANGE: " + BluetoothAdapter.nameForState(
+                                        prevState) + " > " + BluetoothAdapter.nameForState(
+                                        newState));
                     }
                     mState = newState;
                     bluetoothStateChangeHandler(prevState, newState);
                     // handle error state transition case from TURNING_ON to OFF
                     // unbind and rebind bluetooth service and enable bluetooth
-                    if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) &&
-                            (newState == BluetoothAdapter.STATE_OFF) &&
-                            (mBluetooth != null) && mEnable) {
+                    if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) && (newState
+                            == BluetoothAdapter.STATE_OFF) && (mBluetooth != null) && mEnable) {
                         recoverBluetoothServiceFromError(false);
                     }
-                    if ((prevState == BluetoothAdapter.STATE_TURNING_ON) &&
-                            (newState == BluetoothAdapter.STATE_BLE_ON) &&
-                            (mBluetooth != null) && mEnable) {
+                    if ((prevState == BluetoothAdapter.STATE_TURNING_ON) && (newState
+                            == BluetoothAdapter.STATE_BLE_ON) && (mBluetooth != null) && mEnable) {
                         recoverBluetoothServiceFromError(true);
                     }
                     // If we tried to enable BT while BT was in the process of shutting down,
                     // wait for the BT process to fully tear down and then force a restart
                     // here.  This is a bit of a hack (b/29363429).
-                    if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_OFF) &&
-                            (newState == BluetoothAdapter.STATE_OFF)) {
+                    if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_OFF) && (newState
+                            == BluetoothAdapter.STATE_OFF)) {
                         if (mEnable) {
                             Slog.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting.");
                             waitForOnOff(false, true);
-                            Message restartMsg = mHandler.obtainMessage(
-                                    MESSAGE_RESTART_BLUETOOTH_SERVICE);
+                            Message restartMsg =
+                                    mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
                             mHandler.sendMessageDelayed(restartMsg, 2 * SERVICE_RESTART_TIME_MS);
                         }
                     }
-                    if (newState == BluetoothAdapter.STATE_ON ||
-                            newState == BluetoothAdapter.STATE_BLE_ON) {
+                    if (newState == BluetoothAdapter.STATE_ON
+                            || newState == BluetoothAdapter.STATE_BLE_ON) {
                         // bluetooth is working, reset the counter
                         if (mErrorRecoveryRetryCounter != 0) {
                             Slog.w(TAG, "bluetooth is recovered from error");
@@ -1618,14 +1695,15 @@
                     }
                     break;
                 }
-                case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
-                {
+                case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: {
                     Slog.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED(" + msg.arg1 + ")");
                     try {
                         mBluetoothLock.writeLock().lock();
                         if (msg.arg1 == SERVICE_IBLUETOOTH) {
                             // if service is unbinded already, do nothing and return
-                            if (mBluetooth == null) break;
+                            if (mBluetooth == null) {
+                                break;
+                            }
                             mBluetooth = null;
                         } else if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
                             mBluetoothGatt = null;
@@ -1644,33 +1722,31 @@
                     if (mEnable) {
                         mEnable = false;
                         // Send a Bluetooth Restart message
-                        Message restartMsg = mHandler.obtainMessage(
-                            MESSAGE_RESTART_BLUETOOTH_SERVICE);
-                        mHandler.sendMessageDelayed(restartMsg,
-                            SERVICE_RESTART_TIME_MS);
+                        Message restartMsg =
+                                mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
+                        mHandler.sendMessageDelayed(restartMsg, SERVICE_RESTART_TIME_MS);
                     }
 
                     sendBluetoothServiceDownCallback();
 
                     // Send BT state broadcast to update
                     // the BT icon correctly
-                    if ((mState == BluetoothAdapter.STATE_TURNING_ON) ||
-                            (mState == BluetoothAdapter.STATE_ON)) {
+                    if ((mState == BluetoothAdapter.STATE_TURNING_ON) || (mState
+                            == BluetoothAdapter.STATE_ON)) {
                         bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
-                                                    BluetoothAdapter.STATE_TURNING_OFF);
+                                BluetoothAdapter.STATE_TURNING_OFF);
                         mState = BluetoothAdapter.STATE_TURNING_OFF;
                     }
                     if (mState == BluetoothAdapter.STATE_TURNING_OFF) {
                         bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
-                                                    BluetoothAdapter.STATE_OFF);
+                                BluetoothAdapter.STATE_OFF);
                     }
 
                     mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
                     mState = BluetoothAdapter.STATE_OFF;
                     break;
                 }
-                case MESSAGE_RESTART_BLUETOOTH_SERVICE:
-                {
+                case MESSAGE_RESTART_BLUETOOTH_SERVICE: {
                     Slog.d(TAG, "MESSAGE_RESTART_BLUETOOTH_SERVICE");
                     /* Enable without persisting the setting as
                      it doesnt change when IBluetooth
@@ -1687,8 +1763,7 @@
                     mBluetoothLock.writeLock().unlock();
                     break;
                 }
-                case MESSAGE_TIMEOUT_UNBIND:
-                {
+                case MESSAGE_TIMEOUT_UNBIND: {
                     Slog.e(TAG, "MESSAGE_TIMEOUT_UNBIND");
                     mBluetoothLock.writeLock().lock();
                     mUnbinding = false;
@@ -1697,7 +1772,9 @@
                 }
 
                 case MESSAGE_USER_SWITCHED: {
-                    if (DBG) Slog.d(TAG, "MESSAGE_USER_SWITCHED");
+                    if (DBG) {
+                        Slog.d(TAG, "MESSAGE_USER_SWITCHED");
+                    }
                     mHandler.removeMessages(MESSAGE_USER_SWITCHED);
 
                     /* disable and enable BT when detect a user switch */
@@ -1735,12 +1812,12 @@
                         handleDisable();
                         // Pbap service need receive STATE_TURNING_OFF intent to close
                         bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
-                                                    BluetoothAdapter.STATE_TURNING_OFF);
+                                BluetoothAdapter.STATE_TURNING_OFF);
 
                         boolean didDisableTimeout = !waitForOnOff(false, true);
 
                         bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
-                                                    BluetoothAdapter.STATE_OFF);
+                                BluetoothAdapter.STATE_OFF);
                         sendBluetoothServiceDownCallback();
 
                         try {
@@ -1785,14 +1862,18 @@
                     break;
                 }
                 case MESSAGE_USER_UNLOCKED: {
-                    if (DBG) Slog.d(TAG, "MESSAGE_USER_UNLOCKED");
+                    if (DBG) {
+                        Slog.d(TAG, "MESSAGE_USER_UNLOCKED");
+                    }
                     mHandler.removeMessages(MESSAGE_USER_SWITCHED);
 
                     if (mEnable && !mBinding && (mBluetooth == null)) {
                         // We should be connected, but we gave up for some
                         // reason; maybe the Bluetooth service wasn't encryption
                         // aware, so try binding again.
-                        if (DBG) Slog.d(TAG, "Enabled but not bound; retrying after unlock");
+                        if (DBG) {
+                            Slog.d(TAG, "Enabled but not bound; retrying after unlock");
+                        }
                         handleEnable(mQuietEnable);
                     }
                 }
@@ -1807,10 +1888,10 @@
             mBluetoothLock.writeLock().lock();
             if ((mBluetooth == null) && (!mBinding)) {
                 //Start bind timeout and bind
-                Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
-                mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
+                Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
+                mHandler.sendMessageDelayed(timeoutMsg, TIMEOUT_BIND_MS);
                 Intent i = new Intent(IBluetooth.class.getName());
-                if (!doBind(i, mConnection,Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
+                if (!doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
                         UserHandle.CURRENT)) {
                     mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
                 } else {
@@ -1820,17 +1901,16 @@
                 //Enable bluetooth
                 try {
                     if (!mQuietEnable) {
-                        if(!mBluetooth.enable()) {
-                            Slog.e(TAG,"IBluetooth.enable() returned false");
+                        if (!mBluetooth.enable()) {
+                            Slog.e(TAG, "IBluetooth.enable() returned false");
                         }
-                    }
-                    else {
-                        if(!mBluetooth.enableNoAutoConnect()) {
-                            Slog.e(TAG,"IBluetooth.enableNoAutoConnect() returned false");
+                    } else {
+                        if (!mBluetooth.enableNoAutoConnect()) {
+                            Slog.e(TAG, "IBluetooth.enableNoAutoConnect() returned false");
                         }
                     }
                 } catch (RemoteException e) {
-                    Slog.e(TAG,"Unable to call enable()",e);
+                    Slog.e(TAG, "Unable to call enable()", e);
                 }
             }
         } finally {
@@ -1852,13 +1932,15 @@
         try {
             mBluetoothLock.readLock().lock();
             if (mBluetooth != null) {
-                if (DBG) Slog.d(TAG,"Sending off request.");
+                if (DBG) {
+                    Slog.d(TAG, "Sending off request.");
+                }
                 if (!mBluetooth.disable()) {
-                    Slog.e(TAG,"IBluetooth.disable() returned false");
+                    Slog.e(TAG, "IBluetooth.disable() returned false");
                 }
             }
         } catch (RemoteException e) {
-            Slog.e(TAG,"Unable to call disable()",e);
+            Slog.e(TAG, "Unable to call disable()", e);
         } finally {
             mBluetoothLock.readLock().unlock();
         }
@@ -1876,15 +1958,12 @@
         boolean valid = false;
         try {
             foregroundUser = ActivityManager.getCurrentUser();
-            valid = (callingUser == foregroundUser) ||
-                    parentUser == foregroundUser    ||
-                    callingAppId == Process.NFC_UID ||
-                    callingAppId == mSystemUiUid;
+            valid = (callingUser == foregroundUser) || parentUser == foregroundUser
+                    || callingAppId == Process.NFC_UID || callingAppId == mSystemUiUid;
             if (DBG && !valid) {
-                Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid
-                    + " callingUser=" + callingUser
-                    + " parentUser=" + parentUser
-                    + " foregroundUser=" + foregroundUser);
+                Slog.d(TAG, "checkIfCallerIsForegroundUser: valid=" + valid + " callingUser="
+                        + callingUser + " parentUser=" + parentUser + " foregroundUser="
+                        + foregroundUser);
             }
         } finally {
             Binder.restoreCallingIdentity(callingIdentity);
@@ -1893,8 +1972,11 @@
     }
 
     private void sendBleStateChanged(int prevState, int newState) {
-        if (DBG) Slog.d(TAG,"Sending BLE State Change: " + BluetoothAdapter.nameForState(prevState) +
-            " > " + BluetoothAdapter.nameForState(newState));
+        if (DBG) {
+            Slog.d(TAG,
+                    "Sending BLE State Change: " + BluetoothAdapter.nameForState(prevState) + " > "
+                            + BluetoothAdapter.nameForState(newState));
+        }
         // Send broadcast message to everyone else
         Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
         intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
@@ -1909,14 +1991,15 @@
             return;
         }
         // Notify all proxy objects first of adapter state change
-        if (newState == BluetoothAdapter.STATE_BLE_ON ||
-                newState == BluetoothAdapter.STATE_OFF) {
+        if (newState == BluetoothAdapter.STATE_BLE_ON || newState == BluetoothAdapter.STATE_OFF) {
             boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF
-               && newState == BluetoothAdapter.STATE_BLE_ON);
+                    && newState == BluetoothAdapter.STATE_BLE_ON);
 
             if (newState == BluetoothAdapter.STATE_OFF) {
                 // If Bluetooth is off, send service down event to proxy objects, and unbind
-                if (DBG) Slog.d(TAG, "Bluetooth is complete send Service Down");
+                if (DBG) {
+                    Slog.d(TAG, "Bluetooth is complete send Service Down");
+                }
                 sendBluetoothServiceDownCallback();
                 unbindAndFinish();
                 sendBleStateChanged(prevState, newState);
@@ -1925,16 +2008,23 @@
 
             } else if (!intermediate_off) {
                 // connect to GattService
-                if (DBG) Slog.d(TAG, "Bluetooth is in LE only mode");
+                if (DBG) {
+                    Slog.d(TAG, "Bluetooth is in LE only mode");
+                }
                 if (mBluetoothGatt != null) {
-                    if (DBG) Slog.d(TAG, "Calling BluetoothGattServiceUp");
+                    if (DBG) {
+                        Slog.d(TAG, "Calling BluetoothGattServiceUp");
+                    }
                     onBluetoothGattServiceUp();
                 } else {
-                    if (DBG) Slog.d(TAG, "Binding Bluetooth GATT service");
-                    if (mContext.getPackageManager().hasSystemFeature(
-                                                    PackageManager.FEATURE_BLUETOOTH_LE)) {
+                    if (DBG) {
+                        Slog.d(TAG, "Binding Bluetooth GATT service");
+                    }
+                    if (mContext.getPackageManager()
+                            .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
                         Intent i = new Intent(IBluetoothGatt.class.getName());
-                        doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT);
+                        doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
+                                UserHandle.CURRENT);
                     }
                 }
                 sendBleStateChanged(prevState, newState);
@@ -1942,7 +2032,9 @@
                 isStandardBroadcast = false;
 
             } else if (intermediate_off) {
-                if (DBG) Slog.d(TAG, "Intermediate off, back to LE only mode");
+                if (DBG) {
+                    Slog.d(TAG, "Intermediate off, back to LE only mode");
+                }
                 // For LE only mode, broadcast as is
                 sendBleStateChanged(prevState, newState);
                 sendBluetoothStateCallback(false); // BT is OFF for general users
@@ -1955,13 +2047,13 @@
             sendBluetoothStateCallback(isUp);
             sendBleStateChanged(prevState, newState);
 
-        } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON ||
-                newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) {
+        } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON
+                || newState == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
             sendBleStateChanged(prevState, newState);
             isStandardBroadcast = false;
 
-        } else if (newState == BluetoothAdapter.STATE_TURNING_ON ||
-                newState == BluetoothAdapter.STATE_TURNING_OFF) {
+        } else if (newState == BluetoothAdapter.STATE_TURNING_ON
+                || newState == BluetoothAdapter.STATE_TURNING_OFF) {
             sendBleStateChanged(prevState, newState);
         }
 
@@ -1988,13 +2080,21 @@
         while (i < 10) {
             try {
                 mBluetoothLock.readLock().lock();
-                if (mBluetooth == null) break;
+                if (mBluetooth == null) {
+                    break;
+                }
                 if (on) {
-                    if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true;
+                    if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) {
+                        return true;
+                    }
                 } else if (off) {
-                    if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true;
+                    if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) {
+                        return true;
+                    }
                 } else {
-                    if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true;
+                    if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) {
+                        return true;
+                    }
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "getState()", e);
@@ -2009,7 +2109,7 @@
             }
             i++;
         }
-        Slog.e(TAG,"waitForOnOff time out");
+        Slog.e(TAG, "waitForOnOff time out");
         return false;
     }
 
@@ -2019,8 +2119,7 @@
     }
 
     private void sendEnableMsg(boolean quietMode, String packageName) {
-        mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE,
-                             quietMode ? 1 : 0, 0));
+        mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0));
         addActiveLog(packageName, true);
         mLastEnabledTime = SystemClock.elapsedRealtime();
     }
@@ -2035,15 +2134,17 @@
     }
 
     private void addCrashLog() {
-      synchronized (mCrashTimestamps) {
-        if (mCrashTimestamps.size() == CRASH_LOG_MAX_SIZE) mCrashTimestamps.removeFirst();
-        mCrashTimestamps.add(System.currentTimeMillis());
-        mCrashes++;
-      }
+        synchronized (mCrashTimestamps) {
+            if (mCrashTimestamps.size() == CRASH_LOG_MAX_SIZE) {
+                mCrashTimestamps.removeFirst();
+            }
+            mCrashTimestamps.add(System.currentTimeMillis());
+            mCrashes++;
+        }
     }
 
     private void recoverBluetoothServiceFromError(boolean clearBle) {
-        Slog.e(TAG,"recoverBluetoothServiceFromError");
+        Slog.e(TAG, "recoverBluetoothServiceFromError");
         try {
             mBluetoothLock.readLock().lock();
             if (mBluetooth != null) {
@@ -2082,15 +2183,14 @@
         mState = BluetoothAdapter.STATE_OFF;
 
         if (clearBle) {
-          clearBleApps();
+            clearBleApps();
         }
 
         mEnable = false;
 
         if (mErrorRecoveryRetryCounter++ < MAX_ERROR_RESTART_RETRIES) {
             // Send a Bluetooth Restart message to reenable bluetooth
-            Message restartMsg = mHandler.obtainMessage(
-                             MESSAGE_RESTART_BLUETOOTH_SERVICE);
+            Message restartMsg = mHandler.obtainMessage(MESSAGE_RESTART_BLUETOOTH_SERVICE);
             mHandler.sendMessageDelayed(restartMsg, ERROR_RESTART_TIME_MS);
         } else {
             // todo: notify user to power down and power up phone to make bluetooth work.
@@ -2118,9 +2218,9 @@
     private void updateOppLauncherComponentState(int userId, boolean bluetoothSharingDisallowed) {
         final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth",
                 "com.android.bluetooth.opp.BluetoothOppLauncherActivity");
-        final int newState = bluetoothSharingDisallowed
-                ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
-                : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+        final int newState =
+                bluetoothSharingDisallowed ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+                        : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
         try {
             final IPackageManager imp = AppGlobals.getPackageManager();
             imp.setComponentEnabledSetting(oppLauncherComponent, newState,
@@ -2132,7 +2232,9 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) {
+            return;
+        }
         String errorMsg = null;
 
         boolean protoOut = (args.length > 0) && args[0].startsWith("--proto");
@@ -2145,11 +2247,10 @@
             writer.println("  name: " + mName);
             if (mEnable) {
                 long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime;
-                String onDurationString = String.format("%02d:%02d:%02d.%03d",
-                                          (int)(onDuration / (1000 * 60 * 60)),
-                                          (int)((onDuration / (1000 * 60)) % 60),
-                                          (int)((onDuration / 1000) % 60),
-                                          (int)(onDuration % 1000));
+                String onDurationString = String.format(Locale.US, "%02d:%02d:%02d.%03d",
+                        (int) (onDuration / (1000 * 60 * 60)),
+                        (int) ((onDuration / (1000 * 60)) % 60), (int) ((onDuration / 1000) % 60),
+                        (int) (onDuration % 1000));
                 writer.println("  time since enabled: " + onDurationString);
             }
 
@@ -2162,14 +2263,17 @@
                 }
             }
 
-            writer.println("\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s"));
-            if (mCrashes == CRASH_LOG_MAX_SIZE) writer.println("(last " + CRASH_LOG_MAX_SIZE + ")");
+            writer.println(
+                    "\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s"));
+            if (mCrashes == CRASH_LOG_MAX_SIZE) {
+                writer.println("(last " + CRASH_LOG_MAX_SIZE + ")");
+            }
             for (Long time : mCrashTimestamps) {
-              writer.println("  " + timeToLog(time.longValue()));
+                writer.println("  " + timeToLog(time));
             }
 
-            writer.println("\n" + mBleApps.size() + " BLE app" +
-                            (mBleApps.size() == 1 ? "" : "s") + "registered");
+            writer.println("\n" + mBleApps.size() + " BLE app" + (mBleApps.size() == 1 ? "" : "s")
+                    + "registered");
             for (ClientDeathRecipient app : mBleApps.values()) {
                 writer.println("  " + app.getPackageName());
             }
@@ -2194,7 +2298,9 @@
         }
         if (errorMsg != null) {
             // Silently return if we are extracting metrics in Protobuf format
-            if (protoOut) return;
+            if (protoOut) {
+                return;
+            }
             writer.println(errorMsg);
         }
     }
diff --git a/services/core/java/com/android/server/DisplayThread.java b/services/core/java/com/android/server/DisplayThread.java
index cad2a61..85c799c 100644
--- a/services/core/java/com/android/server/DisplayThread.java
+++ b/services/core/java/com/android/server/DisplayThread.java
@@ -40,7 +40,7 @@
         if (sInstance == null) {
             sInstance = new DisplayThread();
             sInstance.start();
-            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
             sHandler = new Handler(sInstance.getLooper());
         }
     }
diff --git a/services/core/java/com/android/server/FgThread.java b/services/core/java/com/android/server/FgThread.java
index 18fb477..021bfaa 100644
--- a/services/core/java/com/android/server/FgThread.java
+++ b/services/core/java/com/android/server/FgThread.java
@@ -39,7 +39,7 @@
         if (sInstance == null) {
             sInstance = new FgThread();
             sInstance.start();
-            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
             sHandler = new Handler(sInstance.getLooper());
         }
     }
diff --git a/services/core/java/com/android/server/IoThread.java b/services/core/java/com/android/server/IoThread.java
index ad4c194..bfe825a 100644
--- a/services/core/java/com/android/server/IoThread.java
+++ b/services/core/java/com/android/server/IoThread.java
@@ -36,7 +36,7 @@
         if (sInstance == null) {
             sInstance = new IoThread();
             sInstance.start();
-            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
             sHandler = new Handler(sInstance.getLooper());
         }
     }
diff --git a/services/core/java/com/android/server/UiThread.java b/services/core/java/com/android/server/UiThread.java
index fd88d26..f813074 100644
--- a/services/core/java/com/android/server/UiThread.java
+++ b/services/core/java/com/android/server/UiThread.java
@@ -47,7 +47,7 @@
             sInstance = new UiThread();
             sInstance.start();
             final Looper looper = sInstance.getLooper();
-            looper.setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+            looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
             looper.setSlowDispatchThresholdMs(SLOW_DISPATCH_THRESHOLD_MS);
             sHandler = new Handler(sInstance.getLooper());
         }
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index b11b16e1..2d2424f 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -65,6 +65,12 @@
     static final int POSITION_TOP = Integer.MAX_VALUE;
     static final int POSITION_BOTTOM = Integer.MIN_VALUE;
 
+
+    /**
+     * Counter for next free stack ID to use for dynamic activity stacks. Unique across displays.
+     */
+    private static int sNextFreeStackId = 0;
+
     private ActivityStackSupervisor mSupervisor;
     /** Actual Display this object tracks. */
     int mDisplayId;
@@ -231,6 +237,10 @@
         return getOrCreateStack(windowingMode, activityType, onTop);
     }
 
+    private int getNextStackId() {
+        return sNextFreeStackId++;
+    }
+
     /**
      * Creates a stack matching the input windowing mode and activity type on this display.
      * @param windowingMode The windowing mode the stack should be created in. If
@@ -278,7 +288,7 @@
             }
         }
 
-        final int stackId = mSupervisor.getNextStackId();
+        final int stackId = getNextStackId();
         return createStackUnchecked(windowingMode, activityType, stackId, onTop);
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 763478e..2ca2c27 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -250,6 +250,7 @@
 import android.app.backup.IBackupManager;
 import android.app.servertransaction.ConfigurationChangeItem;
 import android.app.usage.UsageEvents;
+import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManagerInternal;
 import android.appwidget.AppWidgetManager;
 import android.content.ActivityNotFoundException;
@@ -4522,9 +4523,18 @@
         userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, ALLOW_FULL_ONLY, "startActivity", null);
         // TODO: Switch to user app stacks here.
-        return mActivityStartController.startActivityMayWait(caller, -1, callingPackage,
-                intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
-                profilerInfo, null, null, bOptions, false, userId, null, "startActivityAsUser");
+        return mActivityStartController.obtainStarter(intent, "startActivityAsUser")
+                .setCaller(caller)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setResultTo(resultTo)
+                .setResultWho(resultWho)
+                .setRequestCode(requestCode)
+                .setStartFlags(startFlags)
+                .setProfilerInfo(profilerInfo)
+                .setMayWait(bOptions, userId)
+                .execute();
+
     }
 
     @Override
@@ -4585,11 +4595,17 @@
 
         // TODO: Switch to user app stacks here.
         try {
-            int ret = mActivityStartController.startActivityMayWait(null, targetUid,
-                    targetPackage, intent, resolvedType, null, null, resultTo, resultWho,
-                    requestCode, startFlags, null, null, null, bOptions, ignoreTargetSecurity,
-                    userId, null, "startActivityAsCaller");
-            return ret;
+            return mActivityStartController.obtainStarter(intent, "startActivityAsCaller")
+                    .setCallingUid(targetUid)
+                    .setCallingPackage(targetPackage)
+                    .setResolvedType(resolvedType)
+                    .setResultTo(resultTo)
+                    .setResultWho(resultWho)
+                    .setRequestCode(requestCode)
+                    .setStartFlags(startFlags)
+                    .setMayWait(bOptions, userId)
+                    .setIgnoreTargetSecurity(ignoreTargetSecurity)
+                    .execute();
         } catch (SecurityException e) {
             // XXX need to figure out how to propagate to original app.
             // A SecurityException here is generally actually a fault of the original
@@ -4615,9 +4631,18 @@
                 userId, false, ALLOW_FULL_ONLY, "startActivityAndWait", null);
         WaitResult res = new WaitResult();
         // TODO: Switch to user app stacks here.
-        mActivityStartController.startActivityMayWait(caller, -1, callingPackage, intent,
-                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
-                profilerInfo, res, null, bOptions, false, userId, null, "startActivityAndWait");
+        mActivityStartController.obtainStarter(intent, "startActivityAndWait")
+                .setCaller(caller)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setResultTo(resultTo)
+                .setResultWho(resultWho)
+                .setRequestCode(requestCode)
+                .setStartFlags(startFlags)
+                .setMayWait(bOptions, userId)
+                .setProfilerInfo(profilerInfo)
+                .setWaitResult(res)
+                .execute();
         return res;
     }
 
@@ -4629,10 +4654,17 @@
         userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, ALLOW_FULL_ONLY, "startActivityWithConfig", null);
         // TODO: Switch to user app stacks here.
-        int ret = mActivityStartController.startActivityMayWait(caller, -1, callingPackage, intent,
-                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags, null, null,
-                config, bOptions, false, userId, null, "startActivityWithConfig");
-        return ret;
+        return mActivityStartController.obtainStarter(intent, "startActivityWithConfig")
+                .setCaller(caller)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setResultTo(resultTo)
+                .setResultWho(resultWho)
+                .setRequestCode(requestCode)
+                .setStartFlags(startFlags)
+                .setGlobalConfiguration(config)
+                .setMayWait(bOptions, userId)
+                .execute();
     }
 
     @Override
@@ -4678,9 +4710,16 @@
         userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
                 ALLOW_FULL_ONLY, "startVoiceActivity", null);
         // TODO: Switch to user app stacks here.
-        return mActivityStartController.startActivityMayWait(null, callingUid, callingPackage,
-                intent, resolvedType, session, interactor, null, null, 0, startFlags, profilerInfo,
-                null, null, bOptions, false, userId, null, "startVoiceActivity");
+        return mActivityStartController.obtainStarter(intent, "startVoiceActivity")
+                .setCallingUid(callingUid)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setVoiceSession(session)
+                .setVoiceInteractor(interactor)
+                .setStartFlags(startFlags)
+                .setProfilerInfo(profilerInfo)
+                .setMayWait(bOptions, userId)
+                .execute();
     }
 
     @Override
@@ -4689,9 +4728,13 @@
         enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
         userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
                 ALLOW_FULL_ONLY, "startAssistantActivity", null);
-        return mActivityStartController.startActivityMayWait(null, callingUid, callingPackage,
-                intent, resolvedType, null, null, null, null, 0, 0, null, null, null, bOptions,
-                false, userId, null, "startAssistantActivity");
+
+        return mActivityStartController.obtainStarter(intent, "startAssistantActivity")
+                .setCallingUid(callingUid)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setMayWait(bOptions, userId)
+                .execute();
     }
 
     @Override
@@ -4731,9 +4774,12 @@
                 intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
                 intent.setComponent(recentsComponent);
                 intent.putExtras(options);
-                return mActivityStartController.startActivityMayWait(null, recentsUid,
-                        recentsPackage, intent, null, null, null, null, null, 0, 0, null, null,
-                        null, activityOptions, false, userId, null, "startRecentsActivity");
+
+                return mActivityStartController.obtainStarter(intent, "startRecentsActivity")
+                        .setCallingUid(recentsUid)
+                        .setCallingPackage(recentsPackage)
+                        .setMayWait(activityOptions, userId)
+                        .execute();
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -4906,11 +4952,22 @@
             }
 
             final long origId = Binder.clearCallingIdentity();
-            int res = mActivityStartController.startActivity(r.app.thread, intent,
-                    null /*ephemeralIntent*/, r.resolvedType, aInfo, null /*rInfo*/, null,
-                    null, resultTo != null ? resultTo.appToken : null, resultWho, requestCode, -1,
-                    r.launchedFromUid, r.launchedFromPackage, -1, r.launchedFromUid, 0, options,
-                    false, false, null, null, "startNextMatchingActivity");
+            // TODO(b/64750076): Check if calling pid should really be -1.
+            final int res = mActivityStartController
+                    .obtainStarter(intent, "startNextMatchingActivity")
+                    .setCaller(r.app.thread)
+                    .setResolvedType(r.resolvedType)
+                    .setActivityInfo(aInfo)
+                    .setResultTo(resultTo != null ? resultTo.appToken : null)
+                    .setResultWho(resultWho)
+                    .setRequestCode(requestCode)
+                    .setCallingPid(-1)
+                    .setCallingUid(r.launchedFromUid)
+                    .setCallingPackage(r.launchedFromPackage)
+                    .setRealCallingPid(-1)
+                    .setRealCallingUid(r.launchedFromUid)
+                    .setActivityOptions(options)
+                    .execute();
             Binder.restoreCallingIdentity(origId);
 
             r.finishing = wasFinishing;
@@ -9964,7 +10021,7 @@
                     }
                 }
 
-                TaskRecord task = new TaskRecord(this,
+                TaskRecord task = TaskRecord.create(this,
                         mStackSupervisor.getNextTaskIdForUserLocked(r.userId),
                         ainfo, intent, description);
                 if (!mRecentTasks.addToBottom(task)) {
@@ -20168,6 +20225,11 @@
             // Instrumentation can kill and relaunch even persistent processes
             forceStopPackageLocked(ii.targetPackage, -1, true, false, true, true, false, userId,
                     "start instr");
+            // Inform usage stats to make the target package active
+            if (mUsageStatsService != null) {
+                mUsageStatsService.reportEvent(ii.targetPackage, userId,
+                        UsageEvents.Event.SYSTEM_INTERACTION);
+            }
             ProcessRecord app = addAppLocked(ai, defProcess, false, abiOverride);
             app.instr = activeInstr;
             activeInstr.mFinished = false;
@@ -23824,7 +23886,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..edf9813 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;
@@ -5015,8 +5022,8 @@
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             boolean toTop, ActivityRecord activity, ActivityRecord source,
             ActivityOptions options) {
-        final TaskRecord task = new TaskRecord(mService, taskId, info, intent, voiceSession,
-                voiceInteractor);
+        final TaskRecord task = TaskRecord.create(
+                mService, taskId, info, intent, voiceSession, voiceInteractor);
         // add the task to stack first, mTaskPositioner might need the stack association
         addTask(task, toTop, "createTaskRecord");
         final boolean isLockscreenShown = mService.mStackSupervisor.getKeyguardController()
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 48c08a5..cd3b21c 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -298,9 +298,6 @@
 
     private LaunchingBoundsController mLaunchingBoundsController;
 
-    /** Counter for next free stack ID to use for dynamic activity stacks. */
-    private int mNextFreeStackId = 0;
-
     /**
      * Maps the task identifier that activities are currently being started in to the userId of the
      * task. Each time a new task is created, the entry for the userId of the task is incremented
@@ -2904,16 +2901,6 @@
         }
     }
 
-    int getNextStackId() {
-        while (true) {
-            if (getStack(mNextFreeStackId) == null) {
-                break;
-            }
-            mNextFreeStackId++;
-        }
-        return mNextFreeStackId;
-    }
-
     /**
      * Called to restore the state of the task into the stack that it's supposed to go into.
      *
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index 317a68f..a97b93c 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -25,8 +25,6 @@
 
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
-import android.app.ProfilerInfo;
-import android.app.WaitResult;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Intent;
@@ -34,7 +32,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.res.Configuration;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.FactoryTest;
@@ -43,12 +40,10 @@
 import android.os.Looper;
 import android.os.Message;
 import android.provider.Settings;
-import android.service.voice.IVoiceInteractionSession;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
-import com.android.internal.app.IVoiceInteractor;
 import com.android.server.am.ActivityStarter.DefaultFactory;
 import com.android.server.am.ActivityStarter.Factory;
 
@@ -72,7 +67,6 @@
 
     private final ActivityManagerService mService;
     private final ActivityStackSupervisor mSupervisor;
-    private final ActivityStartInterceptor mInterceptor;
 
     /** Last home activity record we attempted to start. */
     private ActivityRecord mLastHomeActivityStartRecord;
@@ -115,7 +109,9 @@
     private ActivityStarter mLastStarter;
 
     ActivityStartController(ActivityManagerService service) {
-        this(service, service.mStackSupervisor, new DefaultFactory());
+        this(service, service.mStackSupervisor,
+                new DefaultFactory(service, service.mStackSupervisor,
+                    new ActivityStartInterceptor(service, service.mStackSupervisor)));
     }
 
     @VisibleForTesting
@@ -124,38 +120,20 @@
         mService = service;
         mSupervisor = supervisor;
         mHandler = new StartHandler(mService.mHandlerThread.getLooper());
-        mInterceptor = new ActivityStartInterceptor(mService, mSupervisor);
         mFactory = factory;
+        mFactory.setController(this);
     }
 
     /**
-     * Retrieves a starter to be used for a new start request. The starter will be added to the
-     * active starters list.
-     *
-     * TODO(b/64750076): This should be removed when {@link #obtainStarter} is implemented. At that
-     * time, {@link ActivityStarter#execute} will be able to handle cleaning up the starter's
-     * internal references.
+     * @return A starter to configure and execute starting an activity. It is valid until after
+     *         {@link ActivityStarter#execute} is invoked. At that point, the starter should be
+     *         considered invalid and no longer modified or used.
      */
-    private ActivityStarter createStarter() {
-        mLastStarter = mFactory.getStarter(this, mService, mService.mStackSupervisor, mInterceptor);
-        return mLastStarter;
-    }
+    ActivityStarter obtainStarter(Intent intent, String reason) {
+        final ActivityStarter starter = mFactory.obtainStarter();
+        mLastStarter = starter;
 
-    /**
-     * TODO(b/64750076): Remove once we directly expose starter interface to callers through
-     * {@link #obtainStarter}.
-     */
-    int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
-            String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
-            String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
-            ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
-            ActivityRecord[] outActivity, TaskRecord inTask, String reason) {
-        return createStarter().startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
-                aInfo, rInfo, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
-                callingPid, callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
-                options, ignoreTargetSecurity, componentSpecified, outActivity, inTask, reason);
+        return starter.setIntent(intent).setReason(reason);
     }
 
     /**
@@ -170,18 +148,12 @@
     void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
         mSupervisor.moveHomeStackTaskToTop(reason);
 
-        final ActivityStarter starter = createStarter();
-
-        mLastHomeActivityStartResult = starter.startActivityLocked(null /*caller*/, intent,
-                null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
-                null /*voiceSession*/, null /*voiceInteractor*/, null /*resultTo*/,
-                null /*resultWho*/, 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/,
-                null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
-                0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/,
-                false /*componentSpecified*/, tmpOutRecord, null /*inTask*/,
-                "startHomeActivity: " + reason);
+        mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
+                .setOutActivity(tmpOutRecord)
+                .setCallingUid(0)
+                .setActivityInfo(aInfo)
+                .execute();
         mLastHomeActivityStartRecord = tmpOutRecord[0];
-
         if (mSupervisor.inResumeTopActivity) {
             // If we are in resume section already, home activity will be initialized, but not
             // resumed (to avoid recursive resume) and will stay that way until something pokes it
@@ -228,9 +200,10 @@
                     intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
                     intent.setComponent(new ComponentName(
                             ri.activityInfo.packageName, ri.activityInfo.name));
-                    startActivity(null, intent, null /*ephemeralIntent*/, null, ri.activityInfo,
-                            null /*rInfo*/, null, null, null, null, 0, 0, 0, null, 0, 0, 0, null,
-                            false, false, null, null, "startSetupActivity");
+                    obtainStarter(intent, "startSetupActivity")
+                            .setCallingUid(0)
+                            .setActivityInfo(ri.activityInfo)
+                            .execute();
                 }
             }
         }
@@ -246,9 +219,17 @@
                 null);
 
         // TODO: Switch to user app stacks here.
-        return startActivityMayWait(null, uid, callingPackage,
-                intent, resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
-                null, null, null, bOptions, false, userId, inTask, reason);
+        return obtainStarter(intent, reason)
+                .setCallingUid(uid)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setResultTo(resultTo)
+                .setResultWho(resultWho)
+                .setRequestCode(requestCode)
+                .setStartFlags(startFlags)
+                .setMayWait(bOptions, userId)
+                .setInTask(inTask)
+                .execute();
     }
 
     final int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
@@ -262,24 +243,6 @@
         return ret;
     }
 
-    /**
-     * TODO(b/64750076): Remove once we directly expose starter interface to callers through
-     * {@link #obtainStarter}.
-     */
-    int startActivityMayWait(IApplicationThread caller, int callingUid,
-            String callingPackage, Intent intent, String resolvedType,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            IBinder resultTo, String resultWho, int requestCode, int startFlags,
-            ProfilerInfo profilerInfo, WaitResult outResult,
-            Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId,
-            TaskRecord inTask, String reason) {
-        return createStarter().startActivityMayWait(caller, callingUid, callingPackage, intent,
-                resolvedType, voiceSession, voiceInteractor, resultTo, resultWho, requestCode,
-                startFlags, profilerInfo, outResult, globalConfig, bOptions,
-                ignoreTargetSecurity,
-                userId, inTask, reason);
-    }
-
     int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions, int userId,
             String reason) {
@@ -340,11 +303,23 @@
 
                     ActivityOptions options = ActivityOptions.fromBundle(
                             i == intents.length - 1 ? bOptions : null);
-                    int res = startActivity(caller, intent, null /*ephemeralIntent*/,
-                            resolvedTypes[i], aInfo, null /*rInfo*/, null, null, resultTo, null, -1,
-                            callingPid, callingUid, callingPackage,
-                            realCallingPid, realCallingUid, 0,
-                            options, false, componentSpecified, outActivity, null, reason);
+
+                    final int res = obtainStarter(intent, reason)
+                            .setCaller(caller)
+                            .setResolvedType(resolvedTypes[i])
+                            .setActivityInfo(aInfo)
+                            .setResultTo(resultTo)
+                            .setRequestCode(-1)
+                            .setCallingPid(callingPid)
+                            .setCallingUid(callingUid)
+                            .setCallingPackage(callingPackage)
+                            .setRealCallingPid(realCallingPid)
+                            .setRealCallingUid(realCallingUid)
+                            .setActivityOptions(options)
+                            .setComponentSpecified(componentSpecified)
+                            .setOutActivity(outActivity)
+                            .execute();
+
                     if (res < 0) {
                         return res;
                     }
@@ -369,10 +344,11 @@
         while (!mPendingActivityLaunches.isEmpty()) {
             final PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);
             final boolean resume = doResume && mPendingActivityLaunches.isEmpty();
-            final ActivityStarter starter = createStarter();
+            final ActivityStarter starter = obtainStarter(null /* intent */,
+                    "pendingActivityLaunch");
             try {
-                starter.startActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags, resume,
-                        null, null, null /*outRecords*/);
+                starter.startResolvedActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags,
+                        resume, null, null, null /* outRecords */);
             } catch (Exception e) {
                 Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
                 pal.sendErrorResult(e.getMessage());
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 3bee4228..dda8e9c 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -74,7 +74,6 @@
 import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
@@ -87,7 +86,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.AuxiliaryResolveInfo;
-import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
@@ -186,6 +184,13 @@
     // The reason we were trying to start the last activity
     private String mLastStartReason;
 
+    /*
+     * Request details provided through setter methods. Should be reset after {@link #execute()}
+     * to avoid unnecessarily retaining parameters. Note that the request is ignored when
+     * {@link #startResolvedActivity} is invoked directly.
+     */
+    private Request mRequest = new Request();
+
     /**
      * An interface that to provide {@link ActivityStarter} instances to the controller. This is
      * used by tests to inject their own starter implementations for verification purposes.
@@ -193,27 +198,96 @@
     @VisibleForTesting
     interface Factory {
         /**
+         * Sets the {@link ActivityStartController} to be passed to {@link ActivityStarter}.
+         */
+        void setController(ActivityStartController controller);
+
+        /**
          * Generates an {@link ActivityStarter} that is ready to handle a new start request.
          * @param controller The {@link ActivityStartController} which the starter who will own
          *                   this instance.
          * @return an {@link ActivityStarter}
          */
-        ActivityStarter getStarter(ActivityStartController controller,
-                ActivityManagerService service, ActivityStackSupervisor supervisor,
-                ActivityStartInterceptor interceptor);
+        ActivityStarter obtainStarter();
     }
 
     /**
      * Default implementation of {@link StarterFactory}.
      */
     static class DefaultFactory implements Factory {
-        @Override
-        public ActivityStarter getStarter(ActivityStartController controller,
-                ActivityManagerService service, ActivityStackSupervisor supervisor,
-                ActivityStartInterceptor interceptor) {
-            // TODO(b/64750076): Investigate recycling instances to reduce object creation overhead.
-            return new ActivityStarter(controller, service, supervisor, interceptor);
+        private ActivityStartController mController;
+        private ActivityManagerService mService;
+        private ActivityStackSupervisor mSupervisor;
+        private ActivityStartInterceptor mInterceptor;
+
+        DefaultFactory(ActivityManagerService service,
+                ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
+            mService = service;
+            mSupervisor = supervisor;
+            mInterceptor = interceptor;
         }
+
+        @Override
+        public void setController(ActivityStartController controller) {
+            mController = controller;
+        }
+
+        @Override
+        public ActivityStarter obtainStarter() {
+            // TODO(b/64750076): Investigate recycling instances to reduce object creation overhead.
+            return new ActivityStarter(mController, mService, mSupervisor, mInterceptor);
+        }
+    }
+
+    /**
+     * Container for capturing initial start request details. This information is NOT reset until
+     * the {@link ActivityStarter} is recycled, allowing for multiple invocations with the same
+     * parameters.
+     *
+     * TODO(b/64750076): Investigate consolidating member variables of {@link ActivityStarter} with
+     * the request object. Note that some member variables are referenced in
+     * {@link #dump(PrintWriter, String)} and therefore cannot be cleared immediately after
+     * execution.
+     */
+    private static class Request {
+        private static final int DEFAULT_CALLING_UID = -1;
+        private static final int DEFAULT_CALLING_PID = 0;
+
+        IApplicationThread caller;
+        Intent intent;
+        Intent ephemeralIntent;
+        String resolvedType;
+        ActivityInfo activityInfo;
+        ResolveInfo resolveInfo;
+        IVoiceInteractionSession voiceSession;
+        IVoiceInteractor voiceInteractor;
+        IBinder resultTo;
+        String resultWho;
+        int requestCode;
+        int callingPid = DEFAULT_CALLING_UID;
+        int callingUid = DEFAULT_CALLING_PID;
+        String callingPackage;
+        int realCallingPid;
+        int realCallingUid;
+        int startFlags;
+        ActivityOptions activityOptions;
+        boolean ignoreTargetSecurity;
+        boolean componentSpecified;
+        ActivityRecord[] outActivity;
+        TaskRecord inTask;
+        String reason;
+        ProfilerInfo profilerInfo;
+        Configuration globalConfig;
+        Bundle waitOptions;
+        int userId;
+        WaitResult waitResult;
+
+        /**
+         * Indicates that we should wait for the result of the start request. This flag is set when
+         * {@link ActivityStarter#setMayWait(Bundle, int)} is called.
+         * {@see ActivityStarter#startActivityMayWait}.
+         */
+        boolean mayWait;
     }
 
     ActivityStarter(ActivityStartController controller, ActivityManagerService service,
@@ -224,14 +298,44 @@
         mInterceptor = interceptor;
     }
 
-    boolean relatedToPackage(String packageName) {
-        return (mLastStartActivityRecord[0] != null
-                        && packageName.equals(mLastStartActivityRecord[0].packageName))
-                || (mStartActivity != null
-                        && packageName.equals(mStartActivity.packageName));
+    ActivityRecord getStartActivity() {
+        return mStartActivity;
     }
 
-    int startActivityLocked(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
+    boolean relatedToPackage(String packageName) {
+        return (mLastStartActivityRecord[0] != null
+                && packageName.equals(mLastStartActivityRecord[0].packageName))
+                || (mStartActivity != null && packageName.equals(mStartActivity.packageName));
+    }
+
+    /**
+     * Starts an activity based on the request parameters provided earlier.
+     * @return The starter result.
+     */
+    int execute() {
+        // TODO(b/64750076): Look into passing request directly to these methods to allow
+        // for transactional diffs and preprocessing.
+        if (mRequest.mayWait) {
+            return startActivityMayWait(mRequest.caller, mRequest.callingUid,
+                    mRequest.callingPackage, mRequest.intent, mRequest.resolvedType,
+                    mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
+                    mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,
+                    mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
+                    mRequest.waitOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
+                    mRequest.inTask, mRequest.reason);
+        } else {
+            return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,
+                    mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,
+                    mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
+                    mRequest.resultWho, mRequest.requestCode, mRequest.callingPid,
+                    mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid,
+                    mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions,
+                    mRequest.ignoreTargetSecurity, mRequest.componentSpecified,
+                    mRequest.outActivity, mRequest.inTask, mRequest.reason);
+        }
+    }
+
+    private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
             String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
@@ -265,7 +369,6 @@
         return result != START_ABORTED ? result : START_SUCCESS;
     }
 
-    /** DO NOT call this method directly. Use {@link #startActivityLocked} instead. */
     private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent,
             String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
@@ -548,8 +651,8 @@
 
         mController.doPendingActivityLaunches(false);
 
-        return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags, true,
-                options, inTask, outActivity);
+        return startResolvedActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
+                true /* doResume */, options, inTask, outActivity);
     }
 
     /**
@@ -569,7 +672,8 @@
                 auxiliaryResponse.failureIntent, callingPackage, verificationBundle,
                 resolvedType, userId, auxiliaryResponse.packageName, auxiliaryResponse.splitName,
                 auxiliaryResponse.installFailureActivity, auxiliaryResponse.versionCode,
-                auxiliaryResponse.token, auxiliaryResponse.needsPhaseTwo);
+                auxiliaryResponse.token, auxiliaryResponse.resolveInfo.getExtras(),
+                auxiliaryResponse.needsPhaseTwo);
     }
 
     void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack targetStack) {
@@ -610,7 +714,7 @@
         }
     }
 
-    final int startActivityMayWait(IApplicationThread caller, int callingUid,
+    private int startActivityMayWait(IApplicationThread caller, int callingUid,
             String callingPackage, Intent intent, String resolvedType,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             IBinder resultTo, String resultWho, int requestCode, int startFlags,
@@ -754,7 +858,7 @@
             }
 
             final ActivityRecord[] outRecord = new ActivityRecord[1];
-            int res = startActivityLocked(caller, intent, ephemeralIntent, resolvedType,
+            int res = startActivity(caller, intent, ephemeralIntent, resolvedType,
                     aInfo, rInfo, voiceSession, voiceInteractor,
                     resultTo, resultWho, requestCode, callingPid,
                     callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
@@ -820,10 +924,16 @@
         }
     }
 
-    int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
-            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
-            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
-            ActivityRecord[] outActivity) {
+    /**
+     * Starts an activity based on the provided {@link ActivityRecord} and environment parameters.
+     * Note that this method is called internally as well as part of {@link #startActivity}.
+     *
+     * @return The start result.
+     */
+    int startResolvedActivity(final ActivityRecord r, ActivityRecord sourceRecord,
+        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+        int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
+        ActivityRecord[] outActivity) {
         int result = START_CANCELED;
         try {
             mService.mWindowManager.deferSurfaceLayout();
@@ -2008,6 +2118,155 @@
                 (flags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0;
     }
 
+    ActivityStarter setIntent(Intent intent) {
+        mRequest.intent = intent;
+        return this;
+    }
+
+    ActivityStarter setReason(String reason) {
+        mRequest.reason = reason;
+        return this;
+    }
+
+    ActivityStarter setCaller(IApplicationThread caller) {
+        mRequest.caller = caller;
+        return this;
+    }
+
+    ActivityStarter setEphemeralIntent(Intent intent) {
+        mRequest.ephemeralIntent = intent;
+        return this;
+    }
+
+
+    ActivityStarter setResolvedType(String type) {
+        mRequest.resolvedType = type;
+        return this;
+    }
+
+    ActivityStarter setActivityInfo(ActivityInfo info) {
+        mRequest.activityInfo = info;
+        return this;
+    }
+
+    ActivityStarter setResolveInfo(ResolveInfo info) {
+        mRequest.resolveInfo = info;
+        return this;
+    }
+
+    ActivityStarter setVoiceSession(IVoiceInteractionSession voiceSession) {
+        mRequest.voiceSession = voiceSession;
+        return this;
+    }
+
+    ActivityStarter setVoiceInteractor(IVoiceInteractor voiceInteractor) {
+        mRequest.voiceInteractor = voiceInteractor;
+        return this;
+    }
+
+    ActivityStarter setResultTo(IBinder resultTo) {
+        mRequest.resultTo = resultTo;
+        return this;
+    }
+
+    ActivityStarter setResultWho(String resultWho) {
+        mRequest.resultWho = resultWho;
+        return this;
+    }
+
+    ActivityStarter setRequestCode(int requestCode) {
+        mRequest.requestCode = requestCode;
+        return this;
+    }
+
+    ActivityStarter setCallingPid(int pid) {
+        mRequest.callingPid = pid;
+        return this;
+    }
+
+    ActivityStarter setCallingUid(int uid) {
+        mRequest.callingUid = uid;
+        return this;
+    }
+
+    ActivityStarter setCallingPackage(String callingPackage) {
+        mRequest.callingPackage = callingPackage;
+        return this;
+    }
+
+    ActivityStarter setRealCallingPid(int pid) {
+        mRequest.realCallingPid = pid;
+        return this;
+    }
+
+    ActivityStarter setRealCallingUid(int uid) {
+        mRequest.realCallingUid = uid;
+        return this;
+    }
+
+    ActivityStarter setStartFlags(int startFlags) {
+        mRequest.startFlags = startFlags;
+        return this;
+    }
+
+    ActivityStarter setActivityOptions(ActivityOptions options) {
+        mRequest.activityOptions = options;
+        return this;
+    }
+
+    ActivityStarter setIgnoreTargetSecurity(boolean ignoreTargetSecurity) {
+        mRequest.ignoreTargetSecurity = ignoreTargetSecurity;
+        return this;
+    }
+
+    ActivityStarter setComponentSpecified(boolean componentSpecified) {
+        mRequest.componentSpecified = componentSpecified;
+        return this;
+    }
+
+    ActivityStarter setOutActivity(ActivityRecord[] outActivity) {
+        mRequest.outActivity = outActivity;
+        return this;
+    }
+
+    ActivityStarter setInTask(TaskRecord inTask) {
+        mRequest.inTask = inTask;
+        return this;
+    }
+
+    ActivityStarter setWaitResult(WaitResult result) {
+        mRequest.waitResult = result;
+        return this;
+    }
+
+    ActivityStarter setProfilerInfo(ProfilerInfo info) {
+        mRequest.profilerInfo = info;
+        return this;
+    }
+
+    ActivityStarter setGlobalConfiguration(Configuration config) {
+        mRequest.globalConfig = config;
+        return this;
+    }
+
+    ActivityStarter setWaitOptions(Bundle options) {
+        mRequest.waitOptions = options;
+        return this;
+    }
+
+    ActivityStarter setUserId(int userId) {
+        mRequest.userId = userId;
+        return this;
+    }
+
+    ActivityStarter setMayWait(Bundle options, int userId) {
+        mRequest.mayWait = true;
+        mRequest.waitOptions = options;
+        mRequest.userId = userId;
+
+        return this;
+    }
+
     void dump(PrintWriter pw, String prefix) {
         prefix = prefix + "  ";
         pw.print(prefix);
diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java
index e5872c03..f821f6b 100644
--- a/services/core/java/com/android/server/am/AppTaskImpl.java
+++ b/services/core/java/com/android/server/am/AppTaskImpl.java
@@ -122,9 +122,14 @@
                 throw new IllegalArgumentException("Bad app thread " + appThread);
             }
         }
-        return mService.getActivityStartController().startActivityMayWait(appThread, -1,
-                callingPackage, intent, resolvedType, null, null, null, null, 0, 0, null, null,
-                null, bOptions, false, callingUser, tr, "AppTaskImpl");
+
+        return mService.getActivityStartController().obtainStarter(intent, "AppTaskImpl")
+                .setCaller(appThread)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setMayWait(bOptions, callingUser)
+                .setInTask(tr)
+                .execute();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index d77e1a2..ba3e25a 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -22,8 +22,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Context.DEVICE_POLICY_SERVICE;
 import static android.content.Context.STATUS_BAR_SERVICE;
+import static android.content.Intent.ACTION_CALL_EMERGENCY;
 import static android.os.UserHandle.USER_ALL;
 import static android.os.UserHandle.USER_CURRENT;
+import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
@@ -45,6 +47,7 @@
 import android.app.admin.IDevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Binder;
 import android.os.Debug;
 import android.os.Handler;
@@ -52,9 +55,11 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
+import android.telecom.TelecomManager;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
@@ -133,6 +138,8 @@
     WindowManagerService mWindowManager;
     @VisibleForTesting
     LockPatternUtils mLockPatternUtils;
+    @VisibleForTesting
+    TelecomManager mTelecomManager;
 
     /**
      * Helper that is responsible for showing the right toast when a disallowed activity operation
@@ -165,7 +172,7 @@
     /**
      * Features that are allowed by DPC to show during LockTask mode.
      */
-    private final SparseArray<Integer> mLockTaskFeatures = new SparseArray<>();
+    private final SparseIntArray mLockTaskFeatures = new SparseIntArray();
 
     /**
      * Store the current lock task mode. Possible values:
@@ -298,6 +305,11 @@
             return false;
         }
 
+        // Allow emergency calling when the device is protected by a locked keyguard
+        if (isKeyguardAllowed(task.userId) && isEmergencyCallTask(task)) {
+            return false;
+        }
+
         return !(isTaskWhitelisted(task) || mLockTaskModeTasks.isEmpty());
     }
 
@@ -306,6 +318,37 @@
                 & DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS) != 0;
     }
 
+    private boolean isKeyguardAllowed(int userId) {
+        return (getLockTaskFeaturesForUser(userId)
+                & DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD) != 0;
+    }
+
+    private boolean isEmergencyCallTask(TaskRecord task) {
+        final Intent intent = task.intent;
+        if (intent == null) {
+            return false;
+        }
+
+        // 1. The emergency keypad activity launched on top of the keyguard
+        if (EMERGENCY_DIALER_COMPONENT.equals(intent.getComponent())) {
+            return true;
+        }
+
+        // 2. The intent sent by the keypad, which is handled by Telephony
+        if (ACTION_CALL_EMERGENCY.equals(intent.getAction())) {
+            return true;
+        }
+
+        // 3. Telephony then starts the default package for making the call
+        final TelecomManager tm = getTelecomManager();
+        final String dialerPackage = tm != null ? tm.getSystemDialerPackage() : null;
+        if (dialerPackage != null && dialerPackage.equals(intent.getComponent().getPackageName())) {
+            return true;
+        }
+
+        return false;
+    }
+
     /**
      * Stop the current lock task mode.
      *
@@ -686,11 +729,10 @@
             mWindowManager.reenableKeyguard(mToken);
 
         } else if (lockTaskModeState == LOCK_TASK_MODE_LOCKED) {
-            int lockTaskFeatures = getLockTaskFeaturesForUser(userId);
-            if ((DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD & lockTaskFeatures) == 0) {
-                mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
-            } else {
+            if (isKeyguardAllowed(userId)) {
                 mWindowManager.reenableKeyguard(mToken);
+            } else {
+                mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
             }
 
         } else { // lockTaskModeState == LOCK_TASK_MODE_PINNED
@@ -784,6 +826,15 @@
         return mLockPatternUtils;
     }
 
+    @Nullable
+    private TelecomManager getTelecomManager() {
+        if (mTelecomManager == null) {
+            // We don't preserve the TelecomManager object to save memory
+            return mContext.getSystemService(TelecomManager.class);
+        }
+        return mTelecomManager;
+    }
+
     // Should only be called on the handler thread
     @NonNull
     private LockTaskNotify getLockTaskNotify() {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 83965ee..48737a5 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -193,6 +193,11 @@
     // Do not move the stack as a part of reparenting
     static final int REPARENT_LEAVE_STACK_IN_PLACE = 2;
 
+    /**
+     * The factory used to create {@link TaskRecord}. This allows OEM subclass {@link TaskRecord}.
+     */
+    private static TaskRecordFactory sTaskRecordFactory;
+
     final int taskId;       // Unique identifier for this task.
     String affinity;        // The affinity name for this task, or null; may change identity.
     String rootAffinity;    // Initial base affinity, or null; does not change from initial root.
@@ -312,6 +317,10 @@
 
     private TaskWindowContainerController mWindowContainerController;
 
+    /**
+     * Don't use constructor directly. Use {@link #create(ActivityManagerService, int, ActivityInfo,
+     * Intent, TaskDescription)} instead.
+     */
     TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
             IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
         mService = service;
@@ -331,6 +340,10 @@
         mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
     }
 
+    /**
+     * Don't use constructor directly. Use {@link #create(ActivityManagerService, int, ActivityInfo,
+     * Intent, IVoiceInteractionSession, IVoiceInteractor)} instead.
+     */
     TaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info, Intent _intent,
             TaskDescription _taskDescription) {
         mService = service;
@@ -357,7 +370,10 @@
         mService.mTaskChangeNotificationController.notifyTaskCreated(_taskId, realActivity);
     }
 
-    private TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
+    /**
+     * Don't use constructor directly. This is only used by XML parser.
+     */
+    TaskRecord(ActivityManagerService service, int _taskId, Intent _intent,
             Intent _affinityIntent, String _affinity, String _rootAffinity,
             ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
             boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId,
@@ -1632,278 +1648,6 @@
         updateTaskDescription();
     }
 
-    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
-        if (DEBUG_RECENTS) Slog.i(TAG_RECENTS, "Saving task=" + this);
-
-        out.attribute(null, ATTR_TASKID, String.valueOf(taskId));
-        if (realActivity != null) {
-            out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
-        }
-        out.attribute(null, ATTR_REALACTIVITY_SUSPENDED, String.valueOf(realActivitySuspended));
-        if (origActivity != null) {
-            out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
-        }
-        // Write affinity, and root affinity if it is different from affinity.
-        // We use the special string "@" for a null root affinity, so we can identify
-        // later whether we were given a root affinity or should just make it the
-        // same as the affinity.
-        if (affinity != null) {
-            out.attribute(null, ATTR_AFFINITY, affinity);
-            if (!affinity.equals(rootAffinity)) {
-                out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
-            }
-        } else if (rootAffinity != null) {
-            out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
-        }
-        out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
-        out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
-        out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
-        out.attribute(null, ATTR_USERID, String.valueOf(userId));
-        out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete));
-        out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
-        out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
-        out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
-        if (lastDescription != null) {
-            out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
-        }
-        if (lastTaskDescription != null) {
-            lastTaskDescription.saveToXml(out);
-        }
-        out.attribute(null, ATTR_TASK_AFFILIATION_COLOR, String.valueOf(mAffiliatedTaskColor));
-        out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
-        out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
-        out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
-        out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
-        out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
-        out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode));
-        out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE,
-                String.valueOf(mSupportsPictureInPicture));
-        if (mLastNonFullscreenBounds != null) {
-            out.attribute(
-                    null, ATTR_NON_FULLSCREEN_BOUNDS, mLastNonFullscreenBounds.flattenToString());
-        }
-        out.attribute(null, ATTR_MIN_WIDTH, String.valueOf(mMinWidth));
-        out.attribute(null, ATTR_MIN_HEIGHT, String.valueOf(mMinHeight));
-        out.attribute(null, ATTR_PERSIST_TASK_VERSION, String.valueOf(PERSIST_TASK_VERSION));
-
-        if (affinityIntent != null) {
-            out.startTag(null, TAG_AFFINITYINTENT);
-            affinityIntent.saveToXml(out);
-            out.endTag(null, TAG_AFFINITYINTENT);
-        }
-
-        out.startTag(null, TAG_INTENT);
-        intent.saveToXml(out);
-        out.endTag(null, TAG_INTENT);
-
-        final ArrayList<ActivityRecord> activities = mActivities;
-        final int numActivities = activities.size();
-        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
-            final ActivityRecord r = activities.get(activityNdx);
-            if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
-                    ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
-                            | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT) &&
-                            activityNdx > 0) {
-                // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
-                break;
-            }
-            out.startTag(null, TAG_ACTIVITY);
-            r.saveToXml(out);
-            out.endTag(null, TAG_ACTIVITY);
-        }
-    }
-
-    static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
-            throws IOException, XmlPullParserException {
-        Intent intent = null;
-        Intent affinityIntent = null;
-        ArrayList<ActivityRecord> activities = new ArrayList<>();
-        ComponentName realActivity = null;
-        boolean realActivitySuspended = false;
-        ComponentName origActivity = null;
-        String affinity = null;
-        String rootAffinity = null;
-        boolean hasRootAffinity = false;
-        boolean rootHasReset = false;
-        boolean autoRemoveRecents = false;
-        boolean askedCompatMode = false;
-        int taskType = 0;
-        int userId = 0;
-        boolean userSetupComplete = true;
-        int effectiveUid = -1;
-        String lastDescription = null;
-        long lastTimeOnTop = 0;
-        boolean neverRelinquishIdentity = true;
-        int taskId = INVALID_TASK_ID;
-        final int outerDepth = in.getDepth();
-        TaskDescription taskDescription = new TaskDescription();
-        int taskAffiliation = INVALID_TASK_ID;
-        int taskAffiliationColor = 0;
-        int prevTaskId = INVALID_TASK_ID;
-        int nextTaskId = INVALID_TASK_ID;
-        int callingUid = -1;
-        String callingPackage = "";
-        int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
-        boolean supportsPictureInPicture = false;
-        Rect bounds = null;
-        int minWidth = INVALID_MIN_SIZE;
-        int minHeight = INVALID_MIN_SIZE;
-        int persistTaskVersion = 0;
-
-        for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
-            final String attrName = in.getAttributeName(attrNdx);
-            final String attrValue = in.getAttributeValue(attrNdx);
-            if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
-                    attrName + " value=" + attrValue);
-            if (ATTR_TASKID.equals(attrName)) {
-                if (taskId == INVALID_TASK_ID) taskId = Integer.parseInt(attrValue);
-            } else if (ATTR_REALACTIVITY.equals(attrName)) {
-                realActivity = ComponentName.unflattenFromString(attrValue);
-            } else if (ATTR_REALACTIVITY_SUSPENDED.equals(attrName)) {
-                realActivitySuspended = Boolean.valueOf(attrValue);
-            } else if (ATTR_ORIGACTIVITY.equals(attrName)) {
-                origActivity = ComponentName.unflattenFromString(attrValue);
-            } else if (ATTR_AFFINITY.equals(attrName)) {
-                affinity = attrValue;
-            } else if (ATTR_ROOT_AFFINITY.equals(attrName)) {
-                rootAffinity = attrValue;
-                hasRootAffinity = true;
-            } else if (ATTR_ROOTHASRESET.equals(attrName)) {
-                rootHasReset = Boolean.parseBoolean(attrValue);
-            } else if (ATTR_AUTOREMOVERECENTS.equals(attrName)) {
-                autoRemoveRecents = Boolean.parseBoolean(attrValue);
-            } else if (ATTR_ASKEDCOMPATMODE.equals(attrName)) {
-                askedCompatMode = Boolean.parseBoolean(attrValue);
-            } else if (ATTR_USERID.equals(attrName)) {
-                userId = Integer.parseInt(attrValue);
-            } else if (ATTR_USER_SETUP_COMPLETE.equals(attrName)) {
-                userSetupComplete = Boolean.parseBoolean(attrValue);
-            } else if (ATTR_EFFECTIVE_UID.equals(attrName)) {
-                effectiveUid = Integer.parseInt(attrValue);
-            } else if (ATTR_TASKTYPE.equals(attrName)) {
-                taskType = Integer.parseInt(attrValue);
-            } else if (ATTR_LASTDESCRIPTION.equals(attrName)) {
-                lastDescription = attrValue;
-            } else if (ATTR_LASTTIMEMOVED.equals(attrName)) {
-                lastTimeOnTop = Long.parseLong(attrValue);
-            } else if (ATTR_NEVERRELINQUISH.equals(attrName)) {
-                neverRelinquishIdentity = Boolean.parseBoolean(attrValue);
-            } else if (attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
-                taskDescription.restoreFromXml(attrName, attrValue);
-            } else if (ATTR_TASK_AFFILIATION.equals(attrName)) {
-                taskAffiliation = Integer.parseInt(attrValue);
-            } else if (ATTR_PREV_AFFILIATION.equals(attrName)) {
-                prevTaskId = Integer.parseInt(attrValue);
-            } else if (ATTR_NEXT_AFFILIATION.equals(attrName)) {
-                nextTaskId = Integer.parseInt(attrValue);
-            } else if (ATTR_TASK_AFFILIATION_COLOR.equals(attrName)) {
-                taskAffiliationColor = Integer.parseInt(attrValue);
-            } else if (ATTR_CALLING_UID.equals(attrName)) {
-                callingUid = Integer.parseInt(attrValue);
-            } else if (ATTR_CALLING_PACKAGE.equals(attrName)) {
-                callingPackage = attrValue;
-            } else if (ATTR_RESIZE_MODE.equals(attrName)) {
-                resizeMode = Integer.parseInt(attrValue);
-            } else if (ATTR_SUPPORTS_PICTURE_IN_PICTURE.equals(attrName)) {
-                supportsPictureInPicture = Boolean.parseBoolean(attrValue);
-            } else if (ATTR_NON_FULLSCREEN_BOUNDS.equals(attrName)) {
-                bounds = Rect.unflattenFromString(attrValue);
-            } else if (ATTR_MIN_WIDTH.equals(attrName)) {
-                minWidth = Integer.parseInt(attrValue);
-            } else if (ATTR_MIN_HEIGHT.equals(attrName)) {
-                minHeight = Integer.parseInt(attrValue);
-            } else if (ATTR_PERSIST_TASK_VERSION.equals(attrName)) {
-                persistTaskVersion = Integer.parseInt(attrValue);
-            } else {
-                Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
-            }
-        }
-
-        int event;
-        while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
-                (event != XmlPullParser.END_TAG || in.getDepth() >= outerDepth)) {
-            if (event == XmlPullParser.START_TAG) {
-                final String name = in.getName();
-                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: START_TAG name=" +
-                        name);
-                if (TAG_AFFINITYINTENT.equals(name)) {
-                    affinityIntent = Intent.restoreFromXml(in);
-                } else if (TAG_INTENT.equals(name)) {
-                    intent = Intent.restoreFromXml(in);
-                } else if (TAG_ACTIVITY.equals(name)) {
-                    ActivityRecord activity = ActivityRecord.restoreFromXml(in, stackSupervisor);
-                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" +
-                            activity);
-                    if (activity != null) {
-                        activities.add(activity);
-                    }
-                } else {
-                    Slog.e(TAG, "restoreTask: Unexpected name=" + name);
-                    XmlUtils.skipCurrentTag(in);
-                }
-            }
-        }
-        if (!hasRootAffinity) {
-            rootAffinity = affinity;
-        } else if ("@".equals(rootAffinity)) {
-            rootAffinity = null;
-        }
-        if (effectiveUid <= 0) {
-            Intent checkIntent = intent != null ? intent : affinityIntent;
-            effectiveUid = 0;
-            if (checkIntent != null) {
-                IPackageManager pm = AppGlobals.getPackageManager();
-                try {
-                    ApplicationInfo ai = pm.getApplicationInfo(
-                            checkIntent.getComponent().getPackageName(),
-                            PackageManager.MATCH_UNINSTALLED_PACKAGES
-                                    | PackageManager.MATCH_DISABLED_COMPONENTS, userId);
-                    if (ai != null) {
-                        effectiveUid = ai.uid;
-                    }
-                } catch (RemoteException e) {
-                }
-            }
-            Slog.w(TAG, "Updating task #" + taskId + " for " + checkIntent
-                    + ": effectiveUid=" + effectiveUid);
-        }
-
-        if (persistTaskVersion < 1) {
-            // We need to convert the resize mode of home activities saved before version one if
-            // they are marked as RESIZE_MODE_RESIZEABLE to RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION
-            // since we didn't have that differentiation before version 1 and the system didn't
-            // resize home activities before then.
-            if (taskType == 1 /* old home type */ && resizeMode == RESIZE_MODE_RESIZEABLE) {
-                resizeMode = RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
-            }
-        } else {
-            // This activity has previously marked itself explicitly as both resizeable and
-            // supporting picture-in-picture.  Since there is no longer a requirement for
-            // picture-in-picture activities to be resizeable, we can mark this simply as
-            // resizeable and supporting picture-in-picture separately.
-            if (resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED) {
-                resizeMode = RESIZE_MODE_RESIZEABLE;
-                supportsPictureInPicture = true;
-            }
-        }
-
-        final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
-                affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
-                autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
-                activities, lastTimeOnTop, neverRelinquishIdentity, taskDescription,
-                taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid,
-                callingPackage, resizeMode, supportsPictureInPicture, realActivitySuspended,
-                userSetupComplete, minWidth, minHeight);
-        task.updateOverrideConfiguration(bounds);
-
-        for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
-            activities.get(activityNdx).setTask(task);
-        }
-
-        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task);
-        return task;
-    }
-
     private void adjustForMinimalTaskDimensions(Rect bounds) {
         if (bounds == null) {
             return;
@@ -2320,4 +2064,382 @@
             top = base = null;
         }
     }
+
+    /**
+     * Saves this {@link TaskRecord} to XML using given serializer.
+     */
+    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
+        if (DEBUG_RECENTS) Slog.i(TAG_RECENTS, "Saving task=" + this);
+
+        out.attribute(null, ATTR_TASKID, String.valueOf(taskId));
+        if (realActivity != null) {
+            out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
+        }
+        out.attribute(null, ATTR_REALACTIVITY_SUSPENDED, String.valueOf(realActivitySuspended));
+        if (origActivity != null) {
+            out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
+        }
+        // Write affinity, and root affinity if it is different from affinity.
+        // We use the special string "@" for a null root affinity, so we can identify
+        // later whether we were given a root affinity or should just make it the
+        // same as the affinity.
+        if (affinity != null) {
+            out.attribute(null, ATTR_AFFINITY, affinity);
+            if (!affinity.equals(rootAffinity)) {
+                out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
+            }
+        } else if (rootAffinity != null) {
+            out.attribute(null, ATTR_ROOT_AFFINITY, rootAffinity != null ? rootAffinity : "@");
+        }
+        out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
+        out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
+        out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
+        out.attribute(null, ATTR_USERID, String.valueOf(userId));
+        out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete));
+        out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
+        out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
+        out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
+        if (lastDescription != null) {
+            out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
+        }
+        if (lastTaskDescription != null) {
+            lastTaskDescription.saveToXml(out);
+        }
+        out.attribute(null, ATTR_TASK_AFFILIATION_COLOR, String.valueOf(mAffiliatedTaskColor));
+        out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
+        out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
+        out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
+        out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
+        out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
+        out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode));
+        out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE,
+                String.valueOf(mSupportsPictureInPicture));
+        if (mLastNonFullscreenBounds != null) {
+            out.attribute(
+                    null, ATTR_NON_FULLSCREEN_BOUNDS, mLastNonFullscreenBounds.flattenToString());
+        }
+        out.attribute(null, ATTR_MIN_WIDTH, String.valueOf(mMinWidth));
+        out.attribute(null, ATTR_MIN_HEIGHT, String.valueOf(mMinHeight));
+        out.attribute(null, ATTR_PERSIST_TASK_VERSION, String.valueOf(PERSIST_TASK_VERSION));
+
+        if (affinityIntent != null) {
+            out.startTag(null, TAG_AFFINITYINTENT);
+            affinityIntent.saveToXml(out);
+            out.endTag(null, TAG_AFFINITYINTENT);
+        }
+
+        out.startTag(null, TAG_INTENT);
+        intent.saveToXml(out);
+        out.endTag(null, TAG_INTENT);
+
+        final ArrayList<ActivityRecord> activities = mActivities;
+        final int numActivities = activities.size();
+        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
+            final ActivityRecord r = activities.get(activityNdx);
+            if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
+                    ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
+                            | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT) &&
+                            activityNdx > 0) {
+                // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
+                break;
+            }
+            out.startTag(null, TAG_ACTIVITY);
+            r.saveToXml(out);
+            out.endTag(null, TAG_ACTIVITY);
+        }
+    }
+
+    @VisibleForTesting
+    static TaskRecordFactory getTaskRecordFactory() {
+        if (sTaskRecordFactory == null) {
+            setTaskRecordFactory(new TaskRecordFactory());
+        }
+        return sTaskRecordFactory;
+    }
+
+    static void setTaskRecordFactory(TaskRecordFactory factory) {
+        sTaskRecordFactory = factory;
+    }
+
+    static TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+            Intent intent, IVoiceInteractionSession voiceSession,
+            IVoiceInteractor voiceInteractor) {
+        return getTaskRecordFactory().create(
+                service, taskId, info, intent, voiceSession, voiceInteractor);
+    }
+
+    static TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+            Intent intent, TaskDescription taskDescription) {
+        return getTaskRecordFactory().create(service, taskId, info, intent, taskDescription);
+    }
+
+    static TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
+            throws IOException, XmlPullParserException {
+        return getTaskRecordFactory().restoreFromXml(in, stackSupervisor);
+    }
+
+    /**
+     * A factory class used to create {@link TaskRecord} or its subclass if any. This can be
+     * specified when system boots by setting it with
+     * {@link #setTaskRecordFactory(TaskRecordFactory)}.
+     */
+    static class TaskRecordFactory {
+
+        TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+                Intent intent, IVoiceInteractionSession voiceSession,
+                IVoiceInteractor voiceInteractor) {
+            return new TaskRecord(
+                    service, taskId, info, intent, voiceSession, voiceInteractor);
+        }
+
+        TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+                Intent intent, TaskDescription taskDescription) {
+            return new TaskRecord(service, taskId, info, intent, taskDescription);
+        }
+
+        /**
+         * Should only be used when we're restoring {@link TaskRecord} from storage.
+         */
+        TaskRecord create(ActivityManagerService service, int taskId, Intent intent,
+                Intent affinityIntent, String affinity, String rootAffinity,
+                ComponentName realActivity, ComponentName origActivity, boolean rootWasReset,
+                boolean autoRemoveRecents, boolean askedCompatMode, int userId,
+                int effectiveUid, String lastDescription, ArrayList<ActivityRecord> activities,
+                long lastTimeMoved, boolean neverRelinquishIdentity,
+                TaskDescription lastTaskDescription, int taskAffiliation, int prevTaskId,
+                int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
+                int resizeMode, boolean supportsPictureInPicture, boolean realActivitySuspended,
+                boolean userSetupComplete, int minWidth, int minHeight) {
+            return new TaskRecord(service, taskId, intent, affinityIntent, affinity,
+                    rootAffinity, realActivity, origActivity, rootWasReset, autoRemoveRecents,
+                    askedCompatMode, userId, effectiveUid, lastDescription, activities,
+                    lastTimeMoved, neverRelinquishIdentity, lastTaskDescription, taskAffiliation,
+                    prevTaskId, nextTaskId, taskAffiliationColor, callingUid, callingPackage,
+                    resizeMode, supportsPictureInPicture, realActivitySuspended, userSetupComplete,
+                    minWidth, minHeight);
+        }
+
+        TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
+                throws IOException, XmlPullParserException {
+            Intent intent = null;
+            Intent affinityIntent = null;
+            ArrayList<ActivityRecord> activities = new ArrayList<>();
+            ComponentName realActivity = null;
+            boolean realActivitySuspended = false;
+            ComponentName origActivity = null;
+            String affinity = null;
+            String rootAffinity = null;
+            boolean hasRootAffinity = false;
+            boolean rootHasReset = false;
+            boolean autoRemoveRecents = false;
+            boolean askedCompatMode = false;
+            int taskType = 0;
+            int userId = 0;
+            boolean userSetupComplete = true;
+            int effectiveUid = -1;
+            String lastDescription = null;
+            long lastTimeOnTop = 0;
+            boolean neverRelinquishIdentity = true;
+            int taskId = INVALID_TASK_ID;
+            final int outerDepth = in.getDepth();
+            TaskDescription taskDescription = new TaskDescription();
+            int taskAffiliation = INVALID_TASK_ID;
+            int taskAffiliationColor = 0;
+            int prevTaskId = INVALID_TASK_ID;
+            int nextTaskId = INVALID_TASK_ID;
+            int callingUid = -1;
+            String callingPackage = "";
+            int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
+            boolean supportsPictureInPicture = false;
+            Rect bounds = null;
+            int minWidth = INVALID_MIN_SIZE;
+            int minHeight = INVALID_MIN_SIZE;
+            int persistTaskVersion = 0;
+
+            for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
+                final String attrName = in.getAttributeName(attrNdx);
+                final String attrValue = in.getAttributeValue(attrNdx);
+                if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: attribute name=" +
+                        attrName + " value=" + attrValue);
+                switch (attrName) {
+                    case ATTR_TASKID:
+                        if (taskId == INVALID_TASK_ID) taskId = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_REALACTIVITY:
+                        realActivity = ComponentName.unflattenFromString(attrValue);
+                        break;
+                    case ATTR_REALACTIVITY_SUSPENDED:
+                        realActivitySuspended = Boolean.valueOf(attrValue);
+                        break;
+                    case ATTR_ORIGACTIVITY:
+                        origActivity = ComponentName.unflattenFromString(attrValue);
+                        break;
+                    case ATTR_AFFINITY:
+                        affinity = attrValue;
+                        break;
+                    case ATTR_ROOT_AFFINITY:
+                        rootAffinity = attrValue;
+                        hasRootAffinity = true;
+                        break;
+                    case ATTR_ROOTHASRESET:
+                        rootHasReset = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_AUTOREMOVERECENTS:
+                        autoRemoveRecents = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_ASKEDCOMPATMODE:
+                        askedCompatMode = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_USERID:
+                        userId = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_USER_SETUP_COMPLETE:
+                        userSetupComplete = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_EFFECTIVE_UID:
+                        effectiveUid = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_TASKTYPE:
+                        taskType = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_LASTDESCRIPTION:
+                        lastDescription = attrValue;
+                        break;
+                    case ATTR_LASTTIMEMOVED:
+                        lastTimeOnTop = Long.parseLong(attrValue);
+                        break;
+                    case ATTR_NEVERRELINQUISH:
+                        neverRelinquishIdentity = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_TASK_AFFILIATION:
+                        taskAffiliation = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_PREV_AFFILIATION:
+                        prevTaskId = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_NEXT_AFFILIATION:
+                        nextTaskId = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_TASK_AFFILIATION_COLOR:
+                        taskAffiliationColor = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_CALLING_UID:
+                        callingUid = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_CALLING_PACKAGE:
+                        callingPackage = attrValue;
+                        break;
+                    case ATTR_RESIZE_MODE:
+                        resizeMode = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_SUPPORTS_PICTURE_IN_PICTURE:
+                        supportsPictureInPicture = Boolean.parseBoolean(attrValue);
+                        break;
+                    case ATTR_NON_FULLSCREEN_BOUNDS:
+                        bounds = Rect.unflattenFromString(attrValue);
+                        break;
+                    case ATTR_MIN_WIDTH:
+                        minWidth = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_MIN_HEIGHT:
+                        minHeight = Integer.parseInt(attrValue);
+                        break;
+                    case ATTR_PERSIST_TASK_VERSION:
+                        persistTaskVersion = Integer.parseInt(attrValue);
+                        break;
+                    default:
+                        if (attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) {
+                            taskDescription.restoreFromXml(attrName, attrValue);
+                        } else {
+                            Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
+                        }
+                }
+            }
+
+            int event;
+            while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
+                    (event != XmlPullParser.END_TAG || in.getDepth() >= outerDepth)) {
+                if (event == XmlPullParser.START_TAG) {
+                    final String name = in.getName();
+                    if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG,
+                            "TaskRecord: START_TAG name=" + name);
+                    if (TAG_AFFINITYINTENT.equals(name)) {
+                        affinityIntent = Intent.restoreFromXml(in);
+                    } else if (TAG_INTENT.equals(name)) {
+                        intent = Intent.restoreFromXml(in);
+                    } else if (TAG_ACTIVITY.equals(name)) {
+                        ActivityRecord activity =
+                                ActivityRecord.restoreFromXml(in, stackSupervisor);
+                        if (TaskPersister.DEBUG) Slog.d(TaskPersister.TAG, "TaskRecord: activity=" +
+                                activity);
+                        if (activity != null) {
+                            activities.add(activity);
+                        }
+                    } else {
+                        Slog.e(TAG, "restoreTask: Unexpected name=" + name);
+                        XmlUtils.skipCurrentTag(in);
+                    }
+                }
+            }
+            if (!hasRootAffinity) {
+                rootAffinity = affinity;
+            } else if ("@".equals(rootAffinity)) {
+                rootAffinity = null;
+            }
+            if (effectiveUid <= 0) {
+                Intent checkIntent = intent != null ? intent : affinityIntent;
+                effectiveUid = 0;
+                if (checkIntent != null) {
+                    IPackageManager pm = AppGlobals.getPackageManager();
+                    try {
+                        ApplicationInfo ai = pm.getApplicationInfo(
+                                checkIntent.getComponent().getPackageName(),
+                                PackageManager.MATCH_UNINSTALLED_PACKAGES
+                                        | PackageManager.MATCH_DISABLED_COMPONENTS, userId);
+                        if (ai != null) {
+                            effectiveUid = ai.uid;
+                        }
+                    } catch (RemoteException e) {
+                    }
+                }
+                Slog.w(TAG, "Updating task #" + taskId + " for " + checkIntent
+                        + ": effectiveUid=" + effectiveUid);
+            }
+
+            if (persistTaskVersion < 1) {
+                // We need to convert the resize mode of home activities saved before version one if
+                // they are marked as RESIZE_MODE_RESIZEABLE to
+                // RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION since we didn't have that differentiation
+                // before version 1 and the system didn't resize home activities before then.
+                if (taskType == 1 /* old home type */ && resizeMode == RESIZE_MODE_RESIZEABLE) {
+                    resizeMode = RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+                }
+            } else {
+                // This activity has previously marked itself explicitly as both resizeable and
+                // supporting picture-in-picture.  Since there is no longer a requirement for
+                // picture-in-picture activities to be resizeable, we can mark this simply as
+                // resizeable and supporting picture-in-picture separately.
+                if (resizeMode == RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED) {
+                    resizeMode = RESIZE_MODE_RESIZEABLE;
+                    supportsPictureInPicture = true;
+                }
+            }
+
+            final TaskRecord task = create(stackSupervisor.mService, taskId, intent, affinityIntent,
+                    affinity, rootAffinity, realActivity, origActivity, rootHasReset,
+                    autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
+                    activities, lastTimeOnTop, neverRelinquishIdentity, taskDescription,
+                    taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid,
+                    callingPackage, resizeMode, supportsPictureInPicture, realActivitySuspended,
+                    userSetupComplete, minWidth, minHeight);
+            task.updateOverrideConfiguration(bounds);
+
+            for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
+                activities.get(activityNdx).setTask(task);
+            }
+
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task);
+            return task;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 4e3d8d2..6bd599b 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -24,6 +24,7 @@
 import static android.app.ActivityManager.USER_OP_SUCCESS;
 import static android.os.Process.SHELL_UID;
 import static android.os.Process.SYSTEM_UID;
+
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -37,6 +38,7 @@
 import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKING;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
@@ -830,6 +832,9 @@
     private IStorageManager getStorageManager() {
         return IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
     }
+    boolean startUser(final int userId, final boolean foreground) {
+        return startUser(userId, foreground, null);
+    }
 
     /**
      * Start user, if its not already running.
@@ -860,7 +865,10 @@
      * @param foreground true if user should be brought to the foreground
      * @return true if the user has been successfully started
      */
-    boolean startUser(final int userId, final boolean foreground) {
+    boolean startUser(
+            final int userId,
+            final boolean foreground,
+            @Nullable IProgressListener unlockListener) {
         if (mInjector.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
                 != PackageManager.PERMISSION_GRANTED) {
             String msg = "Permission Denial: switchUser() from pid="
@@ -918,6 +926,10 @@
                 final Integer userIdInt = userId;
                 mUserLru.remove(userIdInt);
                 mUserLru.add(userIdInt);
+
+                if (unlockListener != null) {
+                    uss.mUnlockProgress.addListener(unlockListener);
+                }
             }
             if (updateUmState) {
                 mInjector.getUserManagerInternal().setUserState(userId, uss.state);
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 2c6fe94..61b6fa0 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -196,9 +196,10 @@
 
     /**
      * @param userId userId to fetch data for.
+     * @param includePackage if false we will null out BrightnessChangeEvent.packageName
      * @return List of recent {@link BrightnessChangeEvent}s
      */
-    public ParceledListSlice<BrightnessChangeEvent> getEvents(int userId) {
+    public ParceledListSlice<BrightnessChangeEvent> getEvents(int userId, boolean includePackage) {
         // TODO include apps from any managed profiles in the brightness information.
         BrightnessChangeEvent[] events;
         synchronized (mEventsLock) {
@@ -207,7 +208,13 @@
         ArrayList<BrightnessChangeEvent> out = new ArrayList<>(events.length);
         for (int i = 0; i < events.length; ++i) {
             if (events[i].userId == userId) {
-                out.add(events[i]);
+                if (includePackage) {
+                    out.add(events[i]);
+                } else {
+                    BrightnessChangeEvent event = new BrightnessChangeEvent((events[i]));
+                    event.packageName = null;
+                    out.add(event);
+                }
             }
         }
         return new ParceledListSlice<>(out);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 379aaad..19a74d7 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -29,6 +29,7 @@
 
 import android.Manifest;
 import android.annotation.NonNull;
+import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
@@ -1752,14 +1753,30 @@
         }
 
         @Override // Binder call
-        public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents() {
+        public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(String callingPackage) {
             mContext.enforceCallingOrSelfPermission(
                     Manifest.permission.BRIGHTNESS_SLIDER_USAGE,
                     "Permission to read brightness events.");
-            int userId = UserHandle.getUserId(Binder.getCallingUid());
+
+            final int callingUid = Binder.getCallingUid();
+            AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
+            final int mode = appOpsManager.checkOp(AppOpsManager.OP_GET_USAGE_STATS,
+                    callingUid, callingPackage);
+            final boolean hasUsageStats;
+            if (mode == AppOpsManager.MODE_DEFAULT) {
+                // The default behavior here is to check if PackageManager has given the app
+                // permission.
+                hasUsageStats = mContext.checkCallingPermission(
+                        Manifest.permission.PACKAGE_USAGE_STATS)
+                        == PackageManager.PERMISSION_GRANTED;
+            } else {
+                hasUsageStats = mode == AppOpsManager.MODE_ALLOWED;
+            }
+
+            final int userId = UserHandle.getUserId(callingUid);
             final long token = Binder.clearCallingIdentity();
             try {
-                return mBrightnessTracker.getEvents(userId);
+                return mBrightnessTracker.getEvents(userId, hasUsageStats);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index e08c659..4525a49 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -479,7 +479,11 @@
             retArray[i] = foundInstances.get(i).intValue();
         }
 
-        Log.w(TAG, "Found " + retArray.length + " apps on hub handle " + hubHandle);
+        if (retArray.length == 0) {
+            Log.d(TAG, "No nanoapps found on hub ID " + hubHandle + " using NanoAppFilter: "
+                    + filter);
+        }
+
         return retArray;
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index bec6fc2..cf01400 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4743,8 +4743,7 @@
     }
 
     void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
-        if (!mAccessibilityManager.isObservedEventType(
-                AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)) {
+        if (!mAccessibilityManager.isEnabled()) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
index c5f80bb..ba7fe78 100644
--- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
+++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java
@@ -29,8 +29,8 @@
 import android.provider.Settings;
 import android.service.notification.Condition;
 import android.service.notification.IConditionProvider;
+import android.service.notification.ScheduleCalendar;
 import android.service.notification.ZenModeConfig;
-import android.service.notification.ZenModeConfig.ScheduleInfo;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -45,7 +45,6 @@
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.List;
-import java.util.TimeZone;
 
 /**
  * Built-in zen condition provider for daily scheduled time-based conditions.
@@ -134,7 +133,7 @@
             return;
         }
         synchronized (mSubscriptions) {
-            mSubscriptions.put(conditionId, toScheduleCalendar(conditionId));
+            mSubscriptions.put(conditionId, ZenModeConfig.toScheduleCalendar(conditionId));
         }
         evaluateSubscriptions();
     }
@@ -243,15 +242,6 @@
         return cal != null && cal.isInSchedule(time);
     }
 
-    private static ScheduleCalendar toScheduleCalendar(Uri conditionId) {
-        final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(conditionId);
-        if (schedule == null || schedule.days == null || schedule.days.length == 0) return null;
-        final ScheduleCalendar sc = new ScheduleCalendar();
-        sc.setSchedule(schedule);
-        sc.setTimeZone(TimeZone.getDefault());
-        return sc;
-    }
-
     private void setRegistered(boolean registered) {
         if (mRegistered == registered) return;
         if (DEBUG) Slog.d(TAG, "setRegistered " + registered);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 1e9fab5..700ccad 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -98,11 +98,6 @@
     private final Metrics mMetrics = new Metrics();
     private final ConditionProviders.Config mServiceConfig;
 
-    protected final ArrayList<String> mDefaultRuleIds = new ArrayList<>();
-    private final String EVENTS_DEFAULT_RULE = "EVENTS_DEFAULT_RULE";
-    private final String SCHEDULED_DEFAULT_RULE_1 = "SCHEDULED_DEFAULT_RULE_1";
-    private final String SCHEDULED_DEFAULT_RULE_2 = "SCHEDULED_DEFAULT_RULE_2";
-
     @VisibleForTesting protected int mZenMode;
     private int mUser = UserHandle.USER_SYSTEM;
     @VisibleForTesting protected ZenModeConfig mConfig;
@@ -115,9 +110,8 @@
     public static final long SUPPRESSED_EFFECT_ALL = SUPPRESSED_EFFECT_CALLS
             | SUPPRESSED_EFFECT_NOTIFICATIONS;
 
-    protected String mDefaultRuleWeeknightsName;
+    protected String mDefaultRuleEveryNightName;
     protected String mDefaultRuleEventsName;
-    protected String mDefaultRuleWeekendsName;
 
     public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) {
         mContext = context;
@@ -230,12 +224,25 @@
             config = mDefaultConfig.copy();
             config.user = user;
         }
+        enforceDefaultRulesExist(config);
         synchronized (mConfig) {
             setConfigLocked(config, reason);
         }
         cleanUpZenRules();
     }
 
+    private void enforceDefaultRulesExist(ZenModeConfig config) {
+        for (String id : ZenModeConfig.DEFAULT_RULE_IDS) {
+            if (!config.automaticRules.containsKey(id)) {
+                if (id.equals(ZenModeConfig.EVENTS_DEFAULT_RULE_ID)) {
+                    appendDefaultEventRules(config);
+                } else if (id.equals(ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID)) {
+                    appendDefaultEveryNightRule(config);
+                }
+            }
+        }
+    }
+
     public int getZenModeListenerInterruptionFilter() {
         return NotificationManager.zenModeToInterruptionFilter(mZenMode);
     }
@@ -421,17 +428,12 @@
 
     public void setDefaultZenRules(Context context) {
         mDefaultConfig = readDefaultConfig(context.getResources());
-
-        mDefaultRuleIds.add(EVENTS_DEFAULT_RULE);
-        mDefaultRuleIds.add(SCHEDULED_DEFAULT_RULE_1);
-        mDefaultRuleIds.add(SCHEDULED_DEFAULT_RULE_2);
-
         appendDefaultRules(mDefaultConfig);
     }
 
     private void appendDefaultRules (ZenModeConfig config) {
         getDefaultRuleNames();
-        appendDefaultScheduleRules(config);
+        appendDefaultEveryNightRule(config);
         appendDefaultEventRules(config);
     }
 
@@ -450,7 +452,7 @@
     protected void updateDefaultZenRules() {
         ZenModeConfig configDefaultRules = new ZenModeConfig();
         appendDefaultRules(configDefaultRules); // "new" localized default rules
-        for (String ruleId : mDefaultRuleIds) {
+        for (String ruleId : ZenModeConfig.DEFAULT_RULE_IDS) {
             AutomaticZenRule currRule = getAutomaticZenRule(ruleId);
             ZenRule defaultRule = configDefaultRules.automaticRules.get(ruleId);
             // if default rule wasn't customized, use localized name instead of previous
@@ -812,10 +814,8 @@
 
     private void getDefaultRuleNames() {
         // on locale-change, these values differ
-        mDefaultRuleWeeknightsName = mContext.getResources()
-                .getString(R.string.zen_mode_default_weeknights_name);
-        mDefaultRuleWeekendsName = mContext.getResources()
-                .getString(R.string.zen_mode_default_weekends_name);
+        mDefaultRuleEveryNightName = mContext.getResources()
+                .getString(R.string.zen_mode_default_every_night_name);
         mDefaultRuleEventsName = mContext.getResources()
                 .getString(R.string.zen_mode_default_events_name);
     }
@@ -935,39 +935,23 @@
         return new ZenModeConfig();
     }
 
-    private void appendDefaultScheduleRules(ZenModeConfig config) {
+    private void appendDefaultEveryNightRule(ZenModeConfig config) {
         if (config == null) return;
 
         final ScheduleInfo weeknights = new ScheduleInfo();
-        weeknights.days = ZenModeConfig.WEEKNIGHT_DAYS;
+        weeknights.days = ZenModeConfig.ALL_DAYS;
         weeknights.startHour = 22;
         weeknights.endHour = 7;
         weeknights.exitAtAlarm = true;
-        final ZenRule rule1 = new ZenRule();
-        rule1.enabled = false;
-        rule1.name = mDefaultRuleWeeknightsName;
-        rule1.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
-        rule1.zenMode = Global.ZEN_MODE_ALARMS;
-        rule1.component = ScheduleConditionProvider.COMPONENT;
-        rule1.id = SCHEDULED_DEFAULT_RULE_1;
-        rule1.creationTime = System.currentTimeMillis();
-        config.automaticRules.put(rule1.id, rule1);
-
-        final ScheduleInfo weekends = new ScheduleInfo();
-        weekends.days = ZenModeConfig.WEEKEND_DAYS;
-        weekends.startHour = 23;
-        weekends.startMinute = 30;
-        weekends.endHour = 10;
-        weekends.exitAtAlarm = true;
-        final ZenRule rule2 = new ZenRule();
-        rule2.enabled = false;
-        rule2.name = mDefaultRuleWeekendsName;
-        rule2.conditionId = ZenModeConfig.toScheduleConditionId(weekends);
-        rule2.zenMode = Global.ZEN_MODE_ALARMS;
-        rule2.component = ScheduleConditionProvider.COMPONENT;
-        rule2.id = SCHEDULED_DEFAULT_RULE_2;
-        rule2.creationTime = System.currentTimeMillis();
-        config.automaticRules.put(rule2.id, rule2);
+        final ZenRule rule = new ZenRule();
+        rule.enabled = false;
+        rule.name = mDefaultRuleEveryNightName;
+        rule.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
+        rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        rule.component = ScheduleConditionProvider.COMPONENT;
+        rule.id = ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID;
+        rule.creationTime = System.currentTimeMillis();
+        config.automaticRules.put(rule.id, rule);
     }
 
     private void appendDefaultEventRules(ZenModeConfig config) {
@@ -980,9 +964,9 @@
         rule.enabled = false;
         rule.name = mDefaultRuleEventsName;
         rule.conditionId = ZenModeConfig.toEventConditionId(events);
-        rule.zenMode = Global.ZEN_MODE_ALARMS;
+        rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         rule.component = EventConditionProvider.COMPONENT;
-        rule.id = EVENTS_DEFAULT_RULE;
+        rule.id = ZenModeConfig.EVENTS_DEFAULT_RULE_ID;
         rule.creationTime = System.currentTimeMillis();
         config.automaticRules.put(rule.id, rule);
     }
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 6a06be2..be66fe2 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -496,6 +496,26 @@
         }
     }
 
+    public boolean createProfileSnapshot(int appId, String packageName, String codePath)
+            throws InstallerException {
+        if (!checkBeforeRemote()) return false;
+        try {
+            return mInstalld.createProfileSnapshot(appId, packageName, codePath);
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
+    public void destroyProfileSnapshot(String packageName, String codePath)
+            throws InstallerException {
+        if (!checkBeforeRemote()) return;
+        try {
+            mInstalld.destroyProfileSnapshot(packageName, codePath);
+        } catch (Exception e) {
+            throw InstallerException.from(e);
+        }
+    }
+
     public void invalidateMounts() throws InstallerException {
         if (!checkBeforeRemote()) return;
         try {
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index 88fc65e..30072d4 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -40,14 +40,11 @@
 import android.content.pm.InstantAppResolveInfo;
 import android.content.pm.InstantAppResolveInfo.InstantAppDigest;
 import android.metrics.LogMaker;
-import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
-import android.util.Pair;
-import android.util.Slog;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
@@ -56,11 +53,9 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
-import java.util.concurrent.TimeoutException;
 
 /** @hide */
 public abstract class InstantAppResolver {
@@ -161,6 +156,7 @@
                 final String splitName;
                 final long versionCode;
                 final Intent failureIntent;
+                final Bundle extras;
                 if (instantAppResolveInfoList != null && instantAppResolveInfoList.size() > 0) {
                     final AuxiliaryResolveInfo instantAppIntentInfo =
                             InstantAppResolver.filterInstantAppIntent(
@@ -172,17 +168,20 @@
                         splitName = instantAppIntentInfo.splitName;
                         versionCode = instantAppIntentInfo.resolveInfo.getVersionCode();
                         failureIntent = instantAppIntentInfo.failureIntent;
+                        extras = instantAppIntentInfo.resolveInfo.getExtras();
                     } else {
                         packageName = null;
                         splitName = null;
                         versionCode = -1;
                         failureIntent = null;
+                        extras = null;
                     }
                 } else {
                     packageName = null;
                     splitName = null;
                     versionCode = -1;
                     failureIntent = null;
+                    extras = null;
                 }
                 final Intent installerIntent = buildEphemeralInstallerIntent(
                         Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE,
@@ -197,6 +196,7 @@
                         requestObj.responseObj.installFailureActivity,
                         versionCode,
                         token,
+                        extras,
                         false /*needsPhaseTwo*/);
                 installerIntent.setComponent(new ComponentName(
                         instantAppInstaller.packageName, instantAppInstaller.name));
@@ -243,6 +243,7 @@
             @Nullable ComponentName installFailureActivity,
             long versionCode,
             @Nullable String token,
+            @Nullable Bundle extras,
             boolean needsPhaseTwo) {
         // Construct the intent that launches the instant installer
         int flags = origIntent.getFlags();
@@ -259,6 +260,9 @@
             intent.putExtra(Intent.EXTRA_EPHEMERAL_HOSTNAME, origIntent.getData().getHost());
         }
         intent.putExtra(Intent.EXTRA_INSTANT_APP_ACTION, origIntent.getAction());
+        if (extras != null) {
+            intent.putExtra(Intent.EXTRA_INSTANT_APP_EXTRAS, extras);
+        }
 
         // We have all of the data we need; just start the installer without a second phase
         if (!needsPhaseTwo) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9804283..8b2854c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3038,7 +3038,7 @@
             }
 
             mInstallerService = new PackageInstallerService(context, this);
-            mArtManagerService = new ArtManagerService(this);
+            mArtManagerService = new ArtManagerService(this, mInstaller, mInstallLock);
             final Pair<ComponentName, String> instantAppResolverComponent =
                     getInstantAppResolverLPr();
             if (instantAppResolverComponent != null) {
@@ -9495,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
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
index 5a1f840..2dbb34d 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -21,15 +21,23 @@
 import android.content.pm.PackageManager;
 import android.content.pm.dex.ArtManager;
 import android.os.Binder;
+import android.os.Environment;
 import android.os.Handler;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.content.pm.IPackageManager;
 import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.Preconditions;
+import com.android.server.pm.Installer;
+import com.android.server.pm.Installer.InstallerException;
+import java.io.File;
+import java.io.FileNotFoundException;
 
 /**
  * A system service that provides access to runtime and compiler artifacts.
@@ -50,10 +58,16 @@
     private static boolean DEBUG_IGNORE_PERMISSIONS = false;
 
     private final IPackageManager mPackageManager;
+    private final Object mInstallLock;
+    @GuardedBy("mInstallLock")
+    private final Installer mInstaller;
+
     private final Handler mHandler;
 
-    public ArtManagerService(IPackageManager pm) {
+    public ArtManagerService(IPackageManager pm, Installer installer, Object installLock) {
         mPackageManager = pm;
+        mInstaller = installer;
+        mInstallLock = installLock;
         mHandler = new Handler(BackgroundThread.getHandler().getLooper());
     }
 
@@ -105,8 +119,53 @@
             return;
         }
 
-        // All good, move forward and get the profile.
-        postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+        // All good, create the profile snapshot.
+        createProfileSnapshot(packageName, codePath, callback, info);
+        // Destroy the snapshot, we no longer need it.
+        destroyProfileSnapshot(packageName, codePath);
+    }
+
+    private void createProfileSnapshot(String packageName, String codePath,
+            ISnapshotRuntimeProfileCallback callback, PackageInfo info) {
+        // Ask the installer to snapshot the profile.
+        synchronized (mInstallLock) {
+            try {
+                if (!mInstaller.createProfileSnapshot(UserHandle.getAppId(info.applicationInfo.uid),
+                        packageName, codePath)) {
+                    postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+                    return;
+                }
+            } catch (InstallerException e) {
+                postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+                return;
+            }
+        }
+
+        // Open the snapshot and invoke the callback.
+        File snapshotProfile = Environment.getProfileSnapshotPath(packageName, codePath);
+        ParcelFileDescriptor fd;
+        try {
+            fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY);
+            postSuccess(packageName, fd, callback);
+        } catch (FileNotFoundException e) {
+            Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":" + codePath, e);
+            postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
+        }
+    }
+
+    private void destroyProfileSnapshot(String packageName, String codePath) {
+        if (DEBUG) {
+            Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + codePath);
+        }
+
+        synchronized (mInstallLock) {
+            try {
+                mInstaller.destroyProfileSnapshot(packageName, codePath);
+            } catch (InstallerException e) {
+                Slog.e(TAG, "Failed to destroy profile snapshot for " +
+                    packageName + ":" + codePath, e);
+            }
+        }
     }
 
     @Override
@@ -123,6 +182,10 @@
      */
     private void postError(ISnapshotRuntimeProfileCallback callback, String packageName,
             int errCode) {
+        if (DEBUG) {
+            Slog.d(TAG, "Failed to snapshot profile for " + packageName + " with error: " +
+                    errCode);
+        }
         mHandler.post(() -> {
             try {
                 callback.onError(errCode);
@@ -132,6 +195,21 @@
         });
     }
 
+    private void postSuccess(String packageName, ParcelFileDescriptor fd,
+            ISnapshotRuntimeProfileCallback callback) {
+        if (DEBUG) {
+            Slog.d(TAG, "Successfully snapshot profile for " + packageName);
+        }
+        mHandler.post(() -> {
+            try {
+                callback.onSuccess(fd);
+            } catch (RemoteException e) {
+                Slog.w(TAG,
+                        "Failed to call onSuccess after profile snapshot for " + packageName, e);
+            }
+        });
+    }
+
     /**
      * Verify that the binder calling uid has {@code android.permission.READ_RUNTIME_PROFILE}.
      * If not, it throws a {@link SecurityException}.
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7415ec3..4da7452 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -239,6 +239,7 @@
 import android.view.inputmethod.InputMethodManagerInternal;
 
 import com.android.internal.R;
+import com.android.internal.accessibility.AccessibilityShortcutController;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
@@ -246,6 +247,7 @@
 import com.android.internal.policy.IShortcutService;
 import com.android.internal.policy.PhoneWindow;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ScreenShapeHelper;
 import com.android.internal.widget.PointerLocationView;
 import com.android.server.GestureLauncherService;
@@ -3379,7 +3381,7 @@
     }
 
     boolean keyguardOn() {
-        return isKeyguardShowingAndNotOccluded() || inKeyguardRestrictedKeyInputMode();
+        return isKeyguardShowingAndNotOccluded();
     }
 
     private static final int[] WINDOW_TYPES_WHERE_HOME_DOESNT_WORK = {
@@ -6870,13 +6872,6 @@
         return mKeyguardOccluded;
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public boolean inKeyguardRestrictedKeyInputMode() {
-        if (mKeyguardDelegate == null) return false;
-        return mKeyguardDelegate.isInputRestricted();
-    }
-
     @Override
     public void dismissKeyguardLw(IKeyguardDismissCallback callback) {
         if (mKeyguardDelegate != null && mKeyguardDelegate.isShowing()) {
@@ -7213,15 +7208,7 @@
     }
 
     static long[] getLongIntArray(Resources r, int resid) {
-        int[] ar = r.getIntArray(resid);
-        if (ar == null) {
-            return null;
-        }
-        long[] out = new long[ar.length];
-        for (int i=0; i<ar.length; i++) {
-            out[i] = ar[i];
-        }
-        return out;
+        return ArrayUtils.convertToLongArray(r.getIntArray(resid));
     }
 
     private void bindKeyguard() {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 5f067d4..3d458c7 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1357,17 +1357,6 @@
     public boolean isKeyguardTrustedLw();
 
     /**
-     * inKeyguardRestrictedKeyInputMode
-     *
-     * if keyguard screen is showing or in restricted key input mode (i.e. in
-     * keyguard password emergency screen). When in such mode, certain keys,
-     * such as the Home key and the right soft keys, don't work.
-     *
-     * @return true if in keyguard restricted input mode.
-     */
-    public boolean inKeyguardRestrictedKeyInputMode();
-
-    /**
      * Ask the policy to dismiss the keyguard, if it is currently shown.
      *
      * @param callback Callback to be informed about the result.
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index 3810192..6f005a3 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -49,11 +49,21 @@
 
     public static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE.
 
-    // Value of batterySaverGpsMode such that GPS isn't affected by battery saver mode.
+    /** Value of batterySaverGpsMode such that GPS isn't affected by battery saver mode. */
     public static final int GPS_MODE_NO_CHANGE = 0;
-    // Value of batterySaverGpsMode such that GPS is disabled when battery saver mode
-    // is enabled and the screen is off.
+
+    /**
+     * Value of batterySaverGpsMode such that GPS is disabled when battery saver mode
+     * is enabled and the screen is off.
+     */
     public static final int GPS_MODE_DISABLED_WHEN_SCREEN_OFF = 1;
+
+    /**
+     * Value of batterySaverGpsMode such that location should be disabled altogether
+     * when battery saver mode is enabled and the screen is off.
+     */
+    public static final int GPS_MODE_ALL_DISABLED_WHEN_SCREEN_OFF = 2;
+
     // Secure setting for GPS behavior when battery saver mode is on.
     public static final String SECURE_KEY_GPS_MODE = "batterySaverGpsMode";
 
@@ -329,7 +339,7 @@
         }
 
         mVibrationDisabled = parser.getBoolean(KEY_VIBRATION_DISABLED, true);
-        mAnimationDisabled = parser.getBoolean(KEY_ANIMATION_DISABLED, true);
+        mAnimationDisabled = parser.getBoolean(KEY_ANIMATION_DISABLED, false);
         mSoundTriggerDisabled = parser.getBoolean(KEY_SOUNDTRIGGER_DISABLED, true);
         mFullBackupDeferred = parser.getBoolean(KEY_FULLBACKUP_DEFERRED, true);
         mKeyValueBackupDeferred = parser.getBoolean(KEY_KEYVALUE_DEFERRED, true);
@@ -344,7 +354,7 @@
 
         // Get default value from Settings.Secure
         final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE,
-                GPS_MODE_NO_CHANGE);
+                GPS_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
         mGpsMode = parser.getInt(KEY_GPS_MODE, defaultGpsMode);
 
         // Non-device-specific parameters.
@@ -446,6 +456,12 @@
         }
     }
 
+    public int getGpsMode() {
+        synchronized (mLock) {
+            return mGpsMode;
+        }
+    }
+
     public ArrayMap<String, String> getFileValues(boolean interactive) {
         synchronized (mLock) {
             return interactive ? mFilesForInteractive : mFilesForNoninteractive;
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index a6bca0b..d4627c2 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -84,6 +84,23 @@
      */
     private boolean mPreviouslyEnabled;
 
+    @GuardedBy("mLock")
+    private boolean mIsInteractive;
+
+    /**
+     * Read-only list of plugins. No need for synchronization.
+     */
+    private final Plugin[] mPlugins;
+
+    /**
+     * Plugin interface. All methods are guaranteed to be called on the same (handler) thread.
+     */
+    public interface Plugin {
+        void onSystemReady(BatterySaverController caller);
+
+        void onBatterySaverChanged(BatterySaverController caller);
+    }
+
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -109,6 +126,12 @@
         mBatterySaverPolicy = policy;
         mBatterySaverPolicy.addListener(this);
         mFileUpdater = new FileUpdater(context);
+
+        // Initialize plugins.
+        final ArrayList<Plugin> plugins = new ArrayList<>();
+        plugins.add(new BatterySaverLocationPlugin(mContext));
+
+        mPlugins = plugins.toArray(new Plugin[plugins.size()]);
     }
 
     /**
@@ -121,7 +144,7 @@
     }
 
     /**
-     * Called by {@link PowerManagerService} on system ready..
+     * Called by {@link PowerManagerService} on system ready.
      */
     public void systemReady() {
         final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
@@ -130,6 +153,7 @@
 
         mFileUpdater.systemReady(LocalServices.getService(ActivityManagerInternal.class)
                 .isRuntimeRestarted());
+        mHandler.postSystemReady();
     }
 
     private PowerManager getPowerManager() {
@@ -154,6 +178,8 @@
         private static final int ARG_DONT_SEND_BROADCAST = 0;
         private static final int ARG_SEND_BROADCAST = 1;
 
+        private static final int MSG_SYSTEM_READY = 2;
+
         public MyHandler(Looper looper) {
             super(looper);
         }
@@ -163,12 +189,22 @@
                     ARG_SEND_BROADCAST : ARG_DONT_SEND_BROADCAST, 0).sendToTarget();
         }
 
+        public void postSystemReady() {
+            obtainMessage(MSG_SYSTEM_READY, 0, 0).sendToTarget();
+        }
+
         @Override
         public void dispatchMessage(Message msg) {
             switch (msg.what) {
                 case MSG_STATE_CHANGED:
                     handleBatterySaverStateChanged(msg.arg1 == ARG_SEND_BROADCAST);
                     break;
+
+                case MSG_SYSTEM_READY:
+                    for (Plugin p : mPlugins) {
+                        p.onSystemReady(BatterySaverController.this);
+                    }
+                    break;
             }
         }
     }
@@ -188,12 +224,24 @@
     }
 
     /** @return whether battery saver is enabled or not. */
-    boolean isEnabled() {
+    public boolean isEnabled() {
         synchronized (mLock) {
             return mEnabled;
         }
     }
 
+    /** @return whether device is in interactive state. */
+    public boolean isInteractive() {
+        synchronized (mLock) {
+            return mIsInteractive;
+        }
+    }
+
+    /** @return Battery saver policy. */
+    public BatterySaverPolicy getBatterySaverPolicy() {
+        return mBatterySaverPolicy;
+    }
+
     /**
      * @return true if launch boost should currently be disabled.
      */
@@ -230,6 +278,7 @@
             listeners = mListeners.toArray(new LowPowerModeListener[mListeners.size()]);
 
             enabled = mEnabled;
+            mIsInteractive = isInteractive;
 
 
             if (enabled) {
@@ -250,6 +299,10 @@
             mFileUpdater.writeFiles(fileValues);
         }
 
+        for (Plugin p : mPlugins) {
+            p.onBatterySaverChanged(this);
+        }
+
         if (sendBroadcast) {
             if (enabled) {
                 // STOPSHIP Remove the toast.
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java
new file mode 100644
index 0000000..0af19b6
--- /dev/null
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.batterysaver;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.util.Slog;
+
+import com.android.server.power.BatterySaverPolicy;
+import com.android.server.power.batterysaver.BatterySaverController.Plugin;
+
+public class BatterySaverLocationPlugin implements Plugin {
+    private static final String TAG = "BatterySaverLocationPlugin";
+
+    private static final boolean DEBUG = BatterySaverController.DEBUG;
+
+    private final Context mContext;
+
+    public BatterySaverLocationPlugin(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public void onBatterySaverChanged(BatterySaverController caller) {
+        if (DEBUG) {
+            Slog.d(TAG, "onBatterySaverChanged");
+        }
+        updateLocationState(caller);
+    }
+
+    @Override
+    public void onSystemReady(BatterySaverController caller) {
+        if (DEBUG) {
+            Slog.d(TAG, "onSystemReady");
+        }
+        updateLocationState(caller);
+    }
+
+    private void updateLocationState(BatterySaverController caller) {
+        final boolean kill =
+                (caller.getBatterySaverPolicy().getGpsMode()
+                        == BatterySaverPolicy.GPS_MODE_ALL_DISABLED_WHEN_SCREEN_OFF) &&
+                caller.isEnabled() && !caller.isInteractive();
+
+        if (DEBUG) {
+            Slog.d(TAG, "Battery saver " + (kill ? "stopping" : "restoring") + " location.");
+        }
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Global.LOCATION_GLOBAL_KILL_SWITCH, kill ? 1 : 0);
+    }
+}
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
new file mode 100644
index 0000000..047e270
--- /dev/null
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.slice;
+
+import android.app.slice.ISliceManager;
+import android.content.Context;
+
+import com.android.server.SystemService;
+
+public class SliceManagerService extends ISliceManager.Stub {
+
+    public SliceManagerService(Context context) {
+
+    }
+
+    private void systemReady() {
+    }
+
+    private void onUnlockUser(int userHandle) {
+    }
+
+    public static class Lifecycle extends SystemService {
+        private SliceManagerService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            mService = new SliceManagerService(getContext());
+            publishBinderService(Context.SLICE_SERVICE, mService);
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+                mService.systemReady();
+            }
+        }
+
+        @Override
+        public void onUnlockUser(int userHandle) {
+            mService.onUnlockUser(userHandle);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 28f93e1..8d8c26c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -65,6 +65,8 @@
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
+
+import static com.android.internal.util.LatencyTracker.ACTION_ROTATE_SCREEN;
 import static com.android.server.LockGuard.INDEX_WINDOW;
 import static com.android.server.LockGuard.installLock;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -772,6 +774,8 @@
 
     private WindowContentFrameStats mTempWindowRenderStats;
 
+    private final LatencyTracker mLatencyTracker;
+
     /**
      * Whether the UI is currently running in touch mode (not showing
      * navigational focus because the user is directly pressing the screen).
@@ -1071,6 +1075,8 @@
         filter.addAction(Intent.ACTION_USER_REMOVED);
         mContext.registerReceiver(mBroadcastReceiver, filter);
 
+        mLatencyTracker = LatencyTracker.getInstance(context);
+
         mSettingsObserver = new SettingsObserver();
 
         mHoldingScreenWakeLock = mPowerManager.newWakeLock(
@@ -3047,11 +3053,6 @@
     }
 
     @Override
-    public boolean inKeyguardRestrictedInputMode() {
-        return mPolicy.inKeyguardRestrictedKeyInputMode();
-    }
-
-    @Override
     public boolean isKeyguardLocked() {
         return mPolicy.isKeyguardLocked();
     }
@@ -5864,7 +5865,7 @@
             Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
         }
 
-        LatencyTracker.getInstance(mContext).onActionStart(LatencyTracker.ACTION_ROTATE_SCREEN);
+        mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
         // TODO(multidisplay): rotation on non-default displays
         if (CUSTOM_SCREEN_ROTATION && displayContent.isDefaultDisplay) {
             mExitAnimId = exitAnim;
@@ -5989,7 +5990,7 @@
         if (configChanged) {
             mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayId).sendToTarget();
         }
-        LatencyTracker.getInstance(mContext).onActionEnd(LatencyTracker.ACTION_ROTATE_SCREEN);
+        mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN);
     }
 
     static int getPropertyInt(String[] tokens, int index, int defUnits, int defDps,
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
new file mode 100644
index 0000000..5d76304
--- /dev/null
+++ b/services/core/jni/Android.bp
@@ -0,0 +1,121 @@
+cc_library_static {
+    name: "libservices.core",
+    defaults: ["libservices.core-libs"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+
+        "-DEGL_EGLEXT_PROTOTYPES",
+        "-DGL_GLEXT_PROTOTYPES",
+    ],
+
+    srcs: [
+        "BroadcastRadio/JavaRef.cpp",
+        "BroadcastRadio/NativeCallbackThread.cpp",
+        "BroadcastRadio/BroadcastRadioService.cpp",
+        "BroadcastRadio/Tuner.cpp",
+        "BroadcastRadio/TunerCallback.cpp",
+        "BroadcastRadio/convert.cpp",
+        "BroadcastRadio/regions.cpp",
+        "com_android_server_AlarmManagerService.cpp",
+        "com_android_server_am_BatteryStatsService.cpp",
+        "com_android_server_connectivity_Vpn.cpp",
+        "com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp",
+        "com_android_server_ConsumerIrService.cpp",
+        "com_android_server_HardwarePropertiesManagerService.cpp",
+        "com_android_server_hdmi_HdmiCecController.cpp",
+        "com_android_server_input_InputApplicationHandle.cpp",
+        "com_android_server_input_InputManagerService.cpp",
+        "com_android_server_input_InputWindowHandle.cpp",
+        "com_android_server_lights_LightsService.cpp",
+        "com_android_server_location_GnssLocationProvider.cpp",
+        "com_android_server_locksettings_SyntheticPasswordManager.cpp",
+        "com_android_server_power_PowerManagerService.cpp",
+        "com_android_server_SerialService.cpp",
+        "com_android_server_storage_AppFuseBridge.cpp",
+        "com_android_server_SystemServer.cpp",
+        "com_android_server_tv_TvUinputBridge.cpp",
+        "com_android_server_tv_TvInputHal.cpp",
+        "com_android_server_vr_VrManagerService.cpp",
+        "com_android_server_UsbDeviceManager.cpp",
+        "com_android_server_UsbDescriptorParser.cpp",
+        "com_android_server_UsbMidiDevice.cpp",
+        "com_android_server_UsbHostManager.cpp",
+        "com_android_server_VibratorService.cpp",
+        "com_android_server_PersistentDataBlockService.cpp",
+        "com_android_server_GraphicsStatsService.cpp",
+        "onload.cpp",
+    ],
+
+    include_dirs: [
+        "frameworks/base/libs",
+        "frameworks/native/services",
+        "system/gatekeeper/include",
+    ],
+}
+
+cc_defaults {
+    name: "libservices.core-libs",
+    shared_libs: [
+        "libandroid_runtime",
+        "libandroidfw",
+        "libaudioclient",
+        "libbase",
+        "libappfuse",
+        "libbinder",
+        "libcutils",
+        "libcrypto",
+        "liblog",
+        "libhardware",
+        "libhardware_legacy",
+        "libhidlbase",
+        "libkeystore_binder",
+        "libnativehelper",
+        "libutils",
+        "libui",
+        "libinput",
+        "libinputflinger",
+        "libinputservice",
+        "libschedulerservicehidl",
+        "libsensorservice",
+        "libsensorservicehidl",
+        "libgui",
+        "libusbhost",
+        "libsuspend",
+        "libEGL",
+        "libGLESv2",
+        "libnetutils",
+        "libhidlbase",
+        "libhidltransport",
+        "libhwbinder",
+        "libutils",
+        "libhwui",
+        "android.hardware.audio.common@2.0",
+        "android.hardware.broadcastradio@1.0",
+        "android.hardware.broadcastradio@1.1",
+        "android.hardware.broadcastradio@1.2",
+        "android.hardware.contexthub@1.0",
+        "android.hardware.gnss@1.0",
+        "android.hardware.gnss@1.1",
+        "android.hardware.ir@1.0",
+        "android.hardware.light@2.0",
+        "android.hardware.power@1.0",
+        "android.hardware.power@1.1",
+        "android.hardware.tetheroffload.config@1.0",
+        "android.hardware.thermal@1.0",
+        "android.hardware.tv.cec@1.0",
+        "android.hardware.tv.input@1.0",
+        "android.hardware.vibrator@1.0",
+        "android.hardware.vibrator@1.1",
+        "android.hardware.vr@1.0",
+        "android.frameworks.schedulerservice@1.0",
+        "android.frameworks.sensorservice@1.0",
+    ],
+
+    static_libs: [
+        "android.hardware.broadcastradio@common-utils-lib",
+        "libscrypt_static",
+    ],
+}
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
deleted file mode 100644
index 8b9cf4b..0000000
--- a/services/core/jni/Android.mk
+++ /dev/null
@@ -1,114 +0,0 @@
-# This file is included by the top level services directory to collect source
-# files
-LOCAL_REL_DIR := core/jni
-
-LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter
-
-LOCAL_SRC_FILES += \
-    $(LOCAL_REL_DIR)/BroadcastRadio/JavaRef.cpp \
-    $(LOCAL_REL_DIR)/BroadcastRadio/NativeCallbackThread.cpp \
-    $(LOCAL_REL_DIR)/BroadcastRadio/BroadcastRadioService.cpp \
-    $(LOCAL_REL_DIR)/BroadcastRadio/Tuner.cpp \
-    $(LOCAL_REL_DIR)/BroadcastRadio/TunerCallback.cpp \
-    $(LOCAL_REL_DIR)/BroadcastRadio/convert.cpp \
-    $(LOCAL_REL_DIR)/BroadcastRadio/regions.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_AlarmManagerService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_am_BatteryStatsService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_HardwarePropertiesManagerService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecController.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_input_InputApplicationHandle.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_input_InputWindowHandle.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_lights_LightsService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_location_GnssLocationProvider.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_locksettings_SyntheticPasswordManager.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_SerialService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_storage_AppFuseBridge.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_SystemServer.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_tv_TvUinputBridge.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_tv_TvInputHal.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_vr_VrManagerService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_UsbDescriptorParser.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_UsbMidiDevice.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_PersistentDataBlockService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_GraphicsStatsService.cpp \
-    $(LOCAL_REL_DIR)/onload.cpp
-
-LOCAL_C_INCLUDES += \
-    $(JNI_H_INCLUDE) \
-    external/scrypt/lib/crypto \
-    frameworks/base/services \
-    frameworks/base/libs \
-    frameworks/base/core/jni \
-    frameworks/native/services \
-    system/core/libappfuse/include \
-    system/gatekeeper/include \
-    system/security/keystore/include \
-    $(call include-path-for, libhardware)/hardware \
-    $(call include-path-for, libhardware_legacy)/hardware_legacy \
-
-LOCAL_SHARED_LIBRARIES += \
-    libandroid_runtime \
-    libandroidfw \
-    libaudioclient \
-    libbase \
-    libappfuse \
-    libbinder \
-    libcutils \
-    libcrypto \
-    liblog \
-    libhardware \
-    libhardware_legacy \
-    libhidlbase \
-    libkeystore_binder \
-    libnativehelper \
-    libutils \
-    libui \
-    libinput \
-    libinputflinger \
-    libinputservice \
-    libschedulerservicehidl \
-    libsensorservice \
-    libsensorservicehidl \
-    libgui \
-    libusbhost \
-    libsuspend \
-    libEGL \
-    libGLESv2 \
-    libnetutils \
-    libhidlbase \
-    libhidltransport \
-    libhwbinder \
-    libutils \
-    libhwui \
-    android.hardware.audio.common@2.0 \
-    android.hardware.broadcastradio@1.0 \
-    android.hardware.broadcastradio@1.1 \
-    android.hardware.broadcastradio@1.2 \
-    android.hardware.contexthub@1.0 \
-    android.hardware.gnss@1.0 \
-    android.hardware.gnss@1.1 \
-    android.hardware.ir@1.0 \
-    android.hardware.light@2.0 \
-    android.hardware.power@1.0 \
-    android.hardware.power@1.1 \
-    android.hardware.tetheroffload.config@1.0 \
-    android.hardware.thermal@1.0 \
-    android.hardware.tv.cec@1.0 \
-    android.hardware.tv.input@1.0 \
-    android.hardware.vibrator@1.0 \
-    android.hardware.vibrator@1.1 \
-    android.hardware.vr@1.0 \
-    android.frameworks.schedulerservice@1.0 \
-    android.frameworks.sensorservice@1.0 \
-
-LOCAL_STATIC_LIBRARIES += \
-    android.hardware.broadcastradio@common-utils-lib \
-    libscrypt_static \
diff --git a/services/core/jni/com_android_server_input_InputApplicationHandle.cpp b/services/core/jni/com_android_server_input_InputApplicationHandle.cpp
index 232b2c2..514b6e1 100644
--- a/services/core/jni/com_android_server_input_InputApplicationHandle.cpp
+++ b/services/core/jni/com_android_server_input_InputApplicationHandle.cpp
@@ -65,11 +65,11 @@
             gInputApplicationHandleClassInfo.name));
     if (nameObj) {
         const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
-        mInfo->name.setTo(nameStr);
+        mInfo->name = nameStr;
         env->ReleaseStringUTFChars(nameObj, nameStr);
         env->DeleteLocalRef(nameObj);
     } else {
-        mInfo->name.setTo("<null>");
+        mInfo->name = "<null>";
     }
 
     mInfo->dispatchingTimeout = env->GetLongField(obj,
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 21300ed..27c2fac 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -32,6 +32,7 @@
 #include <atomic>
 #include <cinttypes>
 #include <limits.h>
+#include <android-base/stringprintf.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
 
@@ -65,6 +66,8 @@
 
 #define INDENT "  "
 
+using android::base::StringPrintf;
+
 namespace android {
 
 // The exponent used to calculate the pointer speed scaling factor.
@@ -198,7 +201,7 @@
 
     inline sp<InputManager> getInputManager() const { return mInputManager; }
 
-    void dump(String8& dump);
+    void dump(std::string& dump);
 
     void setVirtualDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
     void setDisplayViewport(int32_t viewportType, const DisplayViewport& viewport);
@@ -240,7 +243,7 @@
     virtual void notifyConfigurationChanged(nsecs_t when);
     virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
             const sp<InputWindowHandle>& inputWindowHandle,
-            const String8& reason);
+            const std::string& reason);
     virtual void notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle);
     virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags);
     virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig);
@@ -347,28 +350,28 @@
     env->DeleteGlobalRef(mServiceObj);
 }
 
-void NativeInputManager::dump(String8& dump) {
-    dump.append("Input Manager State:\n");
+void NativeInputManager::dump(std::string& dump) {
+    dump += "Input Manager State:\n";
     {
-        dump.appendFormat(INDENT "Interactive: %s\n", toString(mInteractive.load()));
+        dump += StringPrintf(INDENT "Interactive: %s\n", toString(mInteractive.load()));
     }
     {
         AutoMutex _l(mLock);
-        dump.appendFormat(INDENT "System UI Visibility: 0x%0" PRIx32 "\n",
+        dump += StringPrintf(INDENT "System UI Visibility: 0x%0" PRIx32 "\n",
                 mLocked.systemUiVisibility);
-        dump.appendFormat(INDENT "Pointer Speed: %" PRId32 "\n", mLocked.pointerSpeed);
-        dump.appendFormat(INDENT "Pointer Gestures Enabled: %s\n",
+        dump += StringPrintf(INDENT "Pointer Speed: %" PRId32 "\n", mLocked.pointerSpeed);
+        dump += StringPrintf(INDENT "Pointer Gestures Enabled: %s\n",
                 toString(mLocked.pointerGesturesEnabled));
-        dump.appendFormat(INDENT "Show Touches: %s\n", toString(mLocked.showTouches));
-        dump.appendFormat(INDENT "Pointer Capture Enabled: %s\n", toString(mLocked.pointerCapture));
+        dump += StringPrintf(INDENT "Show Touches: %s\n", toString(mLocked.showTouches));
+        dump += StringPrintf(INDENT "Pointer Capture Enabled: %s\n", toString(mLocked.pointerCapture));
     }
-    dump.append("\n");
+    dump += "\n";
 
     mInputManager->getReader()->dump(dump);
-    dump.append("\n");
+    dump += "\n";
 
     mInputManager->getDispatcher()->dump(dump);
-    dump.append("\n");
+    dump += "\n";
 }
 
 bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
@@ -668,7 +671,7 @@
 }
 
 nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
-        const sp<InputWindowHandle>& inputWindowHandle, const String8& reason) {
+        const sp<InputWindowHandle>& inputWindowHandle, const std::string& reason) {
 #if DEBUG_INPUT_DISPATCHER_POLICY
     ALOGD("notifyANR");
 #endif
@@ -680,7 +683,7 @@
             getInputApplicationHandleObjLocalRef(env, inputApplicationHandle);
     jobject inputWindowHandleObj =
             getInputWindowHandleObjLocalRef(env, inputWindowHandle);
-    jstring reasonObj = env->NewStringUTF(reason.string());
+    jstring reasonObj = env->NewStringUTF(reason.c_str());
 
     jlong newTimeout = env->CallLongMethod(mServiceObj,
                 gServiceClassInfo.notifyANR, inputApplicationHandleObj, inputWindowHandleObj,
@@ -1342,7 +1345,7 @@
     NativeInputManager* im = static_cast<NativeInputManager*>(data);
 
     ALOGW("Input channel object '%s' was disposed without first being unregistered with "
-            "the input manager!", inputChannel->getName().string());
+            "the input manager!", inputChannel->getName().c_str());
     im->unregisterInputChannel(env, inputChannel);
 }
 
@@ -1363,9 +1366,9 @@
     status_t status = im->registerInputChannel(
             env, inputChannel, inputWindowHandle, monitor);
     if (status) {
-        String8 message;
-        message.appendFormat("Failed to register input channel.  status=%d", status);
-        jniThrowRuntimeException(env, message.string());
+        std::string message;
+        message += StringPrintf("Failed to register input channel.  status=%d", status);
+        jniThrowRuntimeException(env, message.c_str());
         return;
     }
 
@@ -1390,9 +1393,9 @@
 
     status_t status = im->unregisterInputChannel(env, inputChannel);
     if (status && status != BAD_VALUE) { // ignore already unregistered channel
-        String8 message;
-        message.appendFormat("Failed to unregister input channel.  status=%d", status);
-        jniThrowRuntimeException(env, message.string());
+        std::string message;
+        message += StringPrintf("Failed to unregister input channel.  status=%d", status);
+        jniThrowRuntimeException(env, message.c_str());
     }
 }
 
@@ -1576,9 +1579,9 @@
 static jstring nativeDump(JNIEnv* env, jclass /* clazz */, jlong ptr) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
-    String8 dump;
+    std::string dump;
     im->dump(dump);
-    return env->NewStringUTF(dump.string());
+    return env->NewStringUTF(dump.c_str());
 }
 
 static void nativeMonitor(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
diff --git a/services/core/jni/com_android_server_input_InputWindowHandle.cpp b/services/core/jni/com_android_server_input_InputWindowHandle.cpp
index 2b2a6fa..c13aa38 100644
--- a/services/core/jni/com_android_server_input_InputWindowHandle.cpp
+++ b/services/core/jni/com_android_server_input_InputWindowHandle.cpp
@@ -103,11 +103,11 @@
             gInputWindowHandleClassInfo.name));
     if (nameObj) {
         const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
-        mInfo->name.setTo(nameStr);
+        mInfo->name = nameStr;
         env->ReleaseStringUTFChars(nameObj, nameStr);
         env->DeleteLocalRef(nameObj);
     } else {
-        mInfo->name.setTo("<null>");
+        mInfo->name = "<null>";
     }
 
     mInfo->layoutParamsFlags = env->GetIntField(obj,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ddc0c23..b979ff4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.BIND_DEVICE_ADMIN;
 import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManager.USER_OP_SUCCESS;
 import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
 import static android.app.admin.DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED;
 import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE;
@@ -150,6 +151,9 @@
 import android.security.IKeyChainService;
 import android.security.KeyChain;
 import android.security.KeyChain.KeyChainConnection;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
+import android.security.KeyStore;
 import android.service.persistentdata.PersistentDataBlockManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -5021,6 +5025,54 @@
     }
 
     @Override
+    public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm,
+            ParcelableKeyGenParameterSpec parcelableKeySpec) {
+        enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
+                DELEGATION_CERT_INSTALL);
+        final KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec();
+        if (TextUtils.isEmpty(keySpec.getKeystoreAlias())) {
+            throw new IllegalArgumentException("Empty alias provided.");
+        }
+        // As the caller will be granted access to the key, ensure no UID was specified, as
+        // it will not have the desired effect.
+        if (keySpec.getUid() != KeyStore.UID_SELF) {
+            Log.e(LOG_TAG, "Only the caller can be granted access to the generated keypair.");
+            return false;
+        }
+        final int callingUid = mInjector.binderGetCallingUid();
+
+        final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            try (KeyChainConnection keyChainConnection =
+                    KeyChain.bindAsUser(mContext, userHandle)) {
+                IKeyChainService keyChain = keyChainConnection.getService();
+                final boolean generationResult = keyChain.generateKeyPair(algorithm, parcelableKeySpec);
+                if (!generationResult) {
+                    Log.e(LOG_TAG, "KeyChain failed to generate a keypair.");
+                    return false;
+                }
+
+                // Set a grant for the caller here so that when the client calls
+                // requestPrivateKey, it will be able to get the key from Keystore.
+                // Note the use of the calling  UID, since the request for the private
+                // key will come from the client's process, so the grant has to be for
+                // that UID.
+                keyChain.setGrant(callingUid, keySpec.getKeystoreAlias(), true);
+                return true;
+            }
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "KeyChain error while generating a keypair", e);
+        } catch (InterruptedException e) {
+            Log.w(LOG_TAG, "Interrupted while generating keypair", e);
+            Thread.currentThread().interrupt();
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
+        }
+        return false;
+    }
+
+    @Override
     public void choosePrivateKeyAlias(final int uid, final Uri uri, final String alias,
             final IBinder response) {
         // Caller UID needs to be trusted, so we restrict this method to SYSTEM_UID callers.
@@ -8403,6 +8455,90 @@
     }
 
     @Override
+    public boolean stopUser(ComponentName who, UserHandle userHandle) {
+        Preconditions.checkNotNull(who, "ComponentName is null");
+
+        synchronized (this) {
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+        }
+
+        final int userId = userHandle.getIdentifier();
+        if (isManagedProfile(userId)) {
+            Log.w(LOG_TAG, "Managed profile cannot be stopped");
+            return false;
+        }
+
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            return mInjector.getIActivityManager().stopUser(userId, true /*force*/, null)
+                    == USER_OP_SUCCESS;
+        } catch (RemoteException e) {
+            // Same process, should not happen.
+            return false;
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
+        }
+    }
+
+    @Override
+    public boolean logoutUser(ComponentName who) {
+        Preconditions.checkNotNull(who, "ComponentName is null");
+
+        final int callingUserId = mInjector.userHandleGetCallingUserId();
+        synchronized (this) {
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            if (!isUserAffiliatedWithDeviceLocked(callingUserId)) {
+                throw new SecurityException("Admin " + who +
+                        " is neither the device owner or affiliated user's profile owner.");
+            }
+        }
+
+        if (isManagedProfile(callingUserId)) {
+            Log.w(LOG_TAG, "Managed profile cannot be logout");
+            return false;
+        }
+
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            if (!mInjector.getIActivityManager().switchUser(UserHandle.USER_SYSTEM)) {
+                Log.w(LOG_TAG, "Failed to switch to primary user");
+                return false;
+            }
+            return mInjector.getIActivityManager().stopUser(callingUserId, true /*force*/, null)
+                    == USER_OP_SUCCESS;
+        } catch (RemoteException e) {
+            // Same process, should not happen.
+            return false;
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
+        }
+    }
+
+    @Override
+    public List<UserHandle> getSecondaryUsers(ComponentName who) {
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        synchronized (this) {
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+        }
+
+        final long id = mInjector.binderClearCallingIdentity();
+        try {
+            final List<UserInfo> userInfos = mInjector.getUserManager().getUsers(true
+                    /*excludeDying*/);
+            final List<UserHandle> userHandles = new ArrayList<>();
+            for (UserInfo userInfo : userInfos) {
+                UserHandle userHandle = userInfo.getUserHandle();
+                if (!userHandle.isSystem() && !isManagedProfile(userHandle.getIdentifier())) {
+                    userHandles.add(userInfo.getUserHandle());
+                }
+            }
+            return userHandles;
+        } finally {
+            mInjector.binderRestoreCallingIdentity(id);
+        }
+    }
+
+    @Override
     public Bundle getApplicationRestrictions(ComponentName who, String callerPackage,
             String packageName) {
         enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 33f4e34..4c994b9 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -215,6 +215,9 @@
             "com.android.server.timezone.RulesManagerService$Lifecycle";
     private static final String IOT_SERVICE_CLASS =
             "com.google.android.things.services.IoTSystemService";
+    private static final String SLICE_MANAGER_SERVICE_CLASS =
+            "com.android.server.slice.SliceManagerService$Lifecycle";
+
     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
 
     private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file";
@@ -730,6 +733,7 @@
         boolean disableVrManager = SystemProperties.getBoolean("config.disable_vrmanager", false);
         boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
                 false);
+        boolean disableSlices = SystemProperties.getBoolean("config.disable_slices", false);
         boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false);
 
         boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
@@ -1523,6 +1527,12 @@
             }
         }
 
+        if (!disableSlices) {
+            traceBeginAndSlog("StartSliceManagerService");
+            mSystemServiceManager.startService(SLICE_MANAGER_SERVICE_CLASS);
+            traceEnd();
+        }
+
         if (!disableCameraService) {
             traceBeginAndSlog("StartCameraServiceProxy");
             mSystemServiceManager.startService(CameraServiceProxy.class);
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index e8ae020..364bbc0 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -25,6 +25,7 @@
 import static com.android.internal.print.DumpUtils.writePrinterId;
 import static com.android.internal.print.DumpUtils.writePrinterInfo;
 import static com.android.internal.print.DumpUtils.writeStringIfNotNull;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -81,7 +82,6 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.BackgroundThread;
-import com.android.internal.os.SomeArgs;
 import com.android.server.print.RemotePrintService.PrintServiceCallbacks;
 import com.android.server.print.RemotePrintServiceRecommendationService
         .RemotePrintServiceRecommendationServiceCallbacks;
@@ -462,7 +462,7 @@
 
             if (mPrinterDiscoverySession == null) {
                 // If we do not have a session, tell all service to create one.
-                mPrinterDiscoverySession = new PrinterDiscoverySessionMediator(mContext) {
+                mPrinterDiscoverySession = new PrinterDiscoverySessionMediator() {
                     @Override
                     public void onDestroyed() {
                         mPrinterDiscoverySession = null;
@@ -1141,12 +1141,8 @@
         // just died. Do this off the main thread since we do to allow
         // calls into the spooler on the main thread.
         if (Looper.getMainLooper().isCurrentThread()) {
-            BackgroundThread.getHandler().post(new Runnable() {
-                @Override
-                public void run() {
-                    failScheduledPrintJobsForServiceInternal(serviceName);
-                }
-            });
+            BackgroundThread.getHandler().sendMessage(obtainMessage(
+                    UserState::failScheduledPrintJobsForServiceInternal, this, serviceName));
         } else {
             failScheduledPrintJobsForServiceInternal(serviceName);
         }
@@ -1341,18 +1337,13 @@
 
         private final List<PrinterId> mStateTrackedPrinters = new ArrayList<PrinterId>();
 
-        private final Handler mSessionHandler;
-
         private boolean mIsDestroyed;
 
-        public PrinterDiscoverySessionMediator(Context context) {
-            mSessionHandler = new SessionHandler(context.getMainLooper());
+        PrinterDiscoverySessionMediator() {
             // Kick off the session creation.
-            List<RemotePrintService> services = new ArrayList<RemotePrintService>(
-                    mActiveServices.values());
-            mSessionHandler.obtainMessage(SessionHandler
-                    .MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION, services)
-                    .sendToTarget();
+            Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
+                    handleDispatchCreatePrinterDiscoverySession,
+                    this, new ArrayList<>(mActiveServices.values())));
         }
 
         public void addObserverLocked(@NonNull IPrinterDiscoveryObserver observer) {
@@ -1361,12 +1352,9 @@
 
             // Bring the added observer up to speed with the printers.
             if (!mPrinters.isEmpty()) {
-                List<PrinterInfo> printers = new ArrayList<PrinterInfo>(mPrinters.values());
-                SomeArgs args = SomeArgs.obtain();
-                args.arg1 = observer;
-                args.arg2 = printers;
-                mSessionHandler.obtainMessage(SessionHandler.MSG_PRINTERS_ADDED,
-                        args).sendToTarget();
+                Handler.getMain().sendMessage(obtainMessage(
+                        UserState.PrinterDiscoverySessionMediator::handlePrintersAdded,
+                        this, observer, new ArrayList<>(mPrinters.values())));
             }
         }
 
@@ -1403,14 +1391,9 @@
                 return;
             }
 
-            List<RemotePrintService> services = new ArrayList<RemotePrintService>(
-                    mActiveServices.values());
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = services;
-            args.arg2 = priorityList;
-            mSessionHandler.obtainMessage(SessionHandler
-                    .MSG_DISPATCH_START_PRINTER_DISCOVERY, args)
-                    .sendToTarget();
+            Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
+                    handleDispatchStartPrinterDiscovery, this,
+                    new ArrayList<>(mActiveServices.values()), priorityList));
         }
 
         public final void stopPrinterDiscoveryLocked(@NonNull IPrinterDiscoveryObserver observer) {
@@ -1426,11 +1409,9 @@
             if (!mStartedPrinterDiscoveryTokens.isEmpty()) {
                 return;
             }
-            List<RemotePrintService> services = new ArrayList<RemotePrintService>(
-                    mActiveServices.values());
-            mSessionHandler.obtainMessage(SessionHandler
-                    .MSG_DISPATCH_STOP_PRINTER_DISCOVERY, services)
-                    .sendToTarget();
+            Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
+                    handleDispatchStopPrinterDiscovery,
+                    this, new ArrayList<>(mActiveServices.values())));
         }
 
         public void validatePrintersLocked(@NonNull List<PrinterId> printerIds) {
@@ -1461,12 +1442,9 @@
                 // Schedule a notification of the service.
                 RemotePrintService service = mActiveServices.get(serviceName);
                 if (service != null) {
-                    SomeArgs args = SomeArgs.obtain();
-                    args.arg1 = service;
-                    args.arg2 = updateList;
-                    mSessionHandler.obtainMessage(SessionHandler
-                            .MSG_VALIDATE_PRINTERS, args)
-                            .sendToTarget();
+                    Handler.getMain().sendMessage(obtainMessage(
+                            UserState.PrinterDiscoverySessionMediator::handleValidatePrinters,
+                            this, service, updateList));
                 }
             }
         }
@@ -1493,12 +1471,8 @@
                 return;
             }
             // Ask the service to start tracking.
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = service;
-            args.arg2 = printerId;
-            mSessionHandler.obtainMessage(SessionHandler
-                    .MSG_START_PRINTER_STATE_TRACKING, args)
-                    .sendToTarget();
+            Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
+                    handleStartPrinterStateTracking, this, service, printerId));
         }
 
         public final void stopPrinterStateTrackingLocked(PrinterId printerId) {
@@ -1520,12 +1494,8 @@
                 return;
             }
             // Ask the service to start tracking.
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = service;
-            args.arg2 = printerId;
-            mSessionHandler.obtainMessage(SessionHandler
-                    .MSG_STOP_PRINTER_STATE_TRACKING, args)
-                    .sendToTarget();
+            Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
+                    handleStopPrinterStateTracking, this, service, printerId));
         }
 
         public void onDestroyed() {
@@ -1551,11 +1521,9 @@
                 stopPrinterDiscoveryLocked(IPrinterDiscoveryObserver.Stub.asInterface(token));
             }
             // Tell the services we are done.
-            List<RemotePrintService> services = new ArrayList<RemotePrintService>(
-                    mActiveServices.values());
-            mSessionHandler.obtainMessage(SessionHandler
-                    .MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION, services)
-                    .sendToTarget();
+            Handler.getMain().sendMessage(obtainMessage(UserState.PrinterDiscoverySessionMediator::
+                    handleDispatchDestroyPrinterDiscoverySession,
+                    this, new ArrayList<>(mActiveServices.values())));
         }
 
         public void onPrintersAddedLocked(List<PrinterInfo> printers) {
@@ -1579,8 +1547,9 @@
                 }
             }
             if (addedPrinters != null) {
-                mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED,
-                        addedPrinters).sendToTarget();
+                Handler.getMain().sendMessage(obtainMessage(
+                        UserState.PrinterDiscoverySessionMediator::handleDispatchPrintersAdded,
+                        this, addedPrinters));
             }
         }
 
@@ -1604,8 +1573,9 @@
                 }
             }
             if (removedPrinterIds != null) {
-                mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED,
-                        removedPrinterIds).sendToTarget();
+                Handler.getMain().sendMessage(obtainMessage(
+                        UserState.PrinterDiscoverySessionMediator::handleDispatchPrintersRemoved,
+                        this, removedPrinterIds));
             }
         }
 
@@ -1646,8 +1616,9 @@
 
                 ArrayList<PrinterInfo> addedPrinters = new ArrayList<>(1);
                 addedPrinters.add(newPrinter);
-                mSessionHandler.obtainMessage(SessionHandler.MSG_DISPATCH_PRINTERS_ADDED,
-                        addedPrinters).sendToTarget();
+                Handler.getMain().sendMessage(obtainMessage(
+                        UserState.PrinterDiscoverySessionMediator::handleDispatchPrintersAdded,
+                        this, addedPrinters));
             }
         }
 
@@ -1661,26 +1632,20 @@
                 return;
             }
             // Tell the service to create a session.
-            mSessionHandler.obtainMessage(
-                    SessionHandler.MSG_CREATE_PRINTER_DISCOVERY_SESSION,
-                    service).sendToTarget();
+            Handler.getMain().sendMessage(obtainMessage(
+                    RemotePrintService::createPrinterDiscoverySession, service));
             // Start printer discovery if necessary.
             if (!mStartedPrinterDiscoveryTokens.isEmpty()) {
-                mSessionHandler.obtainMessage(
-                        SessionHandler.MSG_START_PRINTER_DISCOVERY,
-                        service).sendToTarget();
+                Handler.getMain().sendMessage(obtainMessage(
+                        RemotePrintService::startPrinterDiscovery, service, null));
             }
             // Start tracking printers if necessary
             final int trackedPrinterCount = mStateTrackedPrinters.size();
             for (int i = 0; i < trackedPrinterCount; i++) {
                 PrinterId printerId = mStateTrackedPrinters.get(i);
                 if (printerId.getServiceName().equals(service.getComponentName())) {
-                    SomeArgs args = SomeArgs.obtain();
-                    args.arg1 = service;
-                    args.arg2 = printerId;
-                    mSessionHandler.obtainMessage(SessionHandler
-                            .MSG_START_PRINTER_STATE_TRACKING, args)
-                            .sendToTarget();
+                    Handler.getMain().sendMessage(obtainMessage(
+                            RemotePrintService::startPrinterStateTracking, service, printerId));
                 }
             }
         }
@@ -1781,9 +1746,9 @@
                 for (int i = 0; i < removedPrinterCount; i++) {
                     mPrinters.remove(removedPrinterIds.get(i));
                 }
-                mSessionHandler.obtainMessage(
-                        SessionHandler.MSG_DISPATCH_PRINTERS_REMOVED,
-                        removedPrinterIds).sendToTarget();
+                Handler.getMain().sendMessage(obtainMessage(
+                        UserState.PrinterDiscoverySessionMediator::handleDispatchPrintersRemoved,
+                        this, removedPrinterIds));
             }
         }
 
@@ -1873,134 +1838,6 @@
                 Log.e(LOG_TAG, "Error sending removed printers", re);
             }
         }
-
-        private final class SessionHandler extends Handler {
-            public static final int MSG_PRINTERS_ADDED = 1;
-            public static final int MSG_PRINTERS_REMOVED = 2;
-            public static final int MSG_DISPATCH_PRINTERS_ADDED = 3;
-            public static final int MSG_DISPATCH_PRINTERS_REMOVED = 4;
-
-            public static final int MSG_CREATE_PRINTER_DISCOVERY_SESSION = 5;
-            public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 6;
-            public static final int MSG_START_PRINTER_DISCOVERY = 7;
-            public static final int MSG_STOP_PRINTER_DISCOVERY = 8;
-            public static final int MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION = 9;
-            public static final int MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION = 10;
-            public static final int MSG_DISPATCH_START_PRINTER_DISCOVERY = 11;
-            public static final int MSG_DISPATCH_STOP_PRINTER_DISCOVERY = 12;
-            public static final int MSG_VALIDATE_PRINTERS = 13;
-            public static final int MSG_START_PRINTER_STATE_TRACKING = 14;
-            public static final int MSG_STOP_PRINTER_STATE_TRACKING = 15;
-            public static final int MSG_DESTROY_SERVICE = 16;
-
-            SessionHandler(Looper looper) {
-                super(looper, null, false);
-            }
-
-            @Override
-            @SuppressWarnings("unchecked")
-            public void handleMessage(Message message) {
-                switch (message.what) {
-                    case MSG_PRINTERS_ADDED: {
-                        SomeArgs args = (SomeArgs) message.obj;
-                        IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1;
-                        List<PrinterInfo> addedPrinters = (List<PrinterInfo>) args.arg2;
-                        args.recycle();
-                        handlePrintersAdded(observer, addedPrinters);
-                    } break;
-
-                    case MSG_PRINTERS_REMOVED: {
-                        SomeArgs args = (SomeArgs) message.obj;
-                        IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg1;
-                        List<PrinterId> removedPrinterIds = (List<PrinterId>) args.arg2;
-                        args.recycle();
-                        handlePrintersRemoved(observer, removedPrinterIds);
-                    }
-
-                    case MSG_DISPATCH_PRINTERS_ADDED: {
-                        List<PrinterInfo> addedPrinters = (List<PrinterInfo>) message.obj;
-                        handleDispatchPrintersAdded(addedPrinters);
-                    } break;
-
-                    case MSG_DISPATCH_PRINTERS_REMOVED: {
-                        List<PrinterId> removedPrinterIds = (List<PrinterId>) message.obj;
-                        handleDispatchPrintersRemoved(removedPrinterIds);
-                    } break;
-
-                    case MSG_CREATE_PRINTER_DISCOVERY_SESSION: {
-                        RemotePrintService service = (RemotePrintService) message.obj;
-                        service.createPrinterDiscoverySession();
-                    } break;
-
-                    case MSG_DESTROY_PRINTER_DISCOVERY_SESSION: {
-                        RemotePrintService service = (RemotePrintService) message.obj;
-                        service.destroyPrinterDiscoverySession();
-                    } break;
-
-                    case MSG_START_PRINTER_DISCOVERY: {
-                        RemotePrintService service = (RemotePrintService) message.obj;
-                        service.startPrinterDiscovery(null);
-                    } break;
-
-                    case MSG_STOP_PRINTER_DISCOVERY: {
-                        RemotePrintService service = (RemotePrintService) message.obj;
-                        service.stopPrinterDiscovery();
-                    } break;
-
-                    case MSG_DISPATCH_CREATE_PRINTER_DISCOVERY_SESSION: {
-                        List<RemotePrintService> services = (List<RemotePrintService>) message.obj;
-                        handleDispatchCreatePrinterDiscoverySession(services);
-                    } break;
-
-                    case MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION: {
-                        List<RemotePrintService> services = (List<RemotePrintService>) message.obj;
-                        handleDispatchDestroyPrinterDiscoverySession(services);
-                    } break;
-
-                    case MSG_DISPATCH_START_PRINTER_DISCOVERY: {
-                        SomeArgs args = (SomeArgs) message.obj;
-                        List<RemotePrintService> services = (List<RemotePrintService>) args.arg1;
-                        List<PrinterId> printerIds = (List<PrinterId>) args.arg2;
-                        args.recycle();
-                        handleDispatchStartPrinterDiscovery(services, printerIds);
-                    } break;
-
-                    case MSG_DISPATCH_STOP_PRINTER_DISCOVERY: {
-                        List<RemotePrintService> services = (List<RemotePrintService>) message.obj;
-                        handleDispatchStopPrinterDiscovery(services);
-                    } break;
-
-                    case MSG_VALIDATE_PRINTERS: {
-                        SomeArgs args = (SomeArgs) message.obj;
-                        RemotePrintService service = (RemotePrintService) args.arg1;
-                        List<PrinterId> printerIds = (List<PrinterId>) args.arg2;
-                        args.recycle();
-                        handleValidatePrinters(service, printerIds);
-                    } break;
-
-                    case MSG_START_PRINTER_STATE_TRACKING: {
-                        SomeArgs args = (SomeArgs) message.obj;
-                        RemotePrintService service = (RemotePrintService) args.arg1;
-                        PrinterId printerId = (PrinterId) args.arg2;
-                        args.recycle();
-                        handleStartPrinterStateTracking(service, printerId);
-                    } break;
-
-                    case MSG_STOP_PRINTER_STATE_TRACKING: {
-                        SomeArgs args = (SomeArgs) message.obj;
-                        RemotePrintService service = (RemotePrintService) args.arg1;
-                        PrinterId printerId = (PrinterId) args.arg2;
-                        args.recycle();
-                        handleStopPrinterStateTracking(service, printerId);
-                    } break;
-
-                    case MSG_DESTROY_SERVICE: {
-                        RemotePrintService service = (RemotePrintService) message.obj;
-                        service.destroy();
-                    } break;
-                }
-            }
-        }
     }
 
     private final class PrintJobForAppCache {
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index e5ab44f..218a2b8 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -38,7 +38,7 @@
 LOCAL_JAVA_LIBRARIES := \
     android.hidl.manager-V1.0-java \
     android.test.mock \
-    legacy-android-test \
+    android.test.base android.test.runner \
 
 LOCAL_PACKAGE_NAME := FrameworksServicesTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
index 5676510..7160d7f 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartControllerTests.java
@@ -19,31 +19,16 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 
-import android.app.ActivityOptions;
-import android.app.IApplicationThread;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ResolveInfo;
-import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
-import android.service.voice.IVoiceInteractionSession;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import com.android.internal.app.IVoiceInteractor;
 import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
 import com.android.server.am.ActivityStarter.Factory;
 
 import org.junit.runner.RunWith;
 import org.junit.Test;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyObject;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
@@ -73,59 +58,10 @@
         super.setUp();
         mService = createActivityManagerService();
         mFactory = mock(Factory.class);
-        mStarter = mock(ActivityStarter.class);
-        doReturn(mStarter).when(mFactory).getStarter(any(), any(), any(), any());
         mController = new ActivityStartController(mService, mService.mStackSupervisor, mFactory);
-    }
-
-    /**
-     * Ensures that the starter is correctly invoked on
-     * {@link ActivityStartController#startActivity}
-     */
-    @Test
-    @Presubmit
-    public void testStartActivity() {
-        final Random random = new Random();
-
-        final IApplicationThread applicationThread = mock(IApplicationThread.class);
-        final Intent intent = mock(Intent.class);
-        final Intent ephemeralIntent = mock(Intent.class);
-        final String resolvedType = "TestType";
-        final ActivityInfo aInfo = mock(ActivityInfo.class);
-        final ResolveInfo rInfo = mock(ResolveInfo.class);
-        final IVoiceInteractionSession voiceInteractionSession =
-                mock(IVoiceInteractionSession.class);
-        final IVoiceInteractor voiceInteractor = mock(IVoiceInteractor.class);
-        final IBinder resultTo = mock(IBinder.class);
-        final String resultWho = "resultWho";
-        final int requestCode = random.nextInt();
-        final int callingPid = random.nextInt();
-        final int callingUid = random.nextInt();
-        final String callingPackage = "callingPackage";
-        final int realCallingPid = random.nextInt();
-        final int realCallingUid = random.nextInt();
-        final int startFlags = random.nextInt();
-        final ActivityOptions options = mock(ActivityOptions.class);
-        final boolean ignoreTargetSecurity = random.nextBoolean();
-        final boolean componentSpecified = random.nextBoolean();
-        final ActivityRecord[] outActivity = new ActivityRecord[1];
-        final TaskRecord inTask = mock(TaskRecord.class);
-        final String reason ="reason";
-
-        mController.startActivity(applicationThread, intent, ephemeralIntent, resolvedType,
-                aInfo, rInfo, voiceInteractionSession, voiceInteractor, resultTo, resultWho,
-                requestCode, callingPid, callingUid, callingPackage, realCallingPid, realCallingUid,
-                startFlags, options, ignoreTargetSecurity, componentSpecified, outActivity, inTask,
-                reason);
-
-        // The starter should receive a start command with the originally provided parameters
-        verify(mStarter, times(1)).startActivityLocked(eq(applicationThread), eq(intent),
-                eq(ephemeralIntent), eq(resolvedType), eq(aInfo), eq(rInfo),
-                eq(voiceInteractionSession), eq(voiceInteractor), eq(resultTo), eq(resultWho),
-                eq(requestCode), eq(callingPid), eq(callingUid), eq(callingPackage),
-                eq(realCallingPid), eq(realCallingUid), eq(startFlags), eq(options),
-                eq(ignoreTargetSecurity), eq(componentSpecified), eq(outActivity), eq(inTask),
-                eq(reason));
+        mStarter = spy(new ActivityStarter(mController, mService, mService.mStackSupervisor,
+                mock(ActivityStartInterceptor.class)));
+        doReturn(mStarter).when(mFactory).obtainStarter();
     }
 
     /**
@@ -149,7 +85,7 @@
         final boolean resume = random.nextBoolean();
         mController.doPendingActivityLaunches(resume);
 
-        verify(mStarter, times(1)).startActivity(eq(activity), eq(source), eq(null),
+        verify(mStarter, times(1)).startResolvedActivity(eq(activity), eq(source), eq(null),
                 eq(null), eq(startFlags), eq(resume), eq(null), eq(null), eq(null));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 471726b..e8194dd 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -166,10 +166,9 @@
      * Excercises how the {@link ActivityStarter} reacts to various preconditions. The caller
      * provides a bitmask of all the set conditions (such as {@link #PRECONDITION_NO_CALLER_APP})
      * and the launch flags specified in the intent. The method constructs a call to
-     * {@link ActivityStarter#startActivityLocked} based on these preconditions and ensures the
-     * result matches the expected. It is important to note that the method also checks side effects
-     * of the start, such as ensuring {@link ActivityOptions#abort()} is called in the relevant
-     * scenarios.
+     * {@link ActivityStarter#execute} based on these preconditions and ensures the result matches
+     * the expected. It is important to note that the method also checks side effects of the start,
+     * such as ensuring {@link ActivityOptions#abort()} is called in the relevant scenarios.
      * @param preconditions A bitmask representing the preconditions for the launch
      * @param launchFlags The launch flags to be provided by the launch {@link Intent}.
      * @param expectedResult The expected result from the launch.
@@ -254,14 +253,13 @@
         final int requestCode = containsConditions(preconditions, PRECONDITION_REQUEST_CODE)
                 ? 1 : 0;
 
-        final int result = starter.startActivityLocked(caller, intent,
-                null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
-                null /*voiceSession*/, null /*voiceInteractor*/, resultTo,
-                null /*resultWho*/, requestCode, 0 /*callingPid*/, 0 /*callingUid*/,
-                null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
-                0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/,
-                false /*componentSpecified*/, null /*outActivity*/,
-                null /*inTask*/, "testLaunchActivityPermissionDenied");
+        final int result = starter.setCaller(caller)
+                .setIntent(intent)
+                .setActivityInfo(aInfo)
+                .setResultTo(resultTo)
+                .setRequestCode(requestCode)
+                .setReason("testLaunchActivityPermissionDenied")
+                .execute();
 
         // In some cases the expected result internally is different than the published result. We
         // must use ActivityStarter#getExternalResult to translate.
@@ -269,15 +267,18 @@
 
         // Ensure that {@link ActivityOptions} are aborted with unsuccessful result.
         if (expectedResult != START_SUCCESS) {
+            final ActivityStarter optionStarter = new ActivityStarter(mController, mService,
+                    mService.mStackSupervisor, mock(ActivityStartInterceptor.class));
             final ActivityOptions options = spy(ActivityOptions.makeBasic());
-            final int optionResult = starter.startActivityLocked(caller, intent,
-                    null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
-                    null /*voiceSession*/, null /*voiceInteractor*/, resultTo,
-                    null /*resultWho*/, requestCode, 0 /*callingPid*/, 0 /*callingUid*/,
-                    null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
-                    0 /*startFlags*/, options /*options*/, false /*ignoreTargetSecurity*/,
-                    false /*componentSpecified*/, null /*outActivity*/,
-                    null /*inTask*/, "testLaunchActivityPermissionDenied");
+
+            final int optionResult = optionStarter.setCaller(caller)
+                    .setIntent(intent)
+                    .setActivityInfo(aInfo)
+                    .setResultTo(resultTo)
+                    .setRequestCode(requestCode)
+                    .setReason("testLaunchActivityPermissionDenied")
+                    .setActivityOptions(options)
+                    .execute();
             verify(options, times(1)).abort();
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
index 54df744..d2ae22b 100644
--- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java
@@ -30,6 +30,7 @@
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
 import static android.os.Process.SYSTEM_UID;
+import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
 
 import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_LOCKED;
 import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_PINNED;
@@ -53,6 +54,7 @@
 import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
+import android.telecom.TelecomManager;
 import android.util.Pair;
 
 import com.android.internal.statusbar.IStatusBarService;
@@ -90,6 +92,7 @@
     @Mock private LockPatternUtils mLockPatternUtils;
     @Mock private LockTaskNotify mLockTaskNotify;
     @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+    @Mock private TelecomManager mTelecomManager;
     @Mock private RecentTasks mRecentTasks;
 
     private LockTaskController mLockTaskController;
@@ -118,6 +121,7 @@
         mLockTaskController.setWindowManager(mWindowManager);
         mLockTaskController.mStatusBarService = mStatusBarService;
         mLockTaskController.mDevicePolicyManager = mDevicePolicyManager;
+        mLockTaskController.mTelecomManager = mTelecomManager;
         mLockTaskController.mLockPatternUtils = mLockPatternUtils;
         mLockTaskController.mLockTaskNotify = mLockTaskNotify;
 
@@ -209,7 +213,7 @@
 
     @Test
     public void testLockTaskViolation() throws Exception {
-        // GIVEN one task records with whitelisted auth that is in lock task mode
+        // GIVEN one task record with whitelisted auth that is in lock task mode
         TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
         mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
 
@@ -234,6 +238,38 @@
     }
 
     @Test
+    public void testLockTaskViolation_emergencyCall() throws Exception {
+        // GIVEN one task record with whitelisted auth that is in lock task mode
+        TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
+        mLockTaskController.startLockTaskMode(tr, false, TEST_UID);
+
+        // GIVEN tasks necessary for emergency calling
+        TaskRecord keypad = getTaskRecord(new Intent().setComponent(EMERGENCY_DIALER_COMPONENT),
+                TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+        TaskRecord callAction = getTaskRecord(new Intent(Intent.ACTION_CALL_EMERGENCY),
+                TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+        TaskRecord dialer = getTaskRecord("com.example.dialer", TaskRecord.LOCK_TASK_AUTH_PINNABLE);
+        when(mTelecomManager.getSystemDialerPackage())
+                .thenReturn(dialer.intent.getComponent().getPackageName());
+
+        // GIVEN keyguard is allowed for lock task mode
+        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_KEYGUARD);
+
+        // THEN the above tasks should all be allowed
+        assertFalse(mLockTaskController.isLockTaskModeViolation(keypad));
+        assertFalse(mLockTaskController.isLockTaskModeViolation(callAction));
+        assertFalse(mLockTaskController.isLockTaskModeViolation(dialer));
+
+        // GIVEN keyguard is disallowed for lock task mode (default)
+        mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NONE);
+
+        // THEN the above tasks should all be blocked
+        assertTrue(mLockTaskController.isLockTaskModeViolation(keypad));
+        assertTrue(mLockTaskController.isLockTaskModeViolation(callAction));
+        assertTrue(mLockTaskController.isLockTaskModeViolation(dialer));
+    }
+
+    @Test
     public void testStopLockTaskMode() throws Exception {
         // GIVEN one task record with whitelisted auth that is in lock task mode
         TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED);
@@ -568,10 +604,15 @@
     }
 
     private TaskRecord getTaskRecord(String pkg, int lockTaskAuth) {
+        final Intent intent = new Intent()
+                .setComponent(ComponentName.createRelative(pkg, TEST_CLASS_NAME));
+        return getTaskRecord(intent, lockTaskAuth);
+    }
+
+    private TaskRecord getTaskRecord(Intent intent, int lockTaskAuth) {
         TaskRecord tr = mock(TaskRecord.class);
         tr.mLockTaskAuth = lockTaskAuth;
-        tr.intent = new Intent()
-                .setComponent(ComponentName.createRelative(pkg, TEST_CLASS_NAME));
+        tr.intent = intent;
         tr.userId = TEST_USER_ID;
         return tr;
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
new file mode 100644
index 0000000..5520bd7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.server.am;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.platform.test.annotations.Presubmit;
+import android.service.voice.IVoiceInteractionSession;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.internal.app.IVoiceInteractor;
+import com.android.server.am.TaskRecord.TaskRecordFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Tests for exercising {@link TaskRecord}.
+ *
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.am.TaskRecordTests
+ */
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class TaskRecordTests {
+
+    @Before
+    public void setUp() throws Exception {
+        TaskRecord.setTaskRecordFactory(null);
+    }
+
+    @Test
+    public void testDefaultTaskFactoryNotNull() throws Exception {
+        assertNotNull(TaskRecord.getTaskRecordFactory());
+    }
+
+    @Test
+    public void testCreateTestRecordUsingCustomizedFactory() throws Exception {
+        TestTaskRecordFactory factory = new TestTaskRecordFactory();
+        TaskRecord.setTaskRecordFactory(factory);
+
+        assertFalse(factory.mCreated);
+
+        TaskRecord.create(null, 0, null, null, null, null);
+
+        assertTrue(factory.mCreated);
+    }
+
+    private static class TestTaskRecordFactory extends TaskRecordFactory {
+        private boolean mCreated = false;
+
+        @Override
+        TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+                Intent intent,
+                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
+            mCreated = true;
+            return null;
+        }
+
+        @Override
+        TaskRecord create(ActivityManagerService service, int taskId, ActivityInfo info,
+                Intent intent,
+                ActivityManager.TaskDescription taskDescription) {
+            mCreated = true;
+            return null;
+        }
+
+        @Override
+        TaskRecord create(ActivityManagerService service, int taskId, Intent intent,
+                Intent affinityIntent, String affinity, String rootAffinity,
+                ComponentName realActivity,
+                ComponentName origActivity, boolean rootWasReset, boolean autoRemoveRecents,
+                boolean askedCompatMode, int userId, int effectiveUid, String lastDescription,
+                ArrayList<ActivityRecord> activities, long lastTimeMoved,
+                boolean neverRelinquishIdentity,
+                ActivityManager.TaskDescription lastTaskDescription,
+                int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor,
+                int callingUid, String callingPackage, int resizeMode,
+                boolean supportsPictureInPicture,
+                boolean realActivitySuspended, boolean userSetupComplete, int minWidth,
+                int minHeight) {
+            mCreated = true;
+            return null;
+        }
+
+        @Override
+        TaskRecord restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
+                throws IOException, XmlPullParserException {
+            mCreated = true;
+            return null;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 7e11e87..e2ba4d5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -103,7 +103,7 @@
             long callingIdentity = clearCallingIdentity();
             Throwable throwableToPropagate = null;
             try {
-                action.run();
+                action.runOrThrow();
             } catch (Throwable throwable) {
                 throwableToPropagate = throwable;
             } finally {
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 6a1d268..926009e 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -134,7 +134,7 @@
         mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
         mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
                 Settings.System.SCREEN_BRIGHTNESS));
-        List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+        List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         mTracker.stop();
 
         assertEquals(1, events.size());
@@ -169,7 +169,9 @@
         final long sensorTime = mInjector.currentTimeMillis();
         mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
                 Settings.System.SCREEN_BRIGHTNESS));
-        List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+        List<BrightnessChangeEvent> eventsNoPackage
+                = mTracker.getEvents(0, false).getList();
+        List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         mTracker.stop();
 
         assertEquals(1, events.size());
@@ -184,6 +186,9 @@
         assertEquals(3333, event.colorTemperature);
         assertEquals("a.package", event.packageName);
         assertEquals(0, event.userId);
+
+        assertEquals(1, eventsNoPackage.size());
+        assertNull(eventsNoPackage.get(0).packageName);
     }
 
     @Test
@@ -200,7 +205,7 @@
                 (int) mInjector.mSystemIntSettings.get(Settings.System.SCREEN_BRIGHTNESS));
         mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
                 Settings.System.SCREEN_BRIGHTNESS));
-        List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+        List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         // No events because we filtered out our change.
         assertEquals(0, events.size());
 
@@ -217,7 +222,7 @@
                 secondUserUpdateBrightness);
         mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
                 Settings.System.SCREEN_BRIGHTNESS));
-        events = mTracker.getEvents(0).getList();
+        events = mTracker.getEvents(0, true).getList();
 
         assertEquals(2, events.size());
         // First event is change from system update (20) to first user update (20)
@@ -242,7 +247,7 @@
             mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
                     Settings.System.SCREEN_BRIGHTNESS));
         }
-        List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+        List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         mTracker.stop();
 
         // Should be capped at 100 events, and they should be the most recent 100.
@@ -266,7 +271,7 @@
         }
         mInjector.mSettingsObserver.onChange(false, Settings.System.getUriFor(
                 Settings.System.SCREEN_BRIGHTNESS));
-        List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+        List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         mTracker.stop();
 
         assertEquals(1, events.size());
@@ -317,7 +322,7 @@
                 + Long.toString(twoMonthsAgo) + "," + Long.toString(twoMonthsAgo) + "\"/>"
                 + "</events>";
         tracker.readEventsLocked(getInputStream(eventFile));
-        List<BrightnessChangeEvent> events = tracker.getEvents(0).getList();
+        List<BrightnessChangeEvent> events = tracker.getEvents(0, true).getList();
         assertEquals(1, events.size());
         BrightnessChangeEvent event = events.get(0);
         assertEquals(someTimeAgo, event.timeStamp);
@@ -330,7 +335,7 @@
         assertEquals(1.0f, event.batteryLevel, 0.01);
         assertEquals("com.example.app", event.packageName);
 
-        events = tracker.getEvents(1).getList();
+        events = tracker.getEvents(1, true).getList();
         assertEquals(1, events.size());
         event = events.get(0);
         assertEquals(someTimeAgo, event.timeStamp);
@@ -359,7 +364,7 @@
         } catch (IOException e) {
             // Expected;
         }
-        assertEquals(0, tracker.getEvents(0).getList().size());
+        assertEquals(0, tracker.getEvents(0, true).getList().size());
 
         // Missing lux value.
         eventFile =
@@ -374,7 +379,7 @@
         } catch (IOException e) {
             // Expected;
         }
-        assertEquals(0, tracker.getEvents(0).getList().size());
+        assertEquals(0, tracker.getEvents(0, true).getList().size());
     }
 
     @Test
@@ -405,7 +410,7 @@
         BrightnessTracker tracker = new BrightnessTracker(InstrumentationRegistry.getContext(),
                 mInjector);
         tracker.readEventsLocked(input);
-        List<BrightnessChangeEvent> events = tracker.getEvents(0).getList();
+        List<BrightnessChangeEvent> events = tracker.getEvents(0, true).getList();
 
         assertEquals(1, events.size());
         BrightnessChangeEvent event = events.get(0);
@@ -442,12 +447,12 @@
                 Settings.System.SCREEN_BRIGHTNESS));
         final long eventTime = mInjector.currentTimeMillis();
 
-        List<BrightnessChangeEvent> events = mTracker.getEvents(0).getList();
+        List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
         assertEquals(2, events.size());
 
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         mTracker.writeEventsLocked(baos);
-        events = mTracker.getEvents(0).getList();
+        events = mTracker.getEvents(0, true).getList();
         mTracker.stop();
 
         assertEquals(1, events.size());
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index c735341..481c898 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -411,11 +411,6 @@
     }
 
     @Override
-    public boolean inKeyguardRestrictedKeyInputMode() {
-        return false;
-    }
-
-    @Override
     public void dismissKeyguardLw(@Nullable IKeyguardDismissCallback callback) {
     }
 
diff --git a/services/tests/shortcutmanagerutils/Android.mk b/services/tests/shortcutmanagerutils/Android.mk
index c7657f6..0848fd5 100644
--- a/services/tests/shortcutmanagerutils/Android.mk
+++ b/services/tests/shortcutmanagerutils/Android.mk
@@ -21,7 +21,8 @@
 
 LOCAL_JAVA_LIBRARIES := \
     mockito-target \
-    legacy-android-test
+    legacy-android-test \
+    android.test.runner.stubs
 
 LOCAL_MODULE_TAGS := optional
 
diff --git a/services/tests/notification/Android.mk b/services/tests/uiservicestests/Android.mk
similarity index 89%
rename from services/tests/notification/Android.mk
rename to services/tests/uiservicestests/Android.mk
index 597a584..40e7878 100644
--- a/services/tests/notification/Android.mk
+++ b/services/tests/uiservicestests/Android.mk
@@ -1,5 +1,5 @@
 #########################################################################
-# Build FrameworksNotificationTests package
+# Build FrameworksUiServicesTests package
 #########################################################################
 
 LOCAL_PATH:= $(call my-dir)
@@ -25,12 +25,12 @@
     platform-test-annotations \
     testables
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
 
 LOCAL_JACK_FLAGS := --multi-dex native
 LOCAL_DX_FLAGS := --multi-dex
 
-LOCAL_PACKAGE_NAME := FrameworksNotificationTests
+LOCAL_PACKAGE_NAME := FrameworksUiServicesTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
 
 LOCAL_CERTIFICATE := platform
diff --git a/services/tests/notification/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
similarity index 91%
rename from services/tests/notification/AndroidManifest.xml
rename to services/tests/uiservicestests/AndroidManifest.xml
index c20020a..621b457 100644
--- a/services/tests/notification/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -15,7 +15,7 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.frameworks.tests.notification">
+        package="com.android.frameworks.tests.uiservices">
 
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
@@ -32,6 +32,6 @@
 
     <instrumentation
         android:name="android.testing.TestableInstrumentation"
-        android:targetPackage="com.android.frameworks.tests.notification"
+        android:targetPackage="com.android.frameworks.tests.uiservices"
         android:label="Notification Tests" />
 </manifest>
diff --git a/services/tests/notification/AndroidTest.xml b/services/tests/uiservicestests/AndroidTest.xml
similarity index 82%
rename from services/tests/notification/AndroidTest.xml
rename to services/tests/uiservicestests/AndroidTest.xml
index 448bc3d..d3b9d4a 100644
--- a/services/tests/notification/AndroidTest.xml
+++ b/services/tests/uiservicestests/AndroidTest.xml
@@ -13,16 +13,16 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Runs Frameworks Notification Tests.">
+<configuration description="Runs Frameworks UI Services Tests.">
     <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
-        <option name="test-file-name" value="FrameworksNotificationTests.apk" />
+        <option name="test-file-name" value="FrameworksUiServicesTests.apk" />
     </target_preparer>
 
     <option name="test-suite-tag" value="apct" />
     <option name="test-suite-tag" value="framework-base-presubmit" />
-    <option name="test-tag" value="FrameworksNotificationTests" />
+    <option name="test-tag" value="FrameworksUiServicesTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
-        <option name="package" value="com.android.frameworks.tests.notification" />
+        <option name="package" value="com.android.frameworks.tests.uiservices" />
         <option name="runner" value="android.testing.TestableInstrumentation" />
     </test>
 </configuration>
diff --git a/services/tests/notification/src/com/android/server/notification/AlertRateLimiterTest.java b/services/tests/uiservicestests/src/com/android/server/notification/AlertRateLimiterTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/AlertRateLimiterTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/AlertRateLimiterTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/BadgeExtractorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/GroupHelperTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/ManagedServicesTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationChannelExtractorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationChannelTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationChannelTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationStatsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationStatsTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationTestCase.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTestCase.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/NotificationTestCase.java
rename to services/tests/uiservicestests/src/com/android/server/notification/NotificationTestCase.java
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/RateEstimatorTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
similarity index 99%
rename from services/tests/notification/src/com/android/server/notification/ScheduleCalendarTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
index cbda12d..4eb4220 100644
--- a/services/tests/notification/src/com/android/server/notification/ScheduleCalendarTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -21,10 +21,10 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.service.notification.ScheduleCalendar;
 import android.service.notification.ZenModeConfig;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Slog;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/services/tests/notification/src/com/android/server/notification/ScheduleConditionProviderTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
similarity index 92%
rename from services/tests/notification/src/com/android/server/notification/ScheduleConditionProviderTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
index ddf46a0..610592f 100644
--- a/services/tests/notification/src/com/android/server/notification/ScheduleConditionProviderTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleConditionProviderTest.java
@@ -1,46 +1,51 @@
 package com.android.server.notification;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.spy;
 
 import android.content.Intent;
 import android.net.Uri;
-import android.os.Looper;
 import android.service.notification.Condition;
+import android.service.notification.ScheduleCalendar;
 import android.service.notification.ZenModeConfig;
-import android.support.test.InstrumentationRegistry;
-import android.test.ServiceTestCase;
-import android.testing.TestableContext;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.MockitoAnnotations;
 
 import java.util.Calendar;
 import java.util.GregorianCalendar;
 
-public class ScheduleConditionProviderTest extends ServiceTestCase<ScheduleConditionProvider> {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ScheduleConditionProviderTest extends NotificationTestCase {
 
     ScheduleConditionProvider mService;
 
-    @Rule
-    public final TestableContext mContext =
-            new TestableContext(InstrumentationRegistry.getContext(), null);
-
-    public ScheduleConditionProviderTest() {
-        super(ScheduleConditionProvider.class);
-    }
-
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        Looper.prepare();
 
         Intent startIntent =
                 new Intent("com.android.server.notification.ScheduleConditionProvider");
         startIntent.setPackage("android");
-        bindService(startIntent);
-        mService = spy(getService());
+        ScheduleConditionProvider service = new ScheduleConditionProvider();
+        service.attach(
+                getContext(),
+                null,               // ActivityThread not actually used in Service
+                ScheduleConditionProvider.class.getName(),
+                null,               // token not needed when not talking with the activity manager
+                null,
+                null                // mocked services don't talk with the activity manager
+                );
+        service.onCreate();
+        service.onBind(startIntent);
+        mService = spy(service);
    }
 
     @Test
diff --git a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/ValidateNotificationPeopleTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
diff --git a/services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
similarity index 100%
rename from services/tests/notification/src/com/android/server/notification/ZenModeHelperTest.java
rename to services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 4527879..3d20a64 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -16,10 +16,12 @@
 
 package com.android.server.usage;
 
+import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
 import static android.app.usage.UsageStatsManager.REASON_FORCED;
 import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
 import static android.app.usage.UsageStatsManager.REASON_USAGE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
@@ -378,8 +380,12 @@
                     Slog.d(TAG, "   Checking idle state for " + packageName);
                 }
                 if (isSpecial) {
+                    synchronized (mAppIdleLock) {
+                        mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
+                                STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT);
+                    }
                     maybeInformListeners(packageName, userId, elapsedRealtime,
-                            STANDBY_BUCKET_ACTIVE);
+                            STANDBY_BUCKET_EXEMPTED);
                 } else {
                     synchronized (mAppIdleLock) {
                         String bucketingReason = mAppIdleHistory.getAppStandbyReason(packageName,
@@ -389,7 +395,8 @@
                             continue;
                         }
                         // If the bucket was moved up due to usage, let the timeouts apply.
-                        if (REASON_USAGE.equals(bucketingReason)
+                        if (REASON_DEFAULT.equals(bucketingReason)
+                                || REASON_USAGE.equals(bucketingReason)
                                 || REASON_TIMEOUT.equals(bucketingReason)) {
                             int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId,
                                     elapsedRealtime);
@@ -886,6 +893,11 @@
                 String packageName = pi.packageName;
                 if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
                     mAppIdleHistory.reportUsage(packageName, userId, elapsedRealtime);
+                    if (isAppSpecial(packageName, UserHandle.getAppId(pi.applicationInfo.uid),
+                            userId)) {
+                        mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
+                                STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT);
+                    }
                 }
             }
         }
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index dd2e192..e76d211 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -29,18 +29,21 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.usb.descriptors.UsbDescriptorParser;
+import com.android.server.usb.descriptors.UsbDeviceDescriptor;
 import com.android.server.usb.descriptors.report.TextReportCanvas;
 import com.android.server.usb.descriptors.tree.UsbDescriptorsTree;
 
-import java.util.Collection;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.HashMap;
+import java.util.LinkedList;
 
 /**
  * UsbHostManager manages USB state in host mode.
  */
 public class UsbHostManager {
     private static final String TAG = UsbHostManager.class.getSimpleName();
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
 
     private final Context mContext;
 
@@ -63,6 +66,76 @@
     @GuardedBy("mHandlerLock")
     private ComponentName mUsbDeviceConnectionHandler;
 
+    /*
+     * Member used for tracking connections & disconnections
+     */
+    static final SimpleDateFormat sFormat = new SimpleDateFormat("MM-dd HH:mm:ss:SSS");
+    private static final int MAX_CONNECT_RECORDS = 32;
+    private int mNumConnects;    // TOTAL # of connect/disconnect
+    private final LinkedList<ConnectionRecord> mConnections = new LinkedList<ConnectionRecord>();
+    private ConnectionRecord mLastConnect;
+
+    /*
+     * ConnectionRecord
+     * Stores connection/disconnection data.
+     */
+    class ConnectionRecord {
+        long mTimestamp;        // Same time-base as system log.
+        String mDeviceAddress;
+
+        static final int CONNECT = 0;
+        static final int DISCONNECT = 1;
+        final int mMode;
+        final byte[] mDescriptors;
+
+        ConnectionRecord(String deviceAddress, int mode, byte[] descriptors) {
+            mTimestamp = System.currentTimeMillis();
+            mDeviceAddress = deviceAddress;
+            mMode = mode;
+            mDescriptors = descriptors;
+        }
+
+        private String formatTime() {
+            return (new StringBuilder(sFormat.format(new Date(mTimestamp)))).toString();
+        }
+
+        void dumpShort(IndentingPrintWriter pw) {
+            if (mMode == CONNECT) {
+                pw.println(formatTime() + " Connect " + mDeviceAddress);
+                UsbDescriptorParser parser = new UsbDescriptorParser(mDeviceAddress, mDescriptors);
+
+                UsbDeviceDescriptor deviceDescriptor = parser.getDeviceDescriptor();
+
+                pw.println("manfacturer:0x" + Integer.toHexString(deviceDescriptor.getVendorID())
+                        + " product:" + Integer.toHexString(deviceDescriptor.getProductID()));
+                pw.println("isHeadset[in: " + parser.isInputHeadset()
+                        + " , out: " + parser.isOutputHeadset() + "]");
+            } else {
+                pw.println(formatTime() + " Disconnect " + mDeviceAddress);
+            }
+        }
+
+        void dumpLong(IndentingPrintWriter pw) {
+            if (mMode == CONNECT) {
+                pw.println(formatTime() + " Connect " + mDeviceAddress);
+                UsbDescriptorParser parser = new UsbDescriptorParser(mDeviceAddress, mDescriptors);
+                StringBuilder stringBuilder = new StringBuilder();
+                UsbDescriptorsTree descriptorTree = new UsbDescriptorsTree();
+                descriptorTree.parse(parser);
+                descriptorTree.report(new TextReportCanvas(parser, stringBuilder));
+
+                stringBuilder.append("isHeadset[in: " + parser.isInputHeadset()
+                        + " , out: " + parser.isOutputHeadset() + "]");
+                pw.println(stringBuilder.toString());
+            } else {
+                pw.println(formatTime() + " Disconnect " + mDeviceAddress);
+            }
+        }
+    }
+
+    /*
+     * UsbHostManager
+     */
     public UsbHostManager(Context context, UsbAlsaManager alsaManager,
             UsbSettingsManager settingsManager) {
         mContext = context;
@@ -124,6 +197,19 @@
 
     }
 
+    private void addConnectionRecord(String deviceAddress, int mode, byte[] rawDescriptors) {
+        mNumConnects++;
+        while (mConnections.size() >= MAX_CONNECT_RECORDS) {
+            mConnections.removeFirst();
+        }
+        ConnectionRecord rec =
+                new ConnectionRecord(deviceAddress, mode, rawDescriptors);
+        mConnections.add(rec);
+        if (mode == ConnectionRecord.CONNECT) {
+            mLastConnect = rec;
+        }
+    }
+
     /* Called from JNI in monitorUsbHostBus() to report new USB devices
        Returns true if successful, i.e. the USB Audio device descriptors are
        correctly parsed and the unique device is added to the audio device list.
@@ -174,6 +260,10 @@
                         + " , out: " + isOutputHeadset + "]");
 
                 mUsbAlsaManager.usbDeviceAdded(newDevice, isInputHeadset, isOutputHeadset);
+
+                // Tracking
+                addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT,
+                        parser.getRawDescriptors());
             } else {
                 Slog.e(TAG, "Error parsing USB device descriptors for " + deviceAddress);
                 return false;
@@ -196,6 +286,9 @@
                 mUsbAlsaManager.usbDeviceRemoved(device);
                 mSettingsManager.usbDeviceRemoved(device);
                 getCurrentUserSettings().usbDeviceRemoved(device);
+
+                // Tracking
+                addConnectionRecord(deviceAddress, ConnectionRecord.DISCONNECT, null);
             }
         }
     }
@@ -249,26 +342,15 @@
                 pw.println("  " + name + ": " + mDevices.get(name));
             }
 
-            Collection<UsbDevice> devices = mDevices.values();
-            if (devices.size() != 0) {
-                pw.println("USB Peripheral Descriptors");
-                for (UsbDevice device : devices) {
-                    StringBuilder stringBuilder = new StringBuilder();
+            pw.println("" + mNumConnects + " total connects/disconnects");
+            pw.println("Last " + mConnections.size() + " connections/disconnections");
+            for (ConnectionRecord rec : mConnections) {
+                rec.dumpShort(pw);
+            }
 
-                    UsbDescriptorParser parser = new UsbDescriptorParser(device.getDeviceName());
-                    if (parser.parseDevice()) {
-                        UsbDescriptorsTree descriptorTree = new UsbDescriptorsTree();
-                        descriptorTree.parse(parser);
-
-                        descriptorTree.report(new TextReportCanvas(parser, stringBuilder));
-
-                        stringBuilder.append("isHeadset[in: " + parser.isInputHeadset()
-                                + " , out: " + parser.isOutputHeadset() + "]");
-                    } else {
-                        stringBuilder.append("Error Parsing USB Descriptors");
-                    }
-                    pw.println(stringBuilder.toString());
-                }
+            if (mLastConnect != null) {
+                pw.println("Last Connected USB Device:");
+                mLastConnect.dumpLong(pw);
             }
         }
 
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 6c6bd01..78c7fdc 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -32,7 +32,7 @@
 
     // Descriptor Objects
     private static final int DESCRIPTORS_ALLOC_SIZE = 128;
-    private ArrayList<UsbDescriptor> mDescriptors = new ArrayList<UsbDescriptor>();
+    private final ArrayList<UsbDescriptor> mDescriptors;
 
     private UsbDeviceDescriptor mDeviceDescriptor;
     private UsbConfigDescriptor mCurConfigDescriptor;
@@ -45,6 +45,28 @@
 
     public UsbDescriptorParser(String deviceAddr) {
         mDeviceAddr = deviceAddr;
+        mDescriptors = new ArrayList<UsbDescriptor>(DESCRIPTORS_ALLOC_SIZE);
+    }
+
+    /**
+     * Connect this parser to an existing set of already parsed descriptors.
+     * This is useful for reporting.
+     */
+    public UsbDescriptorParser(String deviceAddr, ArrayList<UsbDescriptor> descriptors) {
+        mDeviceAddr = deviceAddr;
+        mDescriptors = descriptors;
+        //TODO some error checking here....
+        mDeviceDescriptor = (UsbDeviceDescriptor) descriptors.get(0);
+    }
+
+    /**
+     * Connect this parser to an byte array containing unparsed (raw) device descriptors
+     * to be parsed (and parse them). Useful for parsing a stored descriptor buffer.
+     */
+    public UsbDescriptorParser(String deviceAddr, byte[] rawDescriptors) {
+        mDeviceAddr = deviceAddr;
+        mDescriptors = new ArrayList<UsbDescriptor>(DESCRIPTORS_ALLOC_SIZE);
+        parseDescriptors(rawDescriptors);
     }
 
     public String getDeviceAddr() {
@@ -196,8 +218,6 @@
         if (DEBUG) {
             Log.d(TAG, "parseDescriptors() - start");
         }
-        // This will allow us to (probably) alloc mDescriptors just once.
-        mDescriptors = new ArrayList<UsbDescriptor>(DESCRIPTORS_ALLOC_SIZE);
 
         ByteStream stream = new ByteStream(descriptors);
         while (stream.available() > 0) {
@@ -241,7 +261,7 @@
             ? parseDescriptors(rawDescriptors) : false;
     }
 
-    private byte[] getRawDescriptors() {
+    public byte[] getRawDescriptors() {
         return getRawDescriptors_native(mDeviceAddr);
     }
 
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 2834201..7001615 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -143,6 +143,8 @@
     private static final String SESSION_START_RTT = "CS.+RTT";
     private static final String SESSION_STOP_RTT = "CS.-RTT";
     private static final String SESSION_RTT_UPGRADE_RESPONSE = "CS.rTRUR";
+    private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL";
+    private static final String SESSION_CONNECTION_SERVICE_FOCUS_GAINED = "CS.cSFG";
 
     private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;
     private static final int MSG_CREATE_CONNECTION = 2;
@@ -172,6 +174,8 @@
     private static final int MSG_ON_STOP_RTT = 27;
     private static final int MSG_RTT_UPGRADE_RESPONSE = 28;
     private static final int MSG_CREATE_CONNECTION_COMPLETE = 29;
+    private static final int MSG_CONNECTION_SERVICE_FOCUS_LOST = 30;
+    private static final int MSG_CONNECTION_SERVICE_FOCUS_GAINED = 31;
 
     private static Connection sNullConnection;
 
@@ -591,6 +595,26 @@
                 Log.endSession();
             }
         }
+
+        @Override
+        public void connectionServiceFocusLost(Session.Info sessionInfo) throws RemoteException {
+            Log.startSession(sessionInfo, SESSION_CONNECTION_SERVICE_FOCUS_LOST);
+            try {
+                mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_LOST).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
+        public void connectionServiceFocusGained(Session.Info sessionInfo) throws RemoteException {
+            Log.startSession(sessionInfo, SESSION_CONNECTION_SERVICE_FOCUS_GAINED);
+            try {
+                mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_GAINED).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
     };
 
     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -1012,6 +1036,12 @@
                     }
                     break;
                 }
+                case MSG_CONNECTION_SERVICE_FOCUS_GAINED:
+                    onConnectionServiceFocusGained();
+                    break;
+                case MSG_CONNECTION_SERVICE_FOCUS_LOST:
+                    onConnectionServiceFocusLost();
+                    break;
                 default:
                     break;
             }
@@ -1873,6 +1903,16 @@
     }
 
     /**
+     * Call to inform Telecom that your {@link ConnectionService} has released call resources (e.g
+     * microphone, camera).
+     *
+     * @see ConnectionService#onConnectionServiceFocusLost()
+     */
+    public final void connectionServiceFocusReleased() {
+        mAdapter.onConnectionServiceFocusReleased();
+    }
+
+    /**
      * Adds a connection created by the {@link ConnectionService} and informs telecom of the new
      * connection.
      *
@@ -2146,6 +2186,20 @@
     public void onRemoteExistingConnectionAdded(RemoteConnection connection) {}
 
     /**
+     * Called when the {@link ConnectionService} has lost the call focus.
+     * The {@link ConnectionService} should release the call resources and invokes
+     * {@link ConnectionService#connectionServiceFocusReleased()} to inform telecom that it has
+     * released the call resources.
+     */
+    public void onConnectionServiceFocusLost() {}
+
+    /**
+     * Called when the {@link ConnectionService} has gained the call focus. The
+     * {@link ConnectionService} can acquire the call resources at this time.
+     */
+    public void onConnectionServiceFocusGained() {}
+
+    /**
      * @hide
      */
     public boolean containsConference(Conference conference) {
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 92a9dc2..0d319bb 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -628,4 +628,17 @@
             }
         }
     }
+
+    /**
+     * Notifies Telecom that the {@link ConnectionService} has released the call resource.
+     */
+    void onConnectionServiceFocusReleased() {
+        for (IConnectionServiceAdapter adapter : mAdapters) {
+            try {
+                Log.d(this, "onConnectionServiceFocusReleased");
+                adapter.onConnectionServiceFocusReleased(Log.getExternalSession());
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
 }
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 3fbdeb1..3e1bf77 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -73,6 +73,7 @@
     private static final int MSG_ON_RTT_REMOTELY_TERMINATED = 32;
     private static final int MSG_ON_RTT_UPGRADE_REQUEST = 33;
     private static final int MSG_SET_PHONE_ACCOUNT_CHANGED = 34;
+    private static final int MSG_CONNECTION_SERVICE_FOCUS_RELEASED = 35;
 
     private final IConnectionServiceAdapter mDelegate;
 
@@ -329,6 +330,9 @@
                     }
                     break;
                 }
+                case MSG_CONNECTION_SERVICE_FOCUS_RELEASED:
+                    mDelegate.onConnectionServiceFocusReleased(null /*Session.Info*/);
+                    break;
             }
         }
     };
@@ -601,6 +605,11 @@
             args.arg2 = pHandle;
             mHandler.obtainMessage(MSG_SET_PHONE_ACCOUNT_CHANGED, args).sendToTarget();
         }
+
+        @Override
+        public void onConnectionServiceFocusReleased(Session.Info sessionInfo) {
+            mHandler.obtainMessage(MSG_CONNECTION_SERVICE_FOCUS_RELEASED).sendToTarget();
+        }
     };
 
     public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) {
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 85906ad..59ce590 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -213,6 +213,9 @@
         }
 
         @Override
+        public void onConnectionServiceFocusReleased(Session.Info sessionInfo) {}
+
+        @Override
         public void addConferenceCall(
                 final String callId, ParcelableConference parcel, Session.Info sessionInfo) {
             RemoteConference conference = new RemoteConference(callId,
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 92d458f..6dcc3da 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -582,13 +582,21 @@
             "android.telecom.extra.CALL_BACK_INTENT";
 
     /**
+     * The dialer activity responsible for placing emergency calls from, for example, a locked
+     * keyguard.
+     * @hide
+     */
+    public static final ComponentName EMERGENCY_DIALER_COMPONENT =
+            ComponentName.createRelative("com.android.phone", ".EmergencyDialer");
+
+    /**
      * The following 4 constants define how properties such as phone numbers and names are
      * displayed to the user.
      */
 
     /**
      * Indicates that the address or number of a call is allowed to be displayed for caller ID.
-    */
+     */
     public static final int PRESENTATION_ALLOWED = 1;
 
     /**
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index e428286..a740566 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -100,4 +100,8 @@
 
     void respondToRttUpgradeRequest(String callId, in ParcelFileDescriptor fromInCall,
     in ParcelFileDescriptor toInCall, in Session.Info sessionInfo);
+
+    void connectionServiceFocusLost(in Session.Info sessionInfo);
+
+    void connectionServiceFocusGained(in Session.Info sessionInfo);
 }
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index da2015f..be474bd 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -119,4 +119,6 @@
 
     void onPhoneAccountChanged(String callId, in PhoneAccountHandle pHandle,
     in Session.Info sessionInfo);
+
+    void onConnectionServiceFocusReleased(in Session.Info sessionInfo);
 }
diff --git a/telephony/java/android/telephony/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/test-base/Android.mk b/test-base/Android.mk
index 6a1ac9e..03bdcf23 100644
--- a/test-base/Android.mk
+++ b/test-base/Android.mk
@@ -60,6 +60,9 @@
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
+# For unbundled build we'll use the prebuilt jar from prebuilts/sdk.
+ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)))
+
 # Generate the stub source files for android.test.base.stubs
 # ==========================================================
 include $(CLEAR_VARS)
@@ -151,6 +154,8 @@
 	@echo Copying removed.txt
 	$(hide) $(ACP) $(ANDROID_TEST_BASE_OUTPUT_REMOVED_API_FILE) $(ANDROID_TEST_BASE_REMOVED_API_FILE)
 
+endif  # not TARGET_BUILD_APPS not TARGET_BUILD_PDK=true
+
 # Build the legacy-android-test library
 # =====================================
 # This contains the android.test classes that were in Android API level 25,
diff --git a/tests/AppLaunch/Android.mk b/tests/AppLaunch/Android.mk
index d01b1f9..09739e5 100644
--- a/tests/AppLaunch/Android.mk
+++ b/tests/AppLaunch/Android.mk
@@ -9,7 +9,7 @@
 LOCAL_PACKAGE_NAME := AppLaunch
 
 LOCAL_CERTIFICATE := platform
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.base android.test.runner
 
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
index 527d1bbf..9e7f618 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
@@ -24,7 +24,7 @@
 
 LOCAL_SRC_FILES += $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.base
 LOCAL_STATIC_JAVA_LIBRARIES := guava junit
 
 LOCAL_PROGUARD_ENABLED := disabled
diff --git a/tests/ServiceCrashTest/Android.mk b/tests/ServiceCrashTest/Android.mk
index d1f8456..f7b3452 100644
--- a/tests/ServiceCrashTest/Android.mk
+++ b/tests/ServiceCrashTest/Android.mk
@@ -9,7 +9,7 @@
 LOCAL_PACKAGE_NAME := ServiceCrashTest
 
 LOCAL_CERTIFICATE := platform
-LOCAL_JAVA_LIBRARIES := legacy-android-test
+LOCAL_JAVA_LIBRARIES := android.test.base
 
 LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test
 
diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk
index 88794c2..202a699 100644
--- a/tests/backup/Android.mk
+++ b/tests/backup/Android.mk
@@ -20,7 +20,8 @@
 
 LOCAL_SRC_FILES := \
     backup_helper_test.cpp
- 
+
+LOCAL_CFLAGS := -Wall -Werror
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := backup_helper_test
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
@@ -43,4 +44,3 @@
 LOCAL_PROGUARD_ENABLED := disabled
 
 include $(BUILD_PACKAGE)
-    
diff --git a/tests/backup/backup_helper_test.cpp b/tests/backup/backup_helper_test.cpp
index b5f6ff5..457dcc4 100644
--- a/tests/backup/backup_helper_test.cpp
+++ b/tests/backup/backup_helper_test.cpp
@@ -118,7 +118,7 @@
 
 #else
 int
-main(int argc, char** argv)
+main(int, char**)
 {
     printf ("test_backup_helper built without the tests\n");
     return 0;
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 677585c..1bd1af5 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -21,7 +21,9 @@
     services.net
 
 LOCAL_JAVA_LIBRARIES := \
-    android.test.runner
+    android.test.runner \
+    android.test.base \
+    android.test.mock
 
 LOCAL_PACKAGE_NAME := FrameworksNetTests
 LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 8e579aa..0720886f 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -270,8 +270,8 @@
     }
 
     /**
-     * This function checks if the number of encap UDP socket that one UID can reserve
-     * has a reasonable limit.
+     * This function checks if the number of encap UDP socket that one UID can reserve has a
+     * reasonable limit.
      */
     @Test
     public void testSocketResourceTrackerLimitation() throws Exception {
@@ -287,9 +287,10 @@
             openUdpEncapSockets.add(newUdpEncapSocket);
         }
         // Assert that the total sockets quota has a reasonable limit.
+        assertTrue("No UDP encap socket was open", !openUdpEncapSockets.isEmpty());
         assertTrue(
-                openUdpEncapSockets.size() > 0
-                        && openUdpEncapSockets.size() < MAX_NUM_ENCAP_SOCKETS);
+                "Number of open UDP encap sockets is out of bound",
+                openUdpEncapSockets.size() < MAX_NUM_ENCAP_SOCKETS);
 
         // Try to reserve one more UDP encapsulation socket, and should fail.
         IpSecUdpEncapResponse extraUdpEncapSocket =
@@ -297,7 +298,7 @@
         assertNotNull(extraUdpEncapSocket);
         assertEquals(IpSecManager.Status.RESOURCE_UNAVAILABLE, extraUdpEncapSocket.status);
 
-        // Close one of the open UDP encapsulation scokets.
+        // Close one of the open UDP encapsulation sockets.
         mIpSecService.closeUdpEncapsulationSocket(openUdpEncapSockets.get(0).resourceId);
         openUdpEncapSockets.get(0).fileDescriptor.close();
         openUdpEncapSockets.remove(0);
@@ -316,10 +317,9 @@
     }
 
     /**
-     * This function checks if the number of SPI that one UID can reserve
-     * has a reasonable limit.
-     * This test does not test for both address families or duplicate SPIs because resource
-     * tracking code does not depend on them.
+     * This function checks if the number of SPI that one UID can reserve has a reasonable limit.
+     * This test does not test for both address families or duplicate SPIs because resource tracking
+     * code does not depend on them.
      */
     @Test
     public void testSpiResourceTrackerLimitation() throws Exception {
diff --git a/tests/testables/Android.mk b/tests/testables/Android.mk
index 0e36981..7fcfc6e 100644
--- a/tests/testables/Android.mk
+++ b/tests/testables/Android.mk
@@ -25,10 +25,9 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
-    mockito-target-minus-junit4 \
-    legacy-android-test
+    mockito-target-minus-junit4
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.mock
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
diff --git a/tests/utils/testutils/Android.mk b/tests/utils/testutils/Android.mk
index 43d1e37..543c652 100644
--- a/tests/utils/testutils/Android.mk
+++ b/tests/utils/testutils/Android.mk
@@ -25,9 +25,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
-    legacy-android-test \
     mockito-target-minus-junit4
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 3b90637..1bdb762 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -75,6 +75,10 @@
   TableFlattenerOptions table_flattener_options;
 
   Maybe<PostProcessingConfiguration> configuration;
+
+  // Set of artifacts to keep when generating multi-APK splits. If the list is empty, all artifacts
+  // are kept and will be written as output.
+  std::unordered_set<std::string> kept_artifacts;
 };
 
 class OptimizeContext : public IAaptContext {
@@ -197,9 +201,12 @@
 
     if (options_.configuration && options_.output_dir) {
       MultiApkGenerator generator{apk.get(), context_};
-      MultiApkGeneratorOptions generator_options = {options_.output_dir.value(),
-                                                    options_.configuration.value(),
-                                                    options_.table_flattener_options};
+      MultiApkGeneratorOptions generator_options = {
+          options_.output_dir.value(),
+          options_.configuration.value(),
+          options_.table_flattener_options,
+          options_.kept_artifacts,
+      };
       if (!generator.FromBaseApk(generator_options)) {
         return 1;
       }
@@ -323,6 +330,7 @@
   Maybe<std::string> target_abis;
   std::vector<std::string> configs;
   std::vector<std::string> split_args;
+  std::unordered_set<std::string> kept_artifacts;
   bool verbose = false;
   bool print_only = false;
   Flags flags =
@@ -356,6 +364,10 @@
                             "Split APK.\nSyntax: path/to/output.apk;<config>[,<config>[...]].\n"
                             "On Windows, use a semicolon ';' separator instead.",
                             &split_args)
+          .OptionalFlagList("--keep-artifacts",
+                            "Comma separated list of artifacts to keep. If none are specified,\n"
+                            "all artifacts will be kept.",
+                            &kept_artifacts)
           .OptionalSwitch("--enable-sparse-encoding",
                           "Enables encoding sparse entries using a binary search tree.\n"
                           "This decreases APK size at the cost of resource retrieval performance.",
@@ -438,6 +450,14 @@
       return 0;
     }
 
+    if (!kept_artifacts.empty()) {
+      for (const auto& artifact_str : kept_artifacts) {
+        for (const auto& artifact : util::Tokenize(artifact_str, ',')) {
+          options.kept_artifacts.insert(artifact.to_string());
+        }
+      }
+    }
+
     // Since we know that we are going to process the APK (not just print targets), make sure we
     // have somewhere to write them to.
     if (!options.output_dir) {
diff --git a/tools/aapt2/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/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index dc5c14e..4e9391d 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -24,6 +24,10 @@
         "stream_proto_utils.cpp",
         "string_utils.cpp",
     ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
 
     shared_libs: ["libprotoc"],
 }
diff --git a/tools/streaming_proto/stream_proto_utils.cpp b/tools/streaming_proto/stream_proto_utils.cpp
index e8f86bc..fb2d98d 100644
--- a/tools/streaming_proto/stream_proto_utils.cpp
+++ b/tools/streaming_proto/stream_proto_utils.cpp
@@ -13,8 +13,6 @@
 // TODO: packed is not supported yet.
 //
 const uint64_t FIELD_COUNT_SHIFT = 40;
-const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
-const uint64_t FIELD_COUNT_UNKNOWN = 0;
 const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
 const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
 const uint64_t FIELD_COUNT_PACKED = 5ULL << FIELD_COUNT_SHIFT;
diff --git a/wifi/tests/Android.mk b/wifi/tests/Android.mk
index c98e40a..d9f332f 100644
--- a/wifi/tests/Android.mk
+++ b/wifi/tests/Android.mk
@@ -58,6 +58,7 @@
 
 LOCAL_JAVA_LIBRARIES := \
     android.test.runner \
+    android.test.base \
 
 LOCAL_PACKAGE_NAME := FrameworksWifiApiTests
 LOCAL_COMPATIBILITY_SUITE := device-tests